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
Two Stage 
If that is a problem(it certainly would be if you need to detect monsters)can use force_retouch to help you out there. The simplest way would be to set force_retouch = 2 in the touch function as soon as you've touched the trigger. This would end up polling the heck out of all the triggers in your map while someone touched the middle one, but it would fix that problem.

I think the above is the only way to make the trigger responsive within a single frame (assuming you shrink the nextthink time lots). If you're willing to have your trigger less responsive (a minimum of two frames, but the values we pick here will be 0.2 seconds) then we can end up only using force_retouch once in 0.2 seconds.

We need the trigger_detector to have three states:

STATE_EMPTY: no player inside
STATE_READY: detected a player recently
STATE_POLLING: checking if there is still a player

If a player comes into an empty trigger we go from STATE_EMPTY to STATE_READY and wait for 0.1 seconds. After that time expires we go into STATE_POLLING for 0.1 seconds. If a player touches the trigger within that 0.1 seconds, we go back to STATE_READY, otherwise we go to STATE_EMPTY (and trigger the leaving event).

We don't actually need to explicitly track these states, they are just to understand what is going on. We have a think function called trigger_detect_startpolling along the lines of

self.nextthink = time + 0.1;
self.think = trigger_detect_fire;
force_retouch = 2;

When our .think is trigger_detect_startpolling, we are in STATE_READY. When our think is trigger_detect_fire we are in STATE_POLLING. When we don't have a nextthink in the future, we are in STATE_EMPTY.

Finally to hook all of this up, we need the touch function to set

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

which both moves us from STATE_EMPTY to STATE_READY when the player first touches, and back from STATE_POLLING to STATE_READY when the player retouches.


On another topic, force_retouch has an important effect on touch/think order which I hadn't considered before. When set, players will touch things they are in contact with before their think functions run (and then possibly touch things AGAIN after physics has run). So anyone who was intending to exploit the order that functions run had better be careful.

Also, I'm gonna go see if things really can run touches multiple times in a single frame. If that is the case, then it really will be important to make touch functions idempotent. This is a wonderful term from mathematics for a function which doesn't do anything else when you keep applying it to it's output. For example rint(x) is idempotent - once you get a whole number out, applying rint to that whole number just gives you the same whole number.

I'm using idempotent in a slightly weird sense here, the idea being that one of the parameters to our touch function is the value of "time", the frame that we are in. Most of the original touch functions have if(self.nextthink > time) type guards in place to achieve this, but it's something important to think about if you're writing a trigger which can be touched every frame - does it matter if you trigger it many times a frame? 
 
i don't know why it has to be so complicated, preach... it works fine the way i said. :P no need for force_retouch or anything. 
Monsterous 
It's needed in the case of non-player entities which don't link to the world unless they move. If you were trying to detect a monster, you would need the force_retouch. It is more than what mike asked for though, the simple suffices there... 
Oh Right 
i missed that you were checking for monsters as well. 
Necros & Preach 
Thanks. 
Findradius Vs Find Vs Nextent 
are there any differences in how these three work?

ie: is findradius really just doing
nextent(e)
if (distance of e < dist), add to .chain
or is it faster?

for example, if i did a findradius(org, 64) where i'm checking only a small radius, is it faster than if i findradius(org, 1024) or is it the same speed?

and if smaller radii are faster than larger ones, at what point does it become better to use nextent rather than a large findradius?
i guess that kind of thing would also depend on the total # of entities as well... 
Finding 
ie: is findradius really just doing
nextent(e)
if (distance of e < dist), add to .chain
or is it faster?

It turns out that it does something extra I'd never known about - it skips any entity that's SOLID_NOT(*). Other than that, the algorithm is as you describe, but because it's written in c skipping to the next entity is a single instruction to increment a pointer, etc. So it does run a lot faster than the QC equivalent, but it doesn't do any culling of the entity list based on the bsp tree or anything fancy.

for example, if i did a findradius(org, 64) where i'm checking only a small radius, is it faster than if i findradius(org, 1024) or is it the same speed?

They are the same speed if they contain the same number of entities. Otherwise the cost of adding more things to the chain is incurred, although that's fairly light compared to the rest of the loop.

(*) Also worth noting: the distance is measured to
origin + (mins + max) * 0.5;
not just the origin as you might guess. 
 
It turns out that it does something extra I'd never known about - it skips any entity that's SOLID_NOT

haha yeah, i figured that out the hard way. drove me insane for a while. -_-

Also worth noting: the distance is measured to
origin + (mins + max) * 0.5;
not just the origin as you might guess.


didn't know about this bit though. wouldn't have much of an impact unless you had some kind of weird offset bbox but good to know regardless. it does make getting precise findradius distances annoying though. if i did a findradius from one monster origin looking for other monsters, the find would have been completely accurate if it was going from origins and not bbox centers. oh well. :S 
How Difficult 
is it to find out what map a given savegame file is for?

Can someone post code / relevant savegame spec? :P 
Not Difficult At All 
Information on the map and its entities is stored in the .sav file in plain text format. 
You're Like A Savegame Wizard. 
interesting that savegames store lightstyles though. one would think that's easily gettable from the progs. 
Yeah It's Plain-text Alright, 
but the locations in the files seem to change.

I want to parse it with quakeinjector to enable loading savegames for each map, so I'd need to know how to parse the header.

Host_Loadgame_f() in http://svn.icculus.org/twilight/trunk/darkplaces/host_cmd.c?revision=10262&view=markup
seems to be the right function (darkplaces), but it's quite complicated and I'm not familiar with the quake src. For example I have no idea what COM_ParseToken_Simple(&t, false, false); does ;-) It would probably take me an hour or so to get what's going on there. 
Wait 
is it always on the same line? 
 
interesting that savegames store lightstyles though. one would think that's easily gettable from the progs.

The current string for each style needs to be saved because there's no post-loadgame callback for entities to set the lightstyle strings again. 
Looks Like It's The 20th Line 
save game version, description, 16 parameters, skill and then the bsp name. 
Qc/engine Question 
if a coder specifies a file "tORch.Mdl" would the engine usually look for tORch.Mdl or torch.mdl or what? 
Different Cases? 
From what I can glean from a quick browse through the source:

Models in pak files must match capitalisation exactly or they will not be loaded.

Models in the filesystem depend on the operating system's implementation of fopen in the standard c library. I believe this means that unix type OSs will fail to find the file but DOS and windows will succeed, but I can't find any documentation which confirms this for 'fopen'. So where possible make sure the exact case is used. 
 
i have had problems with upper/lower case before.
i found it best to avoid uppercase altogether.
for example, ne_tower had a bunch of custom sounds.

when i was developing it, i had all the files loose in folders and setting keys like 'noise' 'necros/someSound.wav' worked fine. when i packed everything into a pak file, someSound could not be found, even though the filename hadn't changed. 
Preach 
i know you've posted about this before, but i can't find it again :S

basically, there was a faster way to compare distances without using vlen.

was it something like vec1*vec2 > distance*distance ?? 
Necros: 
if (vec_x * vec_x + vec_y * vec_y + vec_z * vec_z > distance * distance) {
//temp is longer than distance
 
er ...

//vec is longer than distance 
Yup 
It's almost like that. You can compare the length of a vector v to a distance d with:

v * v > d * d (notice that on both sides of the equation we have the same variable twice)

If you're testing the distance between two positions p1 and p2, you need to take the difference in positions first:
v = p2 - p1;
v * v > d * d;

It works exactly as metl says, but v * v is a single QC instruction which computes the dot product he has expanded out. 
Very Cool 
thank you!

when you say it's one instruction, i guess doing it as vec * vec is faster then? 
 
It works exactly as metl says, but v * v is a single QC instruction which computes the dot product he has expanded out.

Ah, good to know. 
Monster_boss Glitch In Progs 1.06? 
On skill 2, Chthon's aim prediction fails if the player moves towards him when he's about to throw a lavaball. The result is that he throws it backwards instead (or possibly towards worldspawn). It doesn't occur on 1.01.
Is this a known issue - why wasn't it fixed? 
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.