|
Posted by metlslime on 2007/08/08 04:57:56 |
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. |
|
|
#595 posted by jt_ on 2011/06/21 21:45:30
So I've been thinking about a way to add a lot of new weapons to a mod, but I'm not sure about some things. The first issue would be how the hud handles the weapons, since there's only enough room for 9 weapons, I would need to figure out a way to make sure that weapons don't take each others spots. I was thinking of adding a precache variable to each weapon and then checking to make sure that none of them conflict with each other when the map starts. I think that would eliminate the problem of weapons taking other weapons spots. I have no idea how and for what reason they're placed on the hud the way they are, anyone have any insight on this?
HUD Placement
#596 posted by Preach on 2011/06/21 23:50:38
There's actually quite a lot to be said about the HUD but I'll try and get the basics into one short post.
The engine uses the QC field called items to determine which weapons to display on the HUD. If you look in defs.qc you will find the following:
// items
float IT_AXE = 4096;
float IT_SHOTGUN = 1;
float IT_SUPER_SHOTGUN = 2;
float IT_NAILGUN = 4;
float IT_SUPER_NAILGUN = 8;
float IT_GRENADE_LAUNCHER = 16;
float IT_ROCKET_LAUNCHER = 32;
float IT_LIGHTNING = 64;
To display some collection of these items on the screen simply sum the values of the icons you want displayed, and then set self.items to equal the total.
The numbers are all carefully chosen as powers of 2. This is useful because it makes any sum a unique combination of these numbers. It also means that you can use the bitwise OR function to safely add an item without checking if it is already there:
self.items = self.items | IT_SHOTGUN;
If we started with 0 items, and then blindly added IT_SHOTGUN to self.items we might get into trouble - if that line of code ran twice then self.items would equal 2 and we'd have IT_SUPER_SHOTGUN instead.
We can also use the bitwise AND function to test if an item is present:
if(self.items & IT_NAILGUN)
{
�//do something just for players with a nailgun
}
If the bitwise functions are new to you I'd advise searching for a c tutorial on them, it's probably been explained before in a clearer way than I'd invent today.
Other things to know about icons:
� Setting self.weapon to one of the IT_ values highlights that icon as the selected weapon.
� The mission packs added extra weapons which complicate the icons code somewhat, so I'll avoid that until another day.
� The engine automatically makes new items blink on the HUD when they are added to self.items.
Path_corners
#597 posted by jt_ on 2011/06/29 17:13:57
I made some path_corners in a map that are different z distances (hight/lower than another path_corner) apart, thinking that my flying monster would follow them. To my surprise, it didn't. After removing the z distance from my path_corners, the monster follows the path just fine.
It seems that with flying monsters, if path_corners are different z distances apart, they 'ignore' the path_corners. From a brief look at the quakec, everything looks ok. Maybe this is a bug in PF_walkmove? Looking at the function, it looks like it ignores the z axis all together (line 28), so maybe it's not a bug, Carmack just ignored it :p.
Note: This doesn't affect walkmonsters (as much) as flying monsters. With walkmonsters, they seem to be able be able to find their next path_corner as long as the z distance between them isn't too much (I haven't found out exactly what it is).
Looking a monsters.qc at the walkmonster_start_go and flymonster_start_go functions, and more specifically at if statement testing if there's a target, walkmonsters set their .ideal_yaw to vectoyaw(self.goalentity.origin - self.origin), flymonsters don't do this. I have no idea if that statement is related to the problem, correlation does not imply causation. :)
I need to fresh up on my trig..
#598 posted by necros on 2011/06/29 20:52:34
it is a bug with movetogoal.
movetogoal does not check z unless both .goalentity AND .enemy is set.
if .enemy is set to a path_corner, it actually will track vertically. unfortunately, this necessitates a qc change.
ALSO, if you can believe it, THIS SAME FUCKING BUG IS PRESENT IN DOOM 3. CARMACK. COME ON MAN.
you will not believe the incredulity i felt when i set up a nice path for my cacodemons only to have them not able to fly up or down to reach them, even with AAS computed.
Oh Also
#599 posted by necros on 2011/06/29 20:54:43
walkmove explicitly does ignore z but it is only a simple 'step in this direction' function.
the function you want to look at is the movetogoal one, which does the random monster bumping around stuffs and probably contains the vertical adjustments for flyers when chasing enemies.
Necros
#600 posted by jt_ on 2011/06/30 01:23:14
Upon reading your posts, I naively added ``self.enemy = self.goalentity'' right after path_corner check in *monster_start_go functions in monsters.qc, thinking that it would fix all of my problems. Now the monsters are facing towards the path_corner and are in their walking animation, bit they're not moving at all. D: I suspect this has to do with a change I made, as I've been adding a lot of new things and haven't been testing any of them :p. oops.
Now 'all' I have to do if find what the problem is..
#601 posted by necros on 2011/06/30 02:27:22
you will need to haxor it in.
basically, in ai_walk, change:
movetogoal();
to
self.enemy = self.goalentity;
movetogoal();
self.enemy = world;
or alternatively, create a movetogoal wrapper only for walking:
void(float step) walktogoal =
{
self.enemy = self.goalentity;
movetogoal();
self.enemy = world;
}
and just replace movetogoal calls with walktogoal in ai_walk.
the wrapper method is probably cleaner and if you make new walking functions, you don't need to repeat the hack.
Remember That
#602 posted by Lardarse on 2011/07/02 17:52:49
movetogoal() also needs the distance.
Ragged
#603 posted by madfox on 2011/07/15 03:23:58
I'm trying the shield code for the EldenOgre, but I'm not lucky.
I found some arg in the qc I couldn't place, or I should look at the pentagram code but I couldn't trace it.
OLDONE.QC - line 271 => self.takedamage = DAMAGE_YES;
OLDONE.QC - line 138 => pl.takedamage = DAMAGE_NO;
So I thought to be smart by adding it to the shielding frames like:
void() xogre_shield4 =[ $shield4, xogre_shield5 ] { self.takedamage = DAMAGE_NO;};
This works, but now I can't stop it shielding!
Another thing is the line in Defs.QC
DEFS.QC - line 443 => .void() th_defense; // gb, need to defend against enemy fire
The oldone.qc is the only one with the th_defense in it.
Can I use it on an entity?
Maybe a weird question for someone who knows how the code DOES work, butI thought making an entity shield is something like giving it a Pentagram?
It's not Quake related, but anyone up for helping me with a SAT based collision detection issue?
Madfox
#605 posted by gb on 2011/07/17 21:53:27
th_defense is an RMQ specific AI extension.
Look in ai.qc under ai_defensecheck(). If a monster's self.enemy just fired a weapon, the monster does whatever is defined in its th_defense function.
In the case of the shield ogre, it does xogre_defense() which is calling the shield animation etc.
It also sets self.shielded to 1 (in the shield frames), which is checked in turn in weapons.qc. That is where the actual projectile reflection stuff is done.
xogre is the only monster with a defined defense behaviour atm; however, you can use self.th_defense on *any* monster in RMQ. Just need to make a mymonster_defense function that contains the defensive action. If the monster's defense requires some extra jazz, like projectile reflection, add that to weapons.qc.
All of this requires RMQ.
If you want to do this in your own progs.dat, just port the ai_defensecheck(), self.shielded and all related stuff to your codebase.
I'm pretty sure Supa wrote the actual projectile reflection code in weapons.qc, so ask her (on the trac) if you need help with that.
All of this is really pretty RMQ specific stuff. Monsters actually defending against attacks is something that's still work in progress.
Thanx Gb, For Your Explaination
#606 posted by madfox on 2011/07/17 22:41:47
I've been looking at the RMQ code, but as the normal QC.108 is already over my hat I thought to look at the normal monstercode.
The th.defense I found in Defs.qc and as I couldn't find it elsewhere I wondered where it could relay to.
I tried earlier to calculate the code in but when I used te RMQ code it started stuttering on other args.
Ambush
#607 posted by jt_ on 2011/09/06 02:48:47
Does the engine cause monsters not to make noise when their ambush flag is set? I can't find anything about it in progs.
#608 posted by Lardarse on 2011/09/06 04:39:07
Look in the code for an if statement looking at .spawnflags & 3 - there's a comment next to it about zombies having ambush on a different flag.
#609 posted by necros on 2011/09/07 01:23:21
no, monsters still make noise when they have ambush set.
i had to add that specifically into my progs to get silent monster wakeups.
Then What Does The Ambush Spawn Flag Do?
#610 posted by jt_ on 2011/09/07 04:32:22
Erm, makes them not wake up when they hear something...
#612 posted by metlslime on 2011/09/07 20:49:40
makes them not wake up until they see you -- normally they will also wake up if a nearby monster sees you.
Kind of general programming question about Quake. How does Quake handle broad-phase collision between entities. Does it store them in each BSP leaf and only collide those in the same leaf?
Linked In
#614 posted by Preach on 2011/10/04 23:28:42
There is a bit of a problem with that approach, which is key to understanding how the engine actually does it. The problem is that an entity might occupy more than one bsp leaf at the same time. So you would actually need a list for each entity of all the leaves that it spans.
Since this might get unwieldy the engine does something a bit simpler, and if you're interested in finding all the code behind it then the thing to search the source for is sv_areanodes*. Areanodes actually just split the space occupied by the world into a fixed depth binary tree by repeated partition along the longest axis of the previous areanode.
That last sentence is concise and accurate, so I instantly fear it's not very approachable. It's like making a new binary space partition which almost entirely ignores the geometry of the world and only uses the minimum and maximum points. It then recursive splits the space into two equally sized parts. It does this by splitting the longest side of the remaining space to try and keep the lengths of all the sides as even as possible.
An areanode is one of the dividing lines between two of these areas, or one of the areas themselves in the case of the leaves on the bottom level of the tree. If an entity straddles a division then it is stored within the list attached to that node. If it straddles many divisions then it is stored in the list belonging to the division furthest up the tree. If the entity is wholly contained in a single area, then unsurprisingly it is stored in the list belonging to that "leaf areanode".
It's then fairly simple to pare down the list of entities to collide against. Find the areanode that your collision trace belongs to (imagine the area swept out by the movement as being a big entity), then you need only test all entities in the subtree with that areanode as the root. If you go through the exact centre of the map then you will certainly be testing against all the entities in the map, but it's not often a problem in practice.
It's worth noting that quake actually has a fixed depth for this tree of just 4. This translates to 16 leaf areanodes representing volumes in the map, along with 15 splits separating them. To illustrate the size of that, if your map is 4 times wider in both x and y than it is tall, which it's easy to imagine something like Castle of the Damned might be, then there would be no areanode splits in the z axis at all, instead creating a 4x4 grid over the map.
Presumably custom engine coders who push both the extents of the bsp format and the number of entities in a map do increase these limits somewhat, either dynamically or just to a higher static cap.
*Warning: If you look too carefully in this region of the source you might find this pair of macros which make the Rune of Black Magic look tame...
#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
#define STRUCT_FROM_LINK(l,t,m)
������((t *)((byte *)l - (int)&(((t *)0)->m)))
ic... or at least, I think I do :P So this set up is kind of like a grid containing sub-grids kind of set up (can't remember what they're called)?
How does it manage the list of objects as it changes? ie how does it move an object from one node to another?
/slaps self for awful grammar.
Sorting
#617 posted by Preach on 2011/10/05 02:43:16
The term used within the engine for maintaining these lists is called linking*. You might have heard the term used in QC in discussions about SetOrigin. Typically guides would mention that if you set the origin key directly then the entity will not be correctly linked in the engine. This actually means the entity you moved will still be listed in the collision list for its old position.
In the same way that we have to be responsible and call SetOrigin every time that we move things in QC, the engine code has to call the relinking function every time that it updates the origin of an entity. The nitty-gritty of the relinking is not too complicated, just run through the areanodes until you find one that intersects your entity (or reach a leaf that contains you). Then remove yourself from the old list and append yourself to the new one.
*The term linking does have quite a nice visual metaphor of tying the entities to their areas, but I think it really only arose because the entities are stored in a traditional "linked list" structure.
#618 posted by necros on 2011/10/05 03:44:20
It's worth noting that quake actually has a fixed depth for this tree of just 4. This translates to 16 leaf areanodes representing volumes in the map, along with 15 splits separating them.
this is also the thing that causes large bmodels to flicker or disappear, i believe. especially with rotaters because qbsp errs on the side of (paranoid) caution and makes the bboxes massively oversized. it also doesn't take into account the actual rotations that you will be subjecting the rotater to (this is more understandable though), so if you had like a 1024 long brush that had just a 4x4 cross section, the bbox would still be roughly 1024x1024x1024, even if it only rotated along the long axis.
this makes it get connected to tons of those leafs and and overloads the engine. i guess it just either discards the first links it made, or stops linking when it hits the limit.
It's Weird...
#619 posted by metlslime on 2011/10/05 04:03:59
you'd think it would simply store the node of the first plane that the bbox is on both sides of. Then when testing two bboxes against each other, find out if their node pointers are related (identical nodes, or ancestor-descendant), then test bboxes directly. Maybe they did that and it wasn't fast enough on the target machines.
One thing to point out -- I attempted making a mapper-oriented console warning in fitzquake that said "entity XYZ touches too many leafs, exceeds MAX_ENT_LEAFS", but what i found was that even tiny id maps had these errors. So even e1m1 breaks the limit on a couple of entities, but you never see a symptom of it unless it goes over so much that none of the 16 leafs are in the current PVS, causing the entity to vanish.
|
|
You must be logged in to post in this thread.
|
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.
|
|