quakec/source/server/ai/dog_core.qc

492 lines
No EOL
15 KiB
C++

/*
server/ai/dog_core.qc
dog things
Copyright (C) 2021-2024 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
void() Dog_Think;
void() Dog_Death;
// DOG RUN
// 0-6
$frame dogrun1 dogrun2 dogrun3 dogrun4 dogrun5 dogrun6 dogrun7
void() dog_runanim =[ $dogrun1, dog_runanim2 ] {Dog_Think();Zombie_Walk(30);self.frame = 0;};
void() dog_runanim2 =[ $dogrun2, dog_runanim3 ] {Dog_Think();Zombie_Walk(20);self.frame = 1;};
void() dog_runanim3 =[ $dogrun3, dog_runanim4 ] {Dog_Think();Zombie_Walk(20);self.frame = 2;zombie_footstep();};
void() dog_runanim4 =[ $dogrun4, dog_runanim5 ] {Dog_Think();Zombie_Walk(20);self.frame = 3;};
void() dog_runanim5 =[ $dogrun5, dog_runanim6 ] {Dog_Think();Zombie_Walk(20);self.frame = 4;};
void() dog_runanim6 =[ $dogrun6, dog_runanim7 ] {Dog_Think();Zombie_Walk(20);self.frame = 5;};
void() dog_runanim7 =[ $dogrun7, Dog_Walk_Setup ] {Dog_Think();Zombie_Walk(20);self.frame = 6;zombie_footstep();};
// DOG WALK
// 7-18
$frame dogwalk1 dogwalk2 dogwalk3 dogwalk4 dogwalk5 dogwalk6 dogwalk7 dogwalk8 dogwalk9 dogwalk10 dogwalk11 dogwalk12
void() dog_walkanim =[ $dogwalk1, dog_walkanim2 ] {Dog_Think();Zombie_Walk(10);self.frame = 7;};
void() dog_walkanim2 =[ $dogwalk2, dog_walkanim3 ] {Dog_Think();Zombie_Walk(10);self.frame = 8;zombie_footstep();};
void() dog_walkanim3 =[ $dogwalk3, dog_walkanim4 ] {Dog_Think();Zombie_Walk(10);self.frame = 9;};
void() dog_walkanim4 =[ $dogwalk4, dog_walkanim5 ] {Dog_Think();Zombie_Walk(10);self.frame = 10;};
void() dog_walkanim5 =[ $dogwalk5, dog_walkanim6 ] {Dog_Think();Zombie_Walk(10);self.frame = 11;};
void() dog_walkanim6 =[ $dogwalk6, dog_walkanim7 ] {Dog_Think();Zombie_Walk(10);self.frame = 12;};
void() dog_walkanim7 =[ $dogwalk7, dog_walkanim8 ] {Dog_Think();Zombie_Walk(10);self.frame = 13;zombie_footstep();};
void() dog_walkanim8 =[ $dogwalk8, dog_walkanim9 ] {Dog_Think();Zombie_Walk(10);self.frame = 14;};
void() dog_walkanim9 =[ $dogwalk9, dog_walkanim10 ] {Dog_Think();Zombie_Walk(10);self.frame = 15;};
void() dog_walkanim10 =[ $dogwalk10, dog_walkanim11 ] {Dog_Think();Zombie_Walk(10);self.frame = 16;};
void() dog_walkanim11 =[ $dogwalk11, dog_walkanim12 ] {Dog_Think();Zombie_Walk(10);self.frame = 17;};
void() dog_walkanim12 =[ $dogwalk12, Dog_Walk_Setup ] {Dog_Think();Zombie_Walk(10);self.frame = 18;};
// DOG IDLE
// 19-24
$frame dogstand1 dogstand2 dogstand3 dogstand4 dogstand5 dogstand6
void() dog_idleanim =[ $dogstand1, dog_idleanim2 ] {Dog_Think();self.frame = 19;};
void() dog_idleanim2 =[ $dogstand2, dog_idleanim3 ] {Dog_Think();self.frame = 20;};
void() dog_idleanim3 =[ $dogstand3, dog_idleanim4 ] {Dog_Think();self.frame = 21;};
void() dog_idleanim4 =[ $dogstand4, dog_idleanim5 ] {Dog_Think();self.frame = 22;};
void() dog_idleanim5 =[ $dogstand5, dog_idleanim6 ] {Dog_Think();self.frame = 23;};
void() dog_idleanim6 =[ $dogstand6, dog_idleanim ] {Dog_Think();self.frame = 24;};
// DOG MELEE
// 25-34
$frame dogmelee1 dogmelee2 dogmelee3 dogmelee4 dogmelee5 dogmelee6 dogmelee7 dogmelee8 dogmelee9 dogmelee10
void() dog_meleeanim =[ $dogmelee1, dog_meleeanim2 ] {Dog_Think(); makevectors(self.angles); self.velocity += v_forward * 50; self.frame = 26;};
void() dog_meleeanim2 =[ $dogmelee2, dog_meleeanim3 ] {Dog_Think();Zombie_Walk(0);self.frame = 28; Sound_PlaySound(self, "sounds/hound/a0.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);};
void() dog_meleeanim3 =[ $dogmelee3, dog_meleeanim4 ] {Dog_Think();Zombie_Walk(0);zombie_attack2();self.frame = 30;};
void() dog_meleeanim4 =[ $dogmelee4, dog_meleeanim5 ] {Dog_Think();Zombie_Walk(0);self.frame = 32;};
void() dog_meleeanim5 =[ $dogmelee5, dog_runanim ] {Dog_Think();Zombie_Walk(0);self.frame = 34;};
// DOG DEATH
// 35 - 36
$frame dogdeath1 dogdeath2
void() dog_deathanim =[ $dogdeath1, dog_deathanim2 ] {self.frame = 35;};
void() dog_deathanim2 =[ $dogdeath2, SUB_Null ] {self.nextthink = time + 3; self.think = removeZombie; self.frame = 36;};
// DOG LIGHTNING SPAWN
// 1-13
$frame dls1 dls2 dls3 dls4 dls5 dls6 dls7 dls8 dls9 dls10 dls11 dls12 dls13
void() dog_lightninganim =[ $dls1, dog_lightninganim2 ] {self.frame = 1;};
void() dog_lightninganim2 =[ $dls2, dog_lightninganim3 ] {self.frame = 2;};
void() dog_lightninganim3 =[ $dls3, dog_lightninganim4 ] {self.frame = 3;};
void() dog_lightninganim4 =[ $dls4, dog_lightninganim5 ] {self.frame = 4;};
void() dog_lightninganim5 =[ $dls5, dog_lightninganim6 ] {self.frame = 5;};
void() dog_lightninganim6 =[ $dls6, dog_lightninganim7 ] {self.frame = 6;};
void() dog_lightninganim7 =[ $dls7, dog_lightninganim8 ] {self.frame = 7;};
void() dog_lightninganim8 =[ $dls8, dog_lightninganim9 ] {self.frame = 8;};
void() dog_lightninganim9 =[ $dls9, dog_lightninganim10 ] {self.frame = 9;};
void() dog_lightninganim10 =[ $dls10, dog_lightninganim11 ] {self.frame = 10;};
void() dog_lightninganim11 =[ $dls11, dog_lightninganim12 ] {self.frame = 11;};
void() dog_lightninganim12 =[ $dls12, dog_lightninganim13 ] {self.frame = 12;};
void() dog_lightninganim13 =[ $dls13, SUB_Null ] {self.frame = 13; remove(self);};
// DOG EXPLODE SPRITE
// 1-5
$frame des1 des2 des3 des4 des5 des6
void() dog_explodeanim =[ $des1, dog_explodeanim2 ] {self.frame = 1;};
void() dog_explodeanim2 =[ $des2, dog_explodeanim3 ] {self.frame = 2;};
void() dog_explodeanim3 =[ $des3, dog_explodeanim4 ] {self.frame = 3;};
void() dog_explodeanim4 =[ $des4, dog_explodeanim5 ] {self.frame = 4;};
void() dog_explodeanim5 =[ $des5, dog_explodeanim6 ] {self.frame = 5;};
void() dog_explodeanim6 =[ $des5, SUB_Null ] {removeZombie();};
void() Dog_Walk_Setup =
{
if (self.walktype == 1)
dog_walkanim();
else
dog_runanim();
};
//
// Dog_Taunt()
// Plays some random Taunt sounds
//
void() Dog_Taunt =
{
if (self.sound_time > time)
return;
self.sound_time = time + 3 * random();
if (random() > 0.5) {
Sound_PlaySound(self, "sounds/hound/t0.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
} else {
Sound_PlaySound(self, "sounds/hound/t1.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
}
}
void() Dog_Think =
{
Dog_Taunt();
// Hellhounds begin to run whenever their target is in their line of sight.
// So perform a tracemove and see if it hits it
if (self.ads_release == 0) {
#ifdef FTE
float result = tracemove(self.origin, VEC_HULL_MIN, VEC_HULL_MAX, self.enemy.origin, TRUE, self);
#else
float result = tracemove_fake(self.origin, VEC_HULL_MIN, VEC_HULL_MAX, self.enemy.origin, TRUE, self);
#endif // FTE
self.ads_release = result;
}
// Stalk...
if (self.ads_release == 0)
self.walktype = 1;
// Charge...
else
self.walktype = 2;
// Play particles if we're on fire
if (self.onfire) {
Effect_Fire(self.origin);
}
}
float dogCount;
void(entity ent) Dog_Death_Cleanup = {
// Already dead
if(ent.aistatus == "0") {
return;
}
ent.aistatus = "0";
ent.solid = SOLID_NOT;
ent.movetype = MOVETYPE_NONE;
ent.takedamage = DAMAGE_NO;
sound(ent, CHAN_WEAPON, "sounds/null.wav", 1, ATTN_NORM);
ent.usedent = world;
ent.health = 0;
Remaining_Zombies = Remaining_Zombies - 1;
dogCount -= 1;
}
void() Dog_Death = {
Dog_Death_Cleanup(self);
if(rounds == dogRound && Remaining_Zombies == 0) {
Spawn_Powerup(self.origin, PU_MAXAMMO);
}
if (self.onfire || self.electro_targeted) {
self.frame = 0;
setmodel(self, "models/sprites/explosion.spr");
Sound_PlaySound(self, "sounds/weapons/grenade/explode.wav", SOUND_TYPE_WEAPON_EXPLODE, SOUND_PRIORITY_PLAYALWAYS);
dog_explodeanim();
} else {
dog_deathanim();
}
Sound_PlaySound(self, "sounds/hound/d0.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
self.onfire = 0;
self.ads_release = 0;
spawn_time = time + zombie_spawn_delay;
};
// DOG WAFFE DEATH
// 37-46
void() dog_die_wunder1 =[ 37, dog_die_wunder2 ] {tesla_arc();};
void() dog_die_wunder2 =[ 38, dog_die_wunder3 ] {tesla_arc();};
void() dog_die_wunder3 =[ 39, dog_die_wunder4 ] {tesla_arc();};
void() dog_die_wunder4 =[ 40, dog_die_wunder5 ] {tesla_arc();};
void() dog_die_wunder5 =[ 41, dog_die_wunder6 ] {tesla_arc();};
void() dog_die_wunder6 =[ 42, dog_die_wunder7 ] {tesla_arc();};
void() dog_die_wunder7 =[ 43, dog_die_wunder8 ] {tesla_arc();};
void() dog_die_wunder8 =[ 44, dog_die_wunder9 ] {tesla_arc();};
void() dog_die_wunder9 =[ 45, dog_die_wunder10 ] {tesla_arc();};
void() dog_die_wunder10 =[ 46, SUB_Null ] {tesla_arc(); self.iszomb=0; self.nextthink=time+3; self.think=removeZombie;};
void() Dog_Death_Tesla = {
Dog_Death_Cleanup(self);
// TODO - place tesla spark correctly
tesla_spark(self.origin);
if(rounds == dogRound && Remaining_Zombies == 0) {
Spawn_Powerup(self.origin, PU_MAXAMMO);
}
dog_die_wunder1();
};
//
// Dog_FindEnemy()
// Hellhounds are assigned an enemy at start,
// based on how many Hounds are stalking each
// player already.
//
entity() Dog_FindEnemy =
{
entity least_targeted_player = world;
// Assume that if this is being called again, stop hunting
// currently selected client
if (self.enemy != world) {
self.enemy.hunt_count--;
self.ads_release = 0;
}
// Grab every player in the World
entity players = find(world, classname, "player");
while(players != world) {
// Don't target players in Last Stand
if (!players.downed) {
// Init least_targeted_player
if (least_targeted_player == world)
least_targeted_player = players;
// This player has less dogs following them than the last,
// make this our new golden goose.
if (players.hunt_count < least_targeted_player.hunt_count)
least_targeted_player = players;
}
players = find(players, classname, "player");
}
// Return the player we found
if (least_targeted_player != world) {
least_targeted_player.hunt_count++;
return least_targeted_player;
}
// Couldn't find a player, go lurk at a spawn point
return find(world, classname, "info_player_1_spawn");
}
//
// Dog_GetHealth()
// Returns the amount of health Hellhounds
// should have.
//
float() Dog_GetHealth =
{
switch(dog_round_count) {
case 1: return 400;
case 2: return 900;
case 3: return 1300;
default: return 1600;
}
return 1600;
}
void(entity where) spawn_a_dogB =
{
local entity sdog;//USED FOR WHAT TO SPAWN
sdog = getFreeZombieEnt();
if(sdog == world)
{
return;
}
sdog.origin = where.origin;
sdog.frame = 0;
sdog.target = where.target;
sdog.solid = SOLID_CORPSE;
sdog.movetype = MOVETYPE_WALK;
setmodel(sdog, "models/ai/dog.mdl");
sdog.hop_step = 0;
sdog.gravity = 1.0;
sdog.mins = '-16 -16 -32';
sdog.maxs = '16 16 16';
setsize (sdog, sdog.mins, sdog.maxs);
if(pointcontents(sdog.origin - '0 0 36') == -2)
{
while(pointcontents (sdog.origin - '0 0 36') == -2)
{
sdog.origin = sdog.origin + '0 0 1';
setorigin(sdog,sdog.origin );
}
}
sdog.classname = "ai_dog";
sdog.aistatus = "1";
setorigin (sdog.goaldummy, '0 0 0');
sdog.origin_z = sdog.origin_z + 1;
sdog.takedamage = DAMAGE_YES;
setorigin(sdog, sdog.origin);
sdog.flags = sdog.flags | FL_PARTIALGROUND | FL_MONSTER;
sdog.spawnflags = where.spawnflags;
sdog.spawnflags = sdog.spawnflags | 1;
sdog.ideal_yaw = sdog.angles_y;
sdog.yaw_speed = 20;
sdog.health = Dog_GetHealth();
sdog.th_die = Dog_Death;
sdog.th_walk = Dog_Walk_Setup;
sdog.outside = FALSE;
sdog.iszomb = 1;
sdog.onfire = (random() > 0.5);
sdog.th_melee = dog_meleeanim;
sdog.th_idle = dog_idleanim;
sdog.th_diewunder = Dog_Death_Tesla;
sdog.enemy = Dog_FindEnemy();
sdog.electro_targeted = 0;
sdog.skin = 0;
SetZombieWalk(sdog);
sdog.reload_delay = 30 + time;//save floats, equals respawn time.
local entity old_self;
old_self = self;
self = sdog;
//droptofloor();
self.th_walk();
self = old_self;
};
void() dogsprite_think =
{
self.frame++;
if (self.frame >= 3)
self.frame = 0;
// suicide timer!
if(self.ltime < time) {
spawn_a_dogB(self.owner);
remove(self);
}
self.nextthink = time + 0.05;
}
void(entity where) spawn_dog_lightning =
{
local entity tempe;
local entity doglight;
local entity dogsprite;
// lightning model
doglight = spawn();
setmodel(doglight, "models/ai/dog_lightning.mdl");
setorigin(doglight, where.origin - '0 0 20');
tempe = self;
self = doglight;
dog_lightninganim();
self = tempe;
// lightning sprite
dogsprite = spawn();
setmodel(dogsprite, "models/sprites/lightning.spr");
setorigin(dogsprite, where.origin);
dogsprite.effects = EF_FULLBRIGHT;
dogsprite.owner = where;
dogsprite.think = dogsprite_think;
dogsprite.nextthink = time + 0.05;
dogsprite.ltime = time + 1.3; // we use ltime here to be out remove timer,
// since using frames interrupts think()
Sound_PlaySound(world, "sounds/misc/electric_bolt.wav", SOUND_TYPE_ENV_VOICE, SOUND_PRIORITY_PLAYALWAYS);
}
float() spawn_a_dogA =
{
local float pcount;
local entity thing, szombie;
local float FAIL;
FAIL = false;
pcount = 0;
szombie = getFreeZombieEnt();
if(szombie == world || dogCount >= (2 * (player_count + 1)))
{
return 0;
}
lastspawn = find(lastspawn, classname, "spawn_dog");
while (random() < 0.4)
{
lastspawn = find(lastspawn, classname, "spawn_dog");
}
while(lastspawn)
{
thing = findradius(lastspawn.origin, 60);
while (thing)
{
pcount = 0;
if (thing.classname == "ai_dog")
{
pcount = 1;
break;
}
thing = thing.chain;
}
if (!pcount && random() < 0.6)
{
//spawn_a_dogB(lastspawn);
spawn_dog_lightning(lastspawn);
dogCount++;
spawn_time = time + zombie_spawn_delay;
return true;
}
lastspawn = find(lastspawn, classname, "spawn_dog");
}
return 0; //no free locations fround
};
void() spawn_dog =
{
precache_model("models/ai/dog.mdl");
precache_model("models/ai/dog_lightning.mdl");
precache_model("models/sprites/lightning.spr");
precache_model("models/sprites/explosion.spr");
precache_sound("sounds/rounds/droundend.wav");
precache_sound("sounds/rounds/droundstart.wav");
precache_sound("sounds/misc/electric_bolt.wav");
precache_sound("sounds/hound/d0.wav");
precache_sound("sounds/hound/a0.wav");
precache_sound("sounds/hound/t0.wav");
precache_sound("sounds/hound/t1.wav");
setsize(self, '0 0 0', '0 0 0');
if (self.spawnflags & INACTIVE)
{
if (cvar("developer"))
setmodel(self, "models/player.mdl");
self.classname = "spawn_dog_in";
}
else
{
if (cvar("developer"))
setmodel(self, "models/ai/dog.mdl");
self.classname = "spawn_dog";
}
self.solid = SOLID_NOT;
};