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
 
.huntt_ime
Set to time + something when the player is in sight, but movement straight for
him is blocked. This causes the monster to use wall following code for
movement direction instead of sighting on the player.
(sic)

in ai.qc

is this true? it sounds like a lie. :P 
The Hunt Is Over 
The only instance of "hunt_time" in the qc source is in the comment about ideal_yaw. I'm guessing it's something they used to do in ai_run. It may have been taken out because movetogoal tries the direct path to the enemy before using the wall following code. This would suppose that originally the function would call walkmove if hunt_time wasn't set, set hunt_time if walkmove returned false, and called movetogoal while hunt_time was active. 
 
ah that would make sense.
one thing i noticed about the movetogoal code...
it always seemed to me that doom had much better wall following code. quake monsters often get caught in areas when trying to path to the player, but doom monsters quite often turn up in surprising places especially if you watch them in the automap. quake AI seems to 'give up' wall following very early, usually before the wall following has a chance to get around a particular corner or whatever.
mm, that was random. :P 
Well Then 
we should just see how D00M does it,
and port it to,
(_)uake :D
.| 
Monster Only Clip... 
i wonder if it would be feasible to take the func_togglewall and do this:

rename it to func_monsterclip
make it non-solid
movetogoal -> movetogoal_builtin
and then we make a new movetogoal that loops through all func_monsterclips, makes them solid, called the movetogoal_builtin and then afterwards, makes all the func_monsterclips nonsolid again. 
 
ran a little test.
instead of a generic monsterclip, i created a method to get larger bbox monsters to move correctly.

you essentially build your 'hull3' out of brushes and turn the entire thing into a single func_clipModel, and then you tie that clipModel to the monster you want to run collision with via clipModel->targetname editor key fields.
this could actually work because it won't impede smaller bbox monsters (that can use normal hull1/2 collision) since the clipModel is only every solid during that single monster's walk frame... 
Necros: 
Nice, I'd really like to see some larger monsters or bosses/minibosses (that can actually move) in quake. This could be a piece of that puzzle. 
 
yeah, works pretty decent, but of course it imposes some limitations.

you need to 'clip' any area where the monster will be, of course, and that means you have to visualize the hull expansion yourself and implement it.

also, currently how i do it is to use the same method that hipnotic did for the func_togglewall and that is to just add '8000 8000 8000' to the origin when you want to turn it off, and then subtract the same vector when you want it on.
i don't know how big a deal it is if you had a generic monsterclip entity that EVERY monster in the map would have to toggle back and forth every animation frame. that could be pretty brutal.
it's probably better to somehow localize monster clips so only the monsters most likely to actually touch them will be toggling them (hence why i opted for a clipModel->targetname method instead of just putting it in walkmove and movetogoal). (large bbox monsters use a special wrapper for ai_walk and ai_run). 
Some Problems 
quake seems to use start the hull2 bbox from the bottom left (mins) of the monster.

this means that if you use a bbox of size (for example) '-128 -128 -24' - '128 128 64' only the mins up to '-64 -64 64' is used when checking collision.

i thought that quake would start the hull2 size from the center of the monster, but this is not the case.

this creates a problem now because collision is still messed up.

if we resize the monster to hull2, call movetogoal and resize back to the new hull size, collision against the world (and func_clipModels) is fine, but the monster is now able to walk inside the player and other monsters.

i'll have to put more thought into this, i guess... 
 
Wait, how do shub and chthon work? They have large (stationery) bboxes and players seem to collide against them correctly. 
 
yeah, the collision of other bboxes is fine.
it's the collision against the world that is messed up.

and that's the big problem:
movetogoal will function correctly vs other bboxes if you set the bbox correctly. 256x256x128 or whatever.

movetogoal will function incorrectly vs world when bbox is set to 256x256x128.

if you set bbox to standard hull2 size before movetogoal and reset to 256x256x128 after, monster will move inside player.

what i've done so far is left the bbox at 256x256x128 and simply 'offset' the func_clipModel so that sides that the mins hits are smaller than sides that the maxs hits.
kind of hard to explain, when i figure everything out, i'll probably make a blog post about it with pictures to explain it properly.

also, another interesting (and annoying) side effect: because of the way the standard ai_run code is sandwiched between func_clipModel toggles such that the clipModel is active when ai_run is called, things like the visible() function fail if the player is inside the clipModel since visible uses a traceline to determine if the monster can see it (and since the clipModel is solid during that period, the trace hits the clipModel and determines the player is not visible). 
Append 
another way to look at it is this:

for the purpose of bbox vs world, only the first hull2 coordinates starting at the mins are solid.

that is to say:
if the bbox was mins: -128 -128 -24, maxs: 128 128 64

world collision is done from -128 -128 -24 to -64 -64 64 (mins + VEC_HULL2_SIZE)
because VEC_HULL2_SIZE = 64 64 88 
Oh I See... 
if you set the bbox down to standard hull2 size, it's collision against other entities is wrong. If you leave the bbox alone, its collision box is offset from the correct location.

Two ideas:

1. before moving the oversized entity, make ALL other solid entities (or at least all entities within a findradius) larger by XYZ amount, to compensate.

2. have two entities, one oversize and one normal size, and move them both. If either one is blocked, set both entities to the location of the blocked entity.

Not sure if either of these are completely workable. 
 
1. could work, except it's just (potentially) a lot of entities to enlarge.

2. this is better, but the problem is that if one is blocked, and we reset the position, nothing happens for that frame and likely movetogoal will try the same thing next frame.

currently what i've done is use bboxes on all the func_clipModels. this means each brush needs to be a seperate entity and we can't have sloped/angled faces anymore (since everything is just a box now).
it works and doesn't seem too slow, but we loose a bit of brush flexibility since we can't have any angles any more. of course, you're really just blocking out the area, so this could actually work. i'll leave it this way for now and test it out for now. 
I'm Not Compensating. Really! 
http://necros.quaddicted.com/temp/dragon.jpg

the collision stuff has been working well without any further problems so far.

the really nice thing about it is that for monstrously large monsters like the dragon in that shot where you don't necessarily want the bbox to completely cover the entire model, all you have to do is expand the clipModel entities out further from the actual walls. 
 
Is that the DOE dragon? 
Yeah 
i rerigged him and animated him for actual combat and not just flying around path corners. 
Accelerating Back 
So I've just spent about a month without internet at home, shame I missed all that stuff about large bboxes, because that's a cool idea. If you're doing a landbound monster with a tall box, then you can create maps which can be bbox-blocked with greater ease. Low walls rarely need to be boxed in this case, just obstructions which are above the head height of regular sized entities but low enough for "the boss" to collide with.

Anyway, that's a digression. In my time away, I had lots of time to get on and finish projects. A fair few of them were quake related, and I'm posting the first one now. It's the tutorial I mentioned a dozen posts up about accelerating MOVETYPE_PUSH objects. Since it involves some formulae which I've done up in LaTeX and a few graphs, I've made a web page for it rather than just copy-paste it straight onto func.

Accelerating pushers
Also means I can fix typos and other problems, so post them here! 
 
i haven't really read your above post yet as accelerating movers isn't a pressing issue anymore, but i just wanted to mention: you should compile all your coding tips you've made in this thread and put them up on a site somewhere. some of them are quite useful and others are downright golden. 
No Longer Touching 
I have created a 'trigger' entity. It covers a large portion of the map and instigates certain continuous but randomly timed actions whilst the player is inside the trigger's area. Let's call it an area_trigger.

The events are called by using the trigger's touch function, as in self.touch=do_these_things, so that when the player leaves the area, do_these_things no longer gets called. If he re-enters, the events start again. So far, so good.

I now want to 'enhance' this effect so that when the player leaves the area, a separate single event takes place, and this event takes place each and any time he leaves the area, which could be one or more times throughout the game. This event must not take place at any other time.

Is there a way to read when the player stops 'touching' the trigger so that I can call this exit event. I maybe could set up multiple triggers around the area_trigger and have them switched on by the area_trigger so that the call to the exit event is operated on a one-way basis but I am hoping there is an easier way.

Any views from the coding gurus? 
Not Easily 
A better solution is to have triggers at the entrances to the area. Two in fact, separated a little. The inside one calls the enter function, the outside one calls the exit function. The functions are coded so that they only actually do something on a state change. 
 
actually, it is easy.

on your trigger_area touch function:

self.nextthink = time + 0.1;
self.think = DO_THIS_WHEN_PLAYER_LEAVES;

since touch is called every frame, nextthink will always be set higher than time, so .think will never be called. the minute the player steps out though, and .touch isn't called anymore, nextthink will expire in the next 0.1 seconds and run the .think function.

also, since i believe .touch functions are run first (before .think functions), even if you were getting less than 10 frames per second (such that the next .touch might be AFTER 0.1 seconds) the touch will be run first, thereby resetting nextthink anyway. 
Append 
first: i forgot to mention in your .touch function, remember to store 'other' in self.enemy or somewhere so that your DO_THIS_WHEN_PLAYER_LEAVES function will know how to have as the activator (just add activator = self.enemy;)


if your trigger_area already needs to have a nextthink and think set for whatever reason, just spawn in a relay entity and do the trick on that entity instead:

if (self.owner == world)
{
self.owner = spawn();
self.owner.owner = self;
}

self.owner.nextthink = time + 0.1;
self.owner.think = DO_THIS_WHEN_PLAYER_LEAVES;


then:

void() DO_THIS_WHEN_PLAYER_LEAVES =
{
...........some code here............

self.owner = world; //break link with the relay entity and the trigger

self.nextthink = time + 0.1;
self.think = SUB_Remove; //delay remove
};


we set up .owner so we have an easy way to get access to the trigger_area's .enemy field, target and whatever else might be needed and we manually break the .owner connection when removing the entity because there's like a 2 second delay before entities are removed in quake. 
Crap 
void() DO_THIS_WHEN_PLAYER_LEAVES =
{
...........some code here............

self.owner.owner = world; //break link with the relay entity and the trigger

self.nextthink = time + 0.1;
self.think = SUB_Remove; //delay remove
};


otherwise your only erasing self's owner (the relay entity) and not the .owner belonging to the trigger. sorry. :P 
Not Sure If That Works 
Because when you're in a trigger, I don't think the touch function is called every frame. 
First | Previous | Next | Last
You must be logged in to post in this thread.
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.