@ranger
#3499 posted by metlslime on 2024/12/17 11:56:21
weapons/r_exp3.wav seems to be directly called by some explosions in QC. But for rockets, it's hard-coded in the engine as part of TE_EXPLOSION
@metlslime
#3500 posted by ranger on 2024/12/17 18:29:36
oh so cannot adjust attn for explosion sound?
wanted explosion sound range to go further
Dynamically Spawn/replace Items?
#3501 posted by ranger on 2024/12/17 18:34:29
I saw some mods do this IIRC but it seems complex because quake has a "compiler order" it seems?
I can't have health kits spawn nearby enemies because it would say monster_zombie() is unknown but i cann't move monster_zombie.qc higher up in the "compiler order" because it then has multiple errors?
basically I want the game to have a chance of dynamically spawning nearby zombies at health kits - and it should by realtime/so works with any map
Forward Declaration
#3502 posted by Preach on 2024/12/19 16:41:40
What you need is a forward declaration of void monster_zombie().
https://en.wikipedia.org/wiki/Forward_declaration
Then you can keep zombie.qc lower in the compiler order and use it with healthkits.
For your explosion, you could maybe opt into the mission pack features and use TE_EXPLOSION2 which I think is silent so you can pair it with your own sound. Otherwise there's a hack posted by a scoundrel called Preach in the previous pages of this thread
https://www.celephais.net/board/view_thread.php?id=60097&start=1911&end=1935
@Preach
#3503 posted by ranger on 2024/12/22 11:58:49
is TE_EXPLOSION2 and such values, hardcoded?
got it, I see "Burst Of Particles"
is there a code number guide for palette colors? or I gotta count them manually?
--
also something is up with ATTN_STATIC and such for sounds? I tried setting the attn value for a sound to like 5 or 10 but seems no effect? the sound seems to still travel very far? but if i set it to ATTN_STATIC the range is reduced
Also This Is A Persistant Bug I'm Getting
#3504 posted by ranger on 2024/12/22 15:58:31
(trying to spawn new items/models dynamically in maps)
host error
pf precache can only be done in spawn functions?
Palette And More
#3505 posted by Preach on 2024/12/22 18:53:40
https://quakewiki.org/wiki/Quake_palette
Attenuation values wrap around when you hit the limit, so 1 = 5 = 9 etc. Don’t go above 4.
You can only precache at the first frame. You might need to rewrite the spawn function as multiple functions. One function to just precache things, one to actually create the entity without precache. The latter can be run at any time. But you must run the precache function on the initial frame somehow any time you might run the spawn function.
About Factions Revisted
#3506 posted by ranger on 2024/12/22 19:56:15
my quakec code not working? seems no effect ingame - all monsters still are all teams with each other against the player. (I even added AssignFaction() to every monster and confirmed the monsters are on the correct teams ingame)
in monsters.qc
void () monster_use =
{
if (self.enemy)
return;
if (self.health <= 0)
return;
if (activator.flags & FL_NOTARGET)
return;
//if (activator.classname != "player")
// return;
if (activator.classname != "player" &&
activator.faction != self.faction &&
activator.faction != FACTION_NEUTRAL &&
self.faction != FACTION_NEUTRAL)
{
self.enemy = activator;
self.nextthink = time + 0.1;
self.think = FoundTarget;
return;
}
self.enemy = activator;
self.nextthink = time + 0.1;
self.think = FoundTarget;
};
void() AssignFaction =
{
if (self.classname == "monster_shambler" )
{
self.faction = FACTION_A; //1
}
else if (self.classname == "monster_army" )
{
self.faction = FACTION_B; //2
}
else if (self.classname == "monster_tarbaby")
{
self.faction = FACTION_C; //3
}
else if (self.classname == "monster_dragon")
{
self.faction = FACTION_D; //4
}
// Default faction if none assigned above
if (!self.faction)
self.faction = FACTION_NEUTRAL; //0
}
in ai.qc
float(entity targ) MonsterCheckEnemy =
{
// Invalid or dead targets
if (!targ || targ.health <= 0)
return FALSE;
// Always target players
if (targ.classname == "player")
return TRUE;
// Don't target same faction
if (targ.faction == self.faction)
return FALSE;
// Neutral faction doesn't fight anyone except players
if (targ.faction == FACTION_NEUTRAL || self.faction == FACTION_NEUTRAL)
return FALSE;
// Different factions will fight
return TRUE;
};
float () FindTarget =
{
local entity client;
local float r;
if ((sight_entity_time >= (time - 0.10)) && !(self.spawnflags & 3))
{
client = sight_entity;
if (client.enemy == self.enemy)
return (FALSE);
}
else
{
if ((active_probes != FALSE) && (random () < 0.50))
{
beef = find (world, classname, "hologram");
if ((beef != world) && (beef.holo_owner.flags != (beef.holo_owner.flags | FL_NOTARGET)))
client = beef;
else
client = checkclient ();
}
else
client = checkclient ();
if (!client)
return (FALSE);
}
if (client == self.enemy)
return (FALSE);
if (client.flags & FL_NOTARGET)
return (FALSE);
// Add faction check
//if (client.faction == self.faction || client.faction == FACTION_NEUTRAL || self.faction == FACTION_NEUTRAL)
// return (FALSE);
if (!MonsterCheckEnemy(client))
return FALSE;
r = range (client);
if ((r == RANGE_FAR)) //out of range
return (FALSE);
if (!visible (client))
return (FALSE);
if (r == RANGE_NEAR)
{
if ((client.show_hostile < time) && !infront (client))
return (FALSE);
}
else if (r == RANGE_MID) //combat range max
{
if (!infront (client))
return (FALSE);
}
self.enemy = client;
if ((self.enemy.classname != "player") && (self.enemy.classname != "hologram"))
{
self.enemy = self.enemy.enemy;
if (self.enemy.classname != "player")
{
self.enemy = world;
return (FALSE);
}
}
FoundTarget ();
return (TRUE);
};
#3507 posted by ranger on 2024/12/22 19:59:56
To clarify all that code doesn't seem to work...
ingame monsters on the same side will infight if accidentally hit by teammates,
and will not automatically attack other monsters in other factions
Application
#3508 posted by Preach on 2024/12/22 23:01:23
Your MonsterCheckEnemy needs to be running in T_Damage to have an effect on infighting. And there’s nothing in your FindTarget that ever makes “client” a monster, because checkclient only yields players. So there’s no chance of fighting breaking out spontaneously.
@Preach
#3509 posted by ranger on 2024/12/23 06:39:20
"Your MonsterCheckEnemy needs to be running in T_Damage to have an effect on infighting. "
"And there’s nothing in your FindTarget that ever makes “client” a monster, because checkclient only yields players. So there’s no chance of fighting breaking out spontaneously. "
Can you explain these 2 points in more detail? or provide code snippets? I'm still new to this
T_Damage
#3510 posted by ranger on 2024/12/23 08:12:30
nvm I got the prevent faction infighting in - based on the area that you mentioned before in combat.qc/T_Damage
FindTarget
#3511 posted by ranger on 2024/12/23 15:34:31
I tried adding this , and different monsters in different factions do fight, but strangely only when they first took damage from the player
//>>> If no player found, look for monsters
if (!client)
{
//client = find(world, flags, FL_MONSTER);
client = find(world, classname, "monster_*");
while (client)
{
if (client != self && MonsterCheckEnemy(client))
{
r = range(client);
if (r != RANGE_FAR && visible(client) &&
(r == RANGE_NEAR || infront(client)))
{
break;
}
}
//client = find(client, flags, FL_MONSTER);
client = find(world, classname, "monster_*");
}
}
No Wildcards
#3512 posted by Preach on 2024/12/26 12:46:54
I had to wait for a chance to research this. Can now confirm that “find” does NOT support wildcards, so your code is still not ever finding monsters. Unsure how you are ever getting spontaneous fights but it isn’t via the planned route. Here’s a function that will help, it’s a hand-rolled search for a flag that fits your commented out line of code.
entity (entity start) findNextMonster = {
local entity e;
e = start;
while ( (e = nextent(e)) ) {
if( e.flags & FL_MONSTER) {
return e;
}
return world;
}
}
It might be better to build a bit more structure around it if you want something akin to checkclient, like a global that stores the candidate monster, which is rotated once a frame in StartFrame(). And don’t forget that checkclient does a visibility check which is up to you to handle on the monster side.
Thanks! It Is Working Now!
#3513 posted by ranger on 2024/12/26 19:29:06
But sadly i'm getting a very bizarre unrelated bug?
Host_Error:PR_ExecuteProgram: Null Function
I even restored the original unedited monster code and it's still having the error?
Nvm I Figured It Out It Was An Properly Coded Animation
#3514 posted by ranger on 2024/12/26 20:18:23
Regarding Monster AI
#3515 posted by ranger on 2024/12/26 20:30:09
I was wondering how to improve it?
like even simple pathfinding? (funnily enough the Vore bomb has better cornering than the actual vore/monsters)
or being able to go up and down reasonable heights?
and how to change the angle before the monster can shoot? because seems grunt can shoot the player before his barrel is even pointed straight at the player?
and how to change the max & min "tilt" angles for monsters? because want some monsters to not be able to shoot at enemies at are at too steep angles
(this is what i have so far but honestly i don't really know what i am doing - very new at this)
---
float CheckMonsterJump(entity self)
{
vector start, end;
// Check ground below
start = self.origin;
end = start;
end_z = end_z - 64; // Adjust drop height check
traceline(start, end, TRUE, self);
if (trace_fraction == 1)
return FALSE; // Too high to drop
if (trace_fraction < 1)
{
// Safe to drop - within acceptable height
if ((start_z - trace_endpos_z) <= 64)
{
self.flags = self.flags - (self.flags & FL_ONGROUND);
self.velocity_z = -300; // Adjust drop speed
return TRUE;
}
}
// Check for jumpable height
start = self.origin;
end = start;
end_z = end_z + 32; // Jump check height
traceline(start, end, TRUE, self);
if (trace_fraction == 1)
{
self.velocity_z = 300; // Jump velocity
return TRUE;
}
return FALSE;
}
float TryAlternativePath(entity self)
{
vector dir, right, left, start, end;
float right_clear, left_clear;
// Get perpendicular directions
dir = normalize(self.enemy.origin - self.origin);
right = '0 0 0';
right_x = dir_y;
right_y = -dir_x;
left = -right;
// Check right path
start = self.origin;
end = start + (right * 128); // Adjust side check distance
traceline(start, end, TRUE, self);
right_clear = trace_fraction;
// Check left path
end = start + (left * 128);
traceline(start, end, TRUE, self);
left_clear = trace_fraction;
if (right_clear > left_clear)
{
self.ideal_yaw = vectoyaw(right);
return TRUE;
}
else if (left_clear > right_clear)
{
self.ideal_yaw = vectoyaw(left);
return TRUE;
}
return FALSE;
}
AI Tutorials
#3516 posted by Preach on 2024/12/26 23:11:18
The gold standard for quake AI tutorials are found at the “AI cafe”.
https://web.archive.org/web/20080603081043/http://minion.planetquake.gamespy.com:80/tutorial/main.htm
There are several tutorials about navigation. But bear in mind there’s no magic wand to switch perfect navigation on. Most modern games get better navigation by tagging their levels with lots of metadata. In Quake levels that don’t have any, you’ll never get the same results.
Also tutorials there about aiming. But bear in mind that the current aiming for grunts is
1 aim exactly at the player
2 “correct” the aim to slightly behind where they are now
The point I’m circling is that fun AI needs to be imperfect. Making them shoot the way they are facing is a simple change. The hard part is making them rotate in such a way that they are still fun to fight when they fire straight.
Example Of Grunt's Aiming Leeway
#3517 posted by ranger on 2024/12/27 04:28:04
https://imgur.com/a/DMMkpxO
if you sneakup behind grunt and he shoots, seems there's a 60d arc where it's considered "pointing the gun at you"
This also applies to enforcers and such
trying to reduce this to 5d or less
^It's FacingIdeal In AI.qc
#3518 posted by ranger on 2024/12/27 05:22:16
delta
Bug Where Monster Turns Into Solid BSP But Is Still Alive
#3519 posted by ranger on 2024/12/27 09:30:35
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?
#3520 posted by ranger on 2024/12/27 10:12:06
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
#3521 posted by Preach on 2024/12/27 20:48:40
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!
#3522 posted by ranger on 2024/12/28 19:46:56
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
#3523 posted by Preach on 2024/12/28 20:13:29
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.
|