|
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. |
|
|
Light-based AI
#3475 posted by Preach on 2024/08/30 23:32:47
One of the obstacles is that basic Quake engines don't give you a way to detect light levels. You'll need a custom engine that support the DP_QC_GETLIGHT extension (for example Darkplaces).
Once you have that, you can tell the light at a given coordinate. Worth remembering that Quake doesn't actually have a 3D representation of lighting - a model is lit according to the light level of the floor directly below it. That's what the function will give you, so plan accordingly when placing shadow areas in your map.
What you do with the light level is a whole other matter. In basic Quake, AI awareness is an on-off affair - either the monster is entirely ignorant of you or they are actively attacking and have omniscient awareness of exactly where you are. You'd probably want to invent at least two other states of behaviour:
- Monster is not alerted to you yet, but saw something in the shadows they want to investigate
- Monster was alerted but lost sight of you, and now wants to hunt in the area they last saw you
You'll have to add a field to store the monster's alertness level and make it behave differently at each level. One idea to try out is to create two different sequences of walking functions for e.g. patrol and investigating, which can share the same animation frames but pair them with different AI functions.
Another thing to add to the monster is a vector field to store where they think the player is - crucial to keep this updated when they can see the player (including when they see what they suspect to be the player but aren't sure) and equally important to stop updating it when they can't see you.
Once you have this, the refinements can pile on top. Maybe you give some monsters higher intelligence and have them anticipate where you were heading when they're in hunting mode - offset the last-seen location a short distance in the direction you were moving. Perhaps let even let them take another random guess if they arrive and see you aren't where they expected.
"getlight" Command In Darkplaces - How To Use It?
#3476 posted by rsrsceo on 2024/09/01 09:19:32
I'm working on the stealth system as detailed above, and I'm making steady progress. One thing I'm not sure how to do is use the getlight command from Darkplaces in/as a function. Once I get that part figured out, the rest should be smooth sailing. Any and all help is greatly appreciated.
Also, what sort of light values does getlight output, RBG or just a general light level?
Getlight
#3477 posted by Preach on 2024/09/01 22:28:06
Latter question is a quick answer, it's return a vector with RGB components - you can add them together to get an overall light level.
Basic way to access the function
1) Download the DarkPlaces mod from
https://icculus.org/twilight/darkplaces/download.html
2) Look at the QC that comes with the mod and find dpextensions.qc
3) Open dpextensions.qc and search for the code that defines getlight
4) Copy that code and add it to the bottom of your mod's defs.qc file.
You can repeat steps 3 and 4 for each DP extension you would like to use. Or if you prefer the kitchen sink approach, just add the entire dpextensions.qc file to your mod.
Casting An Entity Origin To The Client/Player Origin?
#3478 posted by rsrsceo on 2024/09/14 00:27:55
I'm working on the stealth mod detailed in my previous posts, and I've run into a conundrum that I'm not sure how to solve.
I want to get the players origin whenever they are visible and assign it to an entity that tracks their last visible position. Whenever I try doing this, the entity just gets assigned to [world.origin].
I'm new to QuakeC but am making steady progress, any and all help is appreciated, thanks!
Three Options
#3479 posted by Preach on 2024/09/14 11:20:20
In your design are you planning:
1. To have a global record of the last place any of the players were seen by any of the monsters
2. To have a record on each player of the last place they were seen by any monster
3. To have a record on each monster of the last place they saw their current enemy
Option 1 is different to option 2 when you think about co-op. To be more blunt, option 1 doesn't work very well in co-op.
Game Crashes When Attacking Unaware Monster
#3480 posted by rsrsceo on 2024/09/16 07:46:36
A [self.think] statement seems to be missing from the ai.qc script (or another related script). Whenever I attack an unaware/passive enemy, the level will crash and boot me to the command menu. Does anyone know where the missing [self.think] statement should go for aggro-ing an enemy?
For context, I'm using StealthQuake by RenegadeC as a base for this mod and I've changed up quite a bit of stuff because the base mod pretty much entirely changes up how Quake plays.
Speculation
#3481 posted by Preach on 2024/09/17 08:23:14
Never seen the source code for StealthQuake, but I can explain how a similar crash can happen in vanilla Quake and that might point you in the right direction. Take a look at HuntTarget
void() HuntTarget =
{
self.goalentity = self.enemy;
self.think = self.th_run;
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
self.nextthink = time + 0.1;
SUB_AttackFinished (1); // wait a while before first attack
};
This function runs when a monster spots a player, and the dangerous line is self.think = self.th_run. The idea is th_run contains a think function bottled up for future use, and HuntTarget finally uncorks it. But if th_run is empty, then you've just scheduled an empty function to run in 0.1 seconds, and a crash is imminent.
Have a look at your code to see if the same transfer from th_run to think is happening when a monster wakes up, or if a similar transfer is happening from a different field. Then make sure that when you create your monster you populate that field with the correct ai function.
Few Biginner Questions
#3482 posted by ranger on 2024/11/20 04:55:01
ok you know how normally quake monsters attack only players on sight, and infight with dissimilar monsters?
...how do i do these changes?
1 - specific monsters are on another "team" and attack other monsters/players who are not on that "team"
2 - to prevent specified team's monsters from infighting (while other team's monsters may still do?
3 do i have to do these changes to every monster's qc or is there a way to do this "globally"?
Sound Ranges
#3483 posted by ranger on 2024/11/20 16:06:27
Also how can I edit ATTN_NONE ATTN_NORM ATTN_IDLE etc?
I want the distance of explosions and gunshots to go further
Hi Ranger
#3484 posted by Preach on 2024/11/21 16:20:01
Gonna take a whole post over answering each of these, and I'm gonna start with "prevent specified team's monsters from infighting (while other team's monsters may still do)".
Did you know that there's already an exception to the normal infighting rules? Two Grunts will infight even though they are the same class of monster. It's the opposite way round to what you're trying to do because it causes MORE infighting. But locating the code where this exception appears is a good way to learn how to set up the rules we want.
if ( (self.flags & FL_MONSTER) && attacker != world)
{
// get mad unless of the same class (except for soldiers)
if (self != attacker && attacker != self.enemy)
{
if ( (self.classname != attacker.classname)
|| (self.classname == "monster_army" ) )
{
if (self.enemy.classname == "player")
self.oldenemy = self.enemy;
self.enemy = attacker;
FoundTarget ();
}
}
}
This code is found in combat.qc, and it applies to all monsters, so you can make a change centrally and create the team functionality.
We need a way to record which team things belong to. We could call it .float monster_team, with the idea that monster_team = 0 represents membership on no teams and each other team gets a unique number. Then we can change the test to
if ((self.classname != attacker.classname && (self.monster_team = 0 || self.monster_team != attacker.monster_team)) || self.classname == "monster_army")
That should make it so that monsters assigned the same team will forgive friendly fire from teammates rather than retaliate (although I've left the exception in place for Grunts for flavour). If you had something a bit different in mind - e.g. a "team" of drones who forgive all friendly fire and focus exclusively on the player, that's possible too. Just write a different rule for which pairs of self and attacker cause self to target attacker.
Next time I'll return to question 1, a more thorny problem...
Appendix
#3485 posted by Preach on 2024/11/22 00:04:07
The test is getting a bit complicated, so it would probably be better to refactor it into its own function
float(entity monster, entity attacker) IsAttackerFairTarget
{
if(monster.classname == "monster_army")
return TRUE; //grunts will attack anything
if(monster.monster_team > 0 && monster.monster_team == attacker.monster_team)
return FALSE; //if we belong to a team, don't attack teammates
return monster.classname != attacker.classname;
}
Preach Is Da Man!
Gotta love it when Preach "preaches"! So much information in a couple hundred words.
Monster Vs Monster
#3487 posted by Preach on 2024/11/23 00:51:42
Coming back to
1 - specific monsters are on another "team" and attack other monsters/players who are not on that "team"
In the first post we've adjusted the rules for REACTION in combat so that our teams don't fight amongst themselves. But getting them to pro-actively attack the other team on their own initiative is another story. I see two big problems
Problem 1: In regular quake, the task of searching for a client who the monster might be able to see and ruling them out if they are in a part of the map not visible from the monster's position is entirely offloaded to the engine. The function is called "checkclient" and it does the whole thing in one go.
Because it's in the engine, your mod can't change how it functions. So you'd have to write something new from scratch in QuakeC to replace it. The engine has access to additional data that QuakeC can't get at, and it exploits some of that data in checkclient - so you can't even create an exact copy in QuakeC as a baseline. It's a very tall order.
Supposing you solve problem 1, problem 2 is a more practical one. You create your map full of rooms, each populated by squads of monsters from team A facing a squad from team B. The player spawns in, and your new AI kicks in. Across the map, team A fights with team B, even when the player is nowhere nearby. A victorious team emerges in each fight, and for the player the only evidence of all this groundbreaking AI is a pile of corpses from team A, and some half-health monsters from team B.
This is probably not what you had in mind - more likely you were wanting the player to be present each time the teams fight so they can see and engage in the spectacle.
That's why I would recommend you CHEAT instead. Rather than creating brilliant AI, just script the fights a bit. Have an entity who looks for monsters with a specific targetname, and assigns them an enemy with the same targetname from the other team. Make sure you trigger this entity when the player is approaching the battlefield. And when a monster kills a monster with the same targetname, make it retrigger to pick them another target.
Attenuation
#3488 posted by Preach on 2024/11/24 22:14:26
Also how can I edit ATTN_NONE ATTN_NORM ATTN_IDLE etc?
The good news is that while the values of
ATTN_NONE/ATTN_NORM/ATTN_IDLE/ATTN_STATIC make it look like there are just discrete values for attenuation, actually the final parameter to sound is a float, and you can supply any decimal starting at 0.0 and going up to (but not including) 4.0.
One of the confusing things is getting a handle on exactly how the scale works. It's actually a backwards scale - the higher the number, the smaller the distance that the sound travels. ATTN_NONE is the absence of attenuation, which means that the sound transmits across the entire level at full volume. If you still want a drop off, but slower than normal sounds, why not try a value of 0.5 instead of ATTN_NORM?
Tiny detail: ATTN_NORM and volume of 1.0 do have a magic benefit compared to other values, although it's a fairly minor one. They actually require less network bandwidth to transmit in a packet, because they are regarded as the "baseline" values, and the protocol only transmits variation from the baseline. In single player scenarios this doesn't make a difference, but it's an interesting quirk to note.
|
|
You must be logged in to post in this thread.
|
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.
|
|