News | Forum | People | FAQ | Links | Search | Register | Log in
Coding Help
This is a counterpart to the "Mapping Help" thread. If you need help with QuakeC coding, or questions about how to do some engine modification, this is the place for you! We've got a few coders here on the forum and hopefully someone knows the answer.
First | Previous | Next | Last
^It's FacingIdeal In AI.qc 
delta 
Bug Where Monster Turns Into Solid BSP But Is Still Alive 
I was following one of the tutorials to add strafing to monsters,and i think it might have caused this bug - but i am not familiar with

nothing looks wrong?

have any ideas?

if it helps i vaguely remember reading that if a fiend kills a nuclear box, it took becomes treated as BSP event though it's still alive? 
Nvm I Found The Cuase, It's FoundTarget ... I Can't See What's Wrong? 
void () FoundTarget = //causes invincible bug??
{
local entity head; //new
local float alertRadius; //new

if ( (self.enemy.flags & FL_MONSTER) || (self.enemy.classname == "player") || (self.enemy.classname == "hologram") || (self.enemy.classname == "cbot") || (self.enemy.classname == "CBot_Create")) //new
{
// let other monsters see this monster for a while
sight_entity = self;
sight_entity_time = time;
}
self.show_hostile = time + 2.00; // was 1 wake up other monsters
alertRadius = 512; // Large radius for alert spread (default was usually 1000)?


// Alert all monsters in radius
head = findradius(self.origin, alertRadius);
while(head)
{
if (head.flags & FL_MONSTER)
{
if (head != self) // Don't alert self
{
// Make other monsters aggressive
head.show_hostile = time + 2;
head.enemy = self.enemy;
head.think = head.th_run;
head.nextthink = time + random() * 0.5; // Stagger response times

// Chance to alert even more distant monsters
//if (random() < 0.3) // 30% chance
// {
// head.noise_time = time + 2; // Make noise to attract others
// }
}
}
head = head.chain;
}
//end new


SightSound ();
HuntTarget ();
}; 
Clarification 
I can't see anything in the code you posted that looks relevant. Are you getting a error message like "MOVETYPE_PUSH with a non bsp model" or are you just seeing a monster that's frozen solid and invulnerable?

If it's the latter, it can be helpful to get a snapshot of the entity data of the frozen monster. One way to do that without much fuss is to save the game once you've reproduced the error, then open the save file in a text editor. Then after a bit of searching for the classname you should find the stuck entity. Easier to do in a test map with a small number of monsters! 
 
the monster is still alive and attacking, but when you hit with an axe, has the "plink" sound like you are hitting a wall & invincible...

also is "MOVETYPE_PUSH with a non bsp model" is this fixable? 
Error 
If you had the "MOVETYPE_PUSH with a non bsp model" error you would know it - the game crashes to console with that text.

My guess is that your oddball monsters come from the following sequence of events:
1) A monster is killed and starts its death animation
2) Another monster wakes up nearby and runs FoundTarget
3) FoundTarget finds the dying monster and changes its animation from dying to running

In step 3 you've quite literally reanimated the dead! When a monster dies, it turns non-damageable immediately, but usually becomes non-solid at some point midway through the death animation. So if you hit the right timing you'd get the behaviour you report. Fix is to exclude dead monsters from the process. 
About Pushable Models 
"MOVETYPE_PUSH with a non bsp model" is this fixable?

So if I make monster corpses pushable it will cause this error?

I know shubnigurrath caused this sometimes... 
Who Is Pushing 
I think you misunderstand MOVETYPE_PUSH. It's not to make a thing be pushable, it's designed for things like doors and platforms that do the pushing. Don't make corpses MOVETYPE_PUSH, or anything else that isn't a BSP object for that matter.

Yes, the teleport train is MOVETYPE_PUSH without being a BSP object, but that's why it sometimes crashes! It was a bad idea, probably done in a hurry to get Quake out of the door.

You can read some more on the issue here, but bear in mind that the fix discussed on the page is an engine-level fix. If you're writing a mod that doesn't prescribe which engine to use, you can't benefit from this.
https://www.quake-info-pool.net/q1/qfix.htm#movetype_push 
Proximity Mine Help (based Off SoA Prox Launcher Code) 
Basically it's overall working but
the key issues are
it never seems to stick on world (it otherwise behaves properly more or less), and thus the arming and stick sounds never play. Also even if it doesn't stick the arming sound never plays

besides that it properly triggers on enemies, bounces off doors & enemies etc, and works more or less...

I've been working on this for 9+h and can't figure it out!

void() ProximityBomb =
{
local entity head;
local float blowup;

if (vlen(self.spawnmaster.velocity)>0)
{
ProximityGrenadeExplode();
self.think();
return;
}
if (time > self.delay)
{
mine_disarm();
self.think();
return;
}


self.touch = SUB_Null; //new
self.owner = world;
self.takedamage = DAMAGE_YES;
head = findradius(self.origin, PROX_MINE_RADIUS); //sensor radius
blowup = 0;

while (head) //what to look for
{

// Don't detonate on players or other mines
if ((head != self) && (head.health > 0) && (head.flags & (FL_CLIENT|FL_MONSTER)) && (head.classname != self.classname) && (head != self.lastvictim)) //(head.classname != "player"))
{
blowup = 1;
}


traceline(self.origin,head.origin,TRUE,self);
if (trace_fraction != 1.0)
blowup = 0;


if (blowup==1)
{

ProximityGrenadeExplode();
self.nextthink = time + 0.5;
return;
}
head = head.chain;
}
self.nextthink = time + 0.25;
}; 
 
void() ProximityArmed = //new
{
sound(self, CHAN_WEAPON, "weapons/proxarmed.wav", 1, ATTN_NORM);
self.think = ProximityBomb;
self.nextthink = time + 0.25;
};



void() ProximityGrenadeTouch =
{
if (other == self || other == self.owner) //|| other == self.owner
{
sound (self, CHAN_WEAPON, "weapons/proxbounce.wav", 1, ATTN_NORM); // bounce sound
return;
}

if (pointcontents(self.origin) == CONTENT_SKY)
{
remove(self);
return;
}
// Don't collide with other mines
if (other.classname == self.classname)
{
sound (self, CHAN_WEAPON, "weapons/proxbounce.wav", 1, ATTN_NORM); // bounce sound
self.movetype = MOVETYPE_BOUNCE; // Add this line
return;
}


// Check if it's a brush entity (like doors)
if (other.movetype == MOVETYPE_PUSH) //|| other.solid == SOLID_BSP
{
sound(self, CHAN_WEAPON, "weapons/proxbounce.wav", 1, ATTN_NORM); // bounce sound
self.movetype = MOVETYPE_BOUNCE; // Add this line
return;
}

if (other.flags & FL_CLIENT) //user disarm mine
{
//self.solid = SOLID_NOT;
mine_disarm();
//remove(self);
return;
}

if ((other.takedamage == DAMAGE_AIM || other.solid == SOLID_TRIGGER || other.flags & FL_MONSTER )) //don't det on monsters || other.flags & FL_CLIENT
{
sound(self, CHAN_WEAPON, "weapons/proxbounce.wav", 1, ATTN_NORM);
//self.movetype = MOVETYPE_BOUNCE;
T_Damage(other, self, self.owner, 1);
//self.think();
return;
}


self.movetype = MOVETYPE_TOSS;
if (self.state == 1)
return;

if (vlen(other.velocity) > 0)
{
ProximityGrenadeExplode();
self.think();
return;
}

else
{

sound (self, CHAN_WEAPON, "weapons/proxstick.wav", 1, ATTN_NORM); // bounce sound
self.movetype = MOVETYPE_NONE;
setsize (self, '-5 -5 -5', '5 5 5');
self.state = 1;
self.spawnmaster = other;

self.velocity = '0 0 0';

// Start arming sequence
self.think = ProximityArmed;
self.nextthink = time + 2;

self.touch = SUB_Null; //new
self.solid = SOLID_NOT; //new
}
}; 
 
void() ThrowProxMine =
{
float x;

// Check if enough time has passed since last throw
if (self.last_mine_time > time - 1.5)
return;

// Check if player has enough ammo
if (self.ammo_rockets2 < 1)



local entity missile; //, mpuff;

self.currentammo = self.ammo_rockets2 = self.ammo_rockets2 - 1;
self.last_mine_time = time; // Update last throw time

sound (self, CHAN_BODY, "zombie/z_shot1.wav", 0.8, ATTN_STATIC);

self.punchangle_x = -2;

missile = spawn ();
missile.owner = self;
missile.lastvictim = self;
missile.movetype = MOVETYPE_TOSS;// MOVETYPE_BOUNCE;
missile.solid = SOLID_BBOX;
missile.classname = "proximity_mine";
missile.takedamage = DAMAGE_YES; //DAMAGE_NO;
missile.health = 2;
missile.state = 0; //not armed

// set missile speed
makevectors (self.v_angle);


if (self.v_angle_x)
missile.velocity = v_forward*400 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
else
{
missile.velocity = aim(self, 10000);
missile.velocity = missile.velocity * 400;
missile.velocity_z = 200;
}
missile.avelocity = '100 400 100';


missile.angles = vectoangles(missile.velocity);

missile.touch = ProximityGrenadeTouch;
// set missile duration
missile.nextthink = time + 2; //min time until mine can explode from enemy
missile.think = ProximityBomb; //arm
missile.th_die = ProximityGrenadeExplode;
missile.delay = time + 90; // time until mine auto disarms

setmodel (missile, "progs/proxmine.mdl");
setorigin (missile, (self.origin) + (v_forward * 0) + (v_right * -17) + '0 0 10');

setsize (missile, '-4 -4 -4', '4 4 4');

}; 
Debugging 
The difficult part with your ProximityGrenadeTouch function is there are so many return statements which MIGHT end the function, and the bit of code that makes it stick to the wall is at the very end.

I would recommend you add some dprint statements to the function after each "checkpoint" - each place where the function has avoided a branch that contains a return. Then you can check how far execution gets through the function when you throw one against a wall, and deduce which exit is being taken unexpectedly. Two tips
a) pull down the console as soon as the bomb hits a wall for the first time, as you'll get a huge pile of debug messages if you let it bounce all over the shop first
b) remove the debug statements once you have solved the mystery, or they will be a pain in future 
I See, Thanks! 
Another issue is... is it possible to dynamically spawn monsters real time via impulse? (like half life's impulse 76)

I tried adding the monster's model precache to void () worldspawn, void () StartFrame , but i keep getting "host error pf precache can only be done in spawn functions?" 
StartFrame Timing 
StartFrame runs every frame! If you put a precache in there, you will hit the error for a late precache just about as fast as it's possible to hit it. Stick to worldspawn and you'll have a better time.

But as long as you time the precaches correctly (always on frame 0, and never afterwards), you can spawn monsters dynamically, in essentially the same way as creating a rocket or grenade. One small catch is that you can't update the total_monsters count after the first frame - so your monster kills can go over 100% unless you ensure that dynamically spawned monsters don't count for the kill count.

Another tricky wrinkle to deal with is where to place the monster so that it isn't stuck. Usually that's the responsibility of the mapper! One thing's for sure, you'll have to come up a compromise, and here's the proof: imagine that we make a map that's just a box 8 units larger than the player. It's physically impossible to successfully spawn a monster into that map. 
Fixed Precaches 
One other thing to warn you - don't ever randomise your precaches! Imagine you had some plan to randomly select one monster which your impulse spawns for the duration of the map, and you make the selection in frame 0 then precache just the assets for the selected monster. This exposes you to an interesting bug!

The bug arises when you save and load the game. In particular when you load the game, the engine:
1) Runs the first few frames of the map as if you had started from scratch
2) Reads the entity states from the save file and shuffles everything around to match it
The point of 1) is mainly to precache everything again. But second time round you might roll a different number on the random monster table and precache different models. Things may start showing up with the wrong models, or if you are less lucky the whole thing just crashes.

The only way to avoid this problem is to make the precache sequence entirely deterministic. If you're married to the random spawnable monster idea, you might have to precache the models for all the options, even if only one is ever used at a time.

And there are other more subtle ways to hit this error. Imagine you have an entity which you only allow a 50% chance of manifesting, randomised on each map load. Even if you have so many of this entity that one spawns every time, random variation might change the order in which models are first precached, as the first manifestation may occur before or after other fixed entities. Changing the order of precache messes up which models appear after a save is reloaded. 
I'm Confused... 
can you give example code?

I keep getting the "host error pf precache can only be done in spawn functions?"

regardless of where I put the monster's precache ? 
On A Side Note - For Dynamically Spawned Monster Placement We Can Try 
spawny.origin_x = (0.5 - random())* 2 * 5000; //It's origin is randomized
spawny.origin_y = (0.5 - random())* 2 * 5000;
spawny.origin_z = (0.5 - random())* 2 * 5000;

while((pointcontents(spawny.origin) != CONTENT_EMPTY) && (spawny.aflag < 75))
{
//If the pointcontents of the monster origin is not empty, reset it To a more suitable space. If no space can be found within 75 loops,
//Leave it where it last was placed.
spawny.origin_x = (0.5 - random())* 2 * 5000;
spawny.origin_y = (0.5 - random())* 2 * 5000;
spawny.origin_z = (0.5 - random())* 2 * 5000;

spawny.aflag = spawny.aflag + 1;
Jazz Is About All The Notes He's NOT Playing 
The important part is not where you put the precaches, but REMOVING them from everywhere except worldspawn. If you are really struggling, remove all the precaches of the model until you start getting a different error about a resource NOT being precached. Then add one precache to worldspawn and you should be set. 
WOW Thanks! It's Almost Fixed! 
I didn't realize it was so simple! weird... i swore i did that before and it didn't work...

Ok so I can spawn the monster now and i can see the monster in front of me for a few sec, but then game crashes with "host error pr execute program null function" 
Also 
what controls how high or low a monster can step up/down?

seems they have trouble with stairs & small ledges? 
Units 
Best measure is 16 units,might be 15.
In a turnine stair it is better to keep the steplength more than 16, aproxemate 24. 
Step Height 
Look up #define STEPSIZE here

https://github.com/id-Software/Quake/blob/master/WinQuake/sv_move.c

The height for climbing a step is applied equally for both players and monsters. But one of the differences is how smoothly the movement is applied. A player moving at 360 units per second on a modern computer would have that movement split up into 72 segments of length 5. Each movement can climb one step, so the player can comfortably manage many steps close together.

In contrast, a monster moving forwards at 360 units per second would move in just 10 segments of 36 units each. And the movement is all or nothing - if the monster would need to climb TWO steps to reach the end point, it will collide with the second step and the entire movement is aborted.

So putting more horizontal spaces between the steps will help a lot. Monsters will also never go off a ledge with drop a of more than 1 step using walkmove or movetogoal - you have to give them velocity movement if you want them to really fall. Be careful, it's easy to turn see that feature turn into an exploit as your monsters can now be lured into pit traps and completely bypassed. 
Smoothing 
Another alternative is to split up the navigation into small segments within a frame, for example replacing movetogoal(20) with movetogoal(10); movetogoal(10); It's not always a 1 to 1 equivalence, like with lots of Quake AI it's a thing to experiment with and tune. 
Host Error Pr Execute Program Null Function 
This error means that you've forgotten to set something, but the error message doesn't tell you which something it is. Some possibilities are

th_stand - the AI for an idle monster
th_run - the AI for a newly alerted monster
th_pain - the AI for a pain animation
th_die - the AI for a death animation
think - the next function to run after a timer.

To try and diagnose, add this line to the START of your monster spawn function

th_stand = SUB_Null;

If that fixes the error, you know that you've forgotten th_stand - and NOW you need to fix that by giving the monster the AI, this is a diagnostic tool only. If that doesn't help, add another line

th_run = SUB_Null;

Repeat until adding a line avoids the crash, then develop a fix. 
Step Height 
soo... you're saying it cannot be changed via mod? it's engine based? 
First | Previous | Next | Last
You must be logged in to post in this thread.
Website copyright © 2002-2025 John Fitzgibbons. All posts are copyright their respective authors.