Host_Error: PRVM_Execute Program

Discuss Artificial Intelligence and Bot programming.
Post Reply
Ghost_Fang
Posts: 336
Joined: Thu Nov 12, 2009 4:37 am

Host_Error: PRVM_Execute Program

Post by Ghost_Fang »

I have a new enemy coded in and it works great as long as you kill him in one hit. If he takes damage of any kind and he doesn't die quake (darkplaces) crashes with "Host_Error: PRVM_Execute Program: QC Function self.think is missing. Now it is clear what is wrong, or is it? I checked the whole new monster qc and all the nextthinks have thinks and everything matches up well. If it helps fitzkurok said "Host_Error: PR_Execute Program: Null function" What would cause this? I'll post code if i need to.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

The only thing I can imagine causing the error is exactly what's stated in the message: it's very likely that in one of the frames of the death sequence you're setting self.nextthink = time + something with self.think not initialized correctly. Without seeing the code itself it's hard to tell, though.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Ghost_Fang
Posts: 336
Joined: Thu Nov 12, 2009 4:37 am

Post by Ghost_Fang »

Code: Select all

/*
barnacle.qc
*/

void() barnacle_check;
void() barnacle_touch;



void() barnacle_push = 
{
	if (self.t_width <= time)
	{
		if (self.walkframe >= 51)
			self.walkframe = 0;
		self.frame = 0 + self.walkframe;
		self.walkframe = self.walkframe + 1;
		self.t_width = time + 0.03;
	}
	
	if (self.enemy.health <= 0)
	{
		barnacle_check ();
		self.touch = barnacle_touch;
		return;
	}
	
	if (self.enemy.flags & FL_ONGROUND)
		self.enemy.flags = self.enemy.flags - FL_ONGROUND;
	
	self.enemy.velocity = '0 0 50';
	self.enemy.origin_x = self.origin_x;
	self.enemy.origin_y = self.origin_y;
	self.nextthink = time;
	self.think = barnacle_push;
};

void() barnacle_check =
{
	self.nextthink = time;
	self.think = barnacle_check;
	
	self.frame = 0;
	
	traceline (self.origin, self.origin - '0 0 1000', FALSE, self);
	if (trace_fraction == 1.0)
		return;
	
	if (trace_ent.takedamage == DAMAGE_AIM)
	{
		self.enemy = trace_ent;
		sound (self, CHAN_VOICE, "barnacle/alert2.wav", 1, ATTN_NORM);
		self.walkframe=0;
		barnacle_push ();
		
	}
};

void() barnacle_bite =
{
	if (self.t_width <= time)
	{
		if (self.walkframe >= 61)
		{
			self.walkframe = 0;
			if (random() < 0.3)
				sound (self, CHAN_BODY, "barnacle/chew1.wav", 1, ATTN_NORM);
			else if (random() < 0.6)
				sound (self, CHAN_BODY, "barnacle/chew2.wav", 1, ATTN_NORM);
			else
				sound (self, CHAN_BODY, "barnacle/chew3.wav", 1, ATTN_NORM);
			T_Damage (self.enemy, self, self, 60);
			if (self.enemy.health <= 0)
			{
				barnacle_check ();
				self.touch = barnacle_touch;
				return;
			}
		}
		
		self.frame = 51 + self.walkframe;
		self.walkframe = self.walkframe + 1;
		self.t_width = time + 0.03;
	}
	
	self.enemy.velocity = '0 0 50';
	self.enemy.origin_x = self.origin_x;
	self.enemy.origin_y = self.origin_y;
	
	self.nextthink = time;
	self.think = barnacle_bite;
};


void() barnacle_die_go = [112, barnacle_die_go]
{
	if (self.walkframe >= 100)
		self.walkframe = 100;
	self.frame = self.frame + self.walkframe;
	self.walkframe = self.walkframe + 1;
	self.nextthink = time + 0.03;
	self.think = barnacle_die_go;

};

void() throw_gibs =
{
	self.nextthink = time;
	self.think = throw_gibs;
	if (self.health < time)
	{
		remove(self);
		return;
	}
	
	if (self.attack_finished < time)
	{
		if (random() < 0.3)
			ThrowGib ("progs/gib1.mdl", 0);
		else if (random() < 0.6)
			ThrowGib ("progs/gib2.mdl", 0);
		else
			ThrowGib ("progs/gib3.mdl", 0);
		
		self.attack_finished = time + 0.15 + random()*0.2;
	}
};

void() barnacle_die =
{
	local entity o;
	
	self.solid = SOLID_NOT;
	
	if (random() < 0.5)
		sound (self, CHAN_VOICE, "barnacle/die1.wav", 1, ATTN_NORM);
	else
		sound (self, CHAN_VOICE, "barnacle/die3.wav", 1, ATTN_NORM);
	
	o = spawn ();
	o.origin = self.origin;
	o.health = time + 2;
	o.nextthink = time;
	o.think = throw_gibs;
	
	remove(self.owner);
	self.walkframe=0;
	self.nextthink = time + 0.1;
	self.think = barnacle_die_go;
};


void() barnacle_touch =
{
	if (other.takedamage == DAMAGE_AIM)
	{
		
		self.walkframe=0;
		barnacle_bite ();
		sound (self, CHAN_BODY, "barnacle/bite3.wav", 1, ATTN_NORM);
		self.touch = SUB_Null;
	}
};

void() ParseTongue =
{
	local entity tongue;
	local float tracedist, tsize;
	
	traceline (self.origin, self.origin - '0 0 1000', FALSE, self);
	
	tracedist = 1000*trace_fraction;
	while (tsize < tracedist)
	{
		tsize = tsize + 32;
		
		tongue = spawn ();
		tongue.origin = self.origin;
		tongue.origin_z = tongue.origin_z - tsize;
		tongue.nextthink = time + 0.2;
		tongue.think = SUB_Remove;
		setmodel (tongue, "progs/tongue.mdl");
	}
};

void() tongue_think =
{
	self.nextthink = time + 0.2;
	self.think = tongue_think;
	ParseTongue ();
};

void() monster_barnacle =
{
	local entity o;
	
	if (deathmatch)
	{
		remove(self);
		return;
	}
	
	precache_model ("progs/barnacle.mdl");
	precache_model ("progs/tongue.mdl");
	
	precache_sound ("barnacle/alert2.wav");
	precache_sound ("barnacle/bite3.wav");
	precache_sound ("barnacle/chew1.wav");
	precache_sound ("barnacle/chew2.wav");
	precache_sound ("barnacle/chew3.wav");
	precache_sound ("barnacle/die1.wav");
	precache_sound ("barnacle/die3.wav");
	
	self.solid = SOLID_SLIDEBOX;
	self.flags = FL_MONSTER;
	
	setmodel (self, "progs/barnacle.mdl");
	
	setsize (self, '-16 -16 -32', '16 16 0');
	
	self.health = 35;
	self.takedamage = DAMAGE_AIM;
	self.th_die = barnacle_die;
	self.th_stand = barnacle_check;
	
	self.nextthink = time + 0.1 + random()*0.5;
	self.think = barnacle_check;
	
	self.touch = barnacle_touch;
	
	total_monsters = total_monsters + 1;
	
	self.owner = spawn ();
	o = self.owner;
	o.origin = self.origin;
	o.nextthink = time + 0.2;
	o.think = tongue_think;
	setmodel (o, "progs/tongue.mdl");
};
Sajt
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Post by Sajt »

My guess is that you have FL_MONSTER flag set but no th_run. When you shoot the barnacle, T_Damage is called. Near the bottom of that function, it calls FoundTarget() if the target is FL_MONSTER. FoundTarget calls HuntTarget, which sets self.think to self.th_run.

You should remove the FL_MONSTER flag, or else add an extra check to return from that code path if classname is "barnacle". (IIRC you might want to keep FL_MONSTER for other reasons, for example the enlarged bbox for opposing traces?)

Or you could add a th_pain, since self.th_pain (if it isn't null) is called after FoundTarget. But your previous think function would still be getting overridden, which might mess up your specialized monster behaviour.
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Ghost_Fang
Posts: 336
Joined: Thu Nov 12, 2009 4:37 am

Post by Ghost_Fang »

I tried both, either/or and at the same time. Still nothing. If it helps to narrow it down, when it is "sucking you up" you can shoot it and its fine, but if its just chillin there and you shoot it and you dont kill it it crashes.

EDIT: I got it now, I did it RIGHT thanks Sajt
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Sajt wrote:My guess is that you have FL_MONSTER flag set but no th_run. When you shoot the barnacle, T_Damage is called. Near the bottom of that function, it calls FoundTarget() if the target is FL_MONSTER. FoundTarget calls HuntTarget, which sets self.think to self.th_run.

You should remove the FL_MONSTER flag, or else add an extra check to return from that code path if classname is "barnacle". (IIRC you might want to keep FL_MONSTER for other reasons, for example the enlarged bbox for opposing traces?)

Or you could add a th_pain, since self.th_pain (if it isn't null) is called after FoundTarget. But your previous think function would still be getting overridden, which might mess up your specialized monster behaviour.
But the reported error was in self.think, not self.th_run (which AFAIK is not mandatory). Even if it was required, one could just set th_run = SUB_Null and everything should work with FL_MONSTER set.

EDIT: I think there's something wrong with this piece of code:

Code: Select all

void() barnacle_die_go = [112, barnacle_die_go]
{
   if (self.walkframe >= 100)
      self.walkframe = 100;
   self.frame = self.frame + self.walkframe;
   self.walkframe = self.walkframe + 1;
   self.nextthink = time + 0.03;
   self.think = barnacle_die_go;
}; 
You could/should remove the "[112, barnacle_die_go]" part, since you're doing all the animation by hand.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Sajt
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Post by Sajt »

frag.machine wrote:But the reported error was in self.think, not self.th_run (which AFAIK is not mandatory). Even if it was required, one could just set th_run = SUB_Null and everything should work with FL_MONSTER set.
It's reporting "think" being null because HuntTarget sets self.think to self.th_run and sets nextthink, it doesn't call th_run directly.

You could add th_run (or th_pain) for the monster, but like I said it would still end up overriding the previous think, which might screw up his special monster behaviour (or it might not, I don't know).
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Ghost_Fang
Posts: 336
Joined: Thu Nov 12, 2009 4:37 am

Post by Ghost_Fang »

well what I did was make the th_walk, th_run, th_pain, etc just do barnacle_check and it seemed to work just fine. I'm pretty sure that was the issue since it had no th_XXXX other than th_die and and th_stand.

Thanks for the help guys!
AI, for some reason, I just can't get the hang of and it irritates me.
Post Reply