1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074 |
- /* Copyright (C) 1996-2022 id Software LLC
- 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 the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- See file, 'COPYING', for details.
- */
- .float storednextthink;
- .void() storedthink;
- void() Countdown;
- void() Wavecheck;
- void() SpawnAmmo;
- .float wave; // what wave of the horde mode we're on
- .float fodder; // how many fodder squads to spawn
- .float elites; // how many elite squads to spawn
- .float bosses; // how many bosses to spawn
- .float spawncount; // number of monster spawns
- .float army; // Spawn Army? True/fallse
- float(entity spawnpoint) CheckBlockedSpawn;
- float key_spawned; // TRUE/FALSE, determine if key has already been spawned, prevent dupes.
- float SPAWN_RESET_TIME = 5; // amount of time that a spawn point is considered occupied and not valid for use
- /*
- ================
- HordeGetPlayersAlive
- added Aug31 2021
- Returns a float for the number of living players
- ================
- */
- float() HordeGetPlayersAlive =
- {
- local float playercount;
- local entity e;
-
- // count entities
- e = find(world, classname, "player");
- while(e)
- {
- if (e.health > 0)
- {
- if (e.flags & FL_CLIENT)
- playercount++;
- else
- dprint("Player is no longer a client!\n");
- }
- e = find(e, classname, "player");
- }
- dprint("there are ");
- dprint(ftos(playercount));
- dprint(" players alive.\n");
-
- return playercount;
- };
- /*
- ================
- HordeGetMonstersAlive
- added Oct28 2021
- Manually counts all living monsters
- ================
- */
- float() HordeGetMonstersAlive =
- {
- local float monstercount;
- local entity e;
-
- // count monster entities
- e = find(world, category, "monster");
- while(e)
- {
- if ((e.health > 0) && (e.classname != "monster_zombie"))
- monstercount++;
-
- e = find(e, category, "monster");
- }
- dprint("verification count. There are ");
- dprint(ftos(monstercount));
- dprint(" monsters alive.\n");
-
- return monstercount;
- };
- /*
- ================
- HordeFindTarget
- added Aug31 2021
- Returns a random living player
- ================
- */
- entity() HordeFindTarget =
- {
- local float playercount = HordeGetPlayersAlive();
-
- if (playercount == 0)
- return world;
-
- local entity e;
- local float rcount; // random value [0,count]
- rcount = random() * playercount;
-
- playercount = 0; // reset for new count
- e = find(world, classname, "player");
- while(e)
- {
- if (e.health > 0)
- playercount++;
-
- if ((rcount <= playercount) && !(e.flags & FL_NOTARGET))
- return e;
- else
- e = find(e, classname, "player");
- }
- return world;
- };
- /*QUAKED info_monster_start START_OFF
- if targeted, it will toggle between on or off, like lights
- */
- float SKIP_BLOCK_CHECK = 2;
- void() monster_start_use =
- {
- if (self.spawnflags & START_OFF)
- self.spawnflags = self.spawnflags - START_OFF;
- else
- self.spawnflags = self.spawnflags + START_OFF;
- };
- void() info_monster_start =
- {
- self.wait = FALSE; // used for checking if this is a valid spawn
- self.use = monster_start_use;
- setorigin (self, self.origin);
- setsize(self, '-80 -80 0', '80 80 128');
- };
- void() info_monster_start_ranged = // used for ogres and enforcers
- {
- self.wait = FALSE; // used for checking if this is a valid spawn
- self.use = monster_start_use;
- setorigin (self, self.origin);
- setsize(self, '-44 -44 0', '44 44 128');
- };
- void() info_monster_start_flying = // used exclusively for scrags (wizards)
- {
- self.wait = FALSE; // used for checking if this is a valid spawn
- self.use = monster_start_use;
- setorigin (self, self.origin);
- setsize(self, '-80 -80 0', '80 80 128');
- };
- void() info_monster_start_boss = // used for shalraths and shamblers
- {
- self.wait = FALSE; // used for checking if this is a valid spawn
- self.use = monster_start_use;
- setorigin (self, self.origin);
- setsize(self, '-44 -44 0', '44 44 128');
- };
- // ENTITY
- void() info_horde_ammo =
- {
- //self.wait = FALSE;
- self.think = SpawnAmmo;
- self.nextthink = time + HORDE_START_DELAY + random() * 3;
- };
- // ENTITY
- void() info_horde_item =
- {
- self.wait = FALSE;
- };
- float KEY_FIRST = 1;
- float KEY_SECOND = 2;
- float KEY_THIRD = 4;
- float KEY_FOURTH_PLUS = 8;
- // ENTITY
- void() info_horde_key =
- {
- self.wait = FALSE;
- };
- //============================================================================
- void() PrecacheMonsters =
- {
- // knight
- precache_model ("progs/knight.mdl");
- precache_model ("progs/h_knight.mdl");
- precache_sound ("knight/kdeath.wav");
- precache_sound ("knight/khurt.wav");
- precache_sound ("knight/ksight.wav");
- precache_sound ("knight/sword1.wav");
- precache_sound ("knight/sword2.wav");
- precache_sound ("knight/idle.wav");
-
- // hellknight
- precache_model2 ("progs/hknight.mdl");
- precache_model2 ("progs/k_spike.mdl");
- precache_model2 ("progs/h_hellkn.mdl");
-
- precache_sound2 ("hknight/attack1.wav");
- precache_sound2 ("hknight/death1.wav");
- precache_sound2 ("hknight/pain1.wav");
- precache_sound2 ("hknight/sight1.wav");
- precache_sound ("hknight/hit.wav"); // used by C code, so don't sound2
- precache_sound2 ("hknight/slash1.wav");
- precache_sound2 ("hknight/idle.wav");
- precache_sound2 ("hknight/grunt.wav");
- precache_sound ("knight/sword1.wav");
- precache_sound ("knight/sword2.wav");
-
- // dog
- precache_model ("progs/h_dog.mdl");
- precache_model ("progs/dog.mdl");
- precache_sound ("dog/dattack1.wav");
- precache_sound ("dog/ddeath.wav");
- precache_sound ("dog/dpain1.wav");
- precache_sound ("dog/dsight.wav");
- precache_sound ("dog/idle.wav");
-
- // demon
- precache_model ("progs/demon.mdl");
- precache_model ("progs/h_demon.mdl");
- precache_sound ("demon/ddeath.wav");
- precache_sound ("demon/dhit2.wav");
- precache_sound ("demon/djump.wav");
- precache_sound ("demon/dpain1.wav");
- precache_sound ("demon/idle1.wav");
- precache_sound ("demon/sight2.wav");
-
- // ogre
- precache_model ("progs/ogre.mdl");
- precache_model ("progs/h_ogre.mdl");
- precache_model ("progs/grenade.mdl");
- precache_sound ("ogre/ogdrag.wav");
- precache_sound ("ogre/ogdth.wav");
- precache_sound ("ogre/ogidle.wav");
- precache_sound ("ogre/ogidle2.wav");
- precache_sound ("ogre/ogpain1.wav");
- precache_sound ("ogre/ogsawatk.wav");
- precache_sound ("ogre/ogwake.wav");
-
- // grunt
- precache_model ("progs/soldier.mdl");
- precache_model ("progs/h_guard.mdl");
- precache_model ("progs/gib1.mdl");
- precache_model ("progs/gib2.mdl");
- precache_model ("progs/gib3.mdl");
- precache_sound ("soldier/death1.wav");
- precache_sound ("soldier/idle.wav");
- precache_sound ("soldier/pain1.wav");
- precache_sound ("soldier/pain2.wav");
- precache_sound ("soldier/sattck1.wav");
- precache_sound ("soldier/sight1.wav");
- precache_sound ("player/udeath.wav"); // gib death
-
- // enforcer
- precache_model2 ("progs/enforcer.mdl");
- precache_model2 ("progs/h_mega.mdl");
- precache_model2 ("progs/laser.mdl");
- precache_sound2 ("enforcer/death1.wav");
- precache_sound2 ("enforcer/enfire.wav");
- precache_sound2 ("enforcer/enfstop.wav");
- precache_sound2 ("enforcer/idle1.wav");
- precache_sound2 ("enforcer/pain1.wav");
- precache_sound2 ("enforcer/pain2.wav");
- precache_sound2 ("enforcer/sight1.wav");
- precache_sound2 ("enforcer/sight2.wav");
- precache_sound2 ("enforcer/sight3.wav");
- precache_sound2 ("enforcer/sight4.wav");
-
- // shambler
- precache_model ("progs/shambler.mdl");
- precache_model ("progs/s_light.mdl");
- precache_model ("progs/h_shams.mdl");
- precache_model ("progs/bolt.mdl");
-
- precache_sound ("shambler/sattck1.wav");
- precache_sound ("shambler/sboom.wav");
- precache_sound ("shambler/sdeath.wav");
- precache_sound ("shambler/shurt2.wav");
- precache_sound ("shambler/sidle.wav");
- precache_sound ("shambler/ssight.wav");
- precache_sound ("shambler/melee1.wav");
- precache_sound ("shambler/melee2.wav");
- precache_sound ("shambler/smack.wav");
-
- // shalrath
- precache_model2 ("progs/shalrath.mdl");
- precache_model2 ("progs/h_shal.mdl");
- precache_model2 ("progs/v_spike.mdl");
-
- precache_sound2 ("shalrath/attack.wav");
- precache_sound2 ("shalrath/attack2.wav");
- precache_sound2 ("shalrath/death.wav");
- precache_sound2 ("shalrath/idle.wav");
- precache_sound2 ("shalrath/pain.wav");
- precache_sound2 ("shalrath/sight.wav");
-
- // wizard
- precache_model ("progs/wizard.mdl");
- precache_model ("progs/h_wizard.mdl");
- precache_model ("progs/w_spike.mdl");
- precache_sound ("wizard/hit.wav"); // used by c code
- precache_sound ("wizard/wattack.wav");
- precache_sound ("wizard/wdeath.wav");
- precache_sound ("wizard/widle1.wav");
- precache_sound ("wizard/widle2.wav");
- precache_sound ("wizard/wpain.wav");
- precache_sound ("wizard/wsight.wav");
-
- // zombies
- precache_model ("progs/zombie.mdl");
- precache_model ("progs/h_zombie.mdl");
- precache_model ("progs/zom_gib.mdl");
- precache_sound ("zombie/z_idle.wav");
- precache_sound ("zombie/z_idle1.wav");
- precache_sound ("zombie/z_shot1.wav");
- precache_sound ("zombie/z_gib.wav");
- precache_sound ("zombie/z_pain.wav");
- precache_sound ("zombie/z_pain1.wav");
- precache_sound ("zombie/z_fall.wav");
- precache_sound ("zombie/z_miss.wav");
- precache_sound ("zombie/z_hit.wav");
- precache_sound ("zombie/idle_w2.wav");
-
- // ammo
- precache_model ("maps/b_shell1.bsp");
- precache_model ("maps/b_shell0.bsp");
- precache_model ("maps/b_nail1.bsp");
- precache_model ("maps/b_nail0.bsp");
- precache_model ("maps/b_rock1.bsp");
- precache_model ("maps/b_rock0.bsp");
- precache_model ("maps/b_batt1.bsp");
- precache_model ("maps/b_batt0.bsp");
-
- // items
- precache_model("maps/b_bh10.bsp"); // rotten health
- precache_sound("items/r_item1.wav");
- precache_model("maps/b_bh25.bsp"); // regular health
- precache_sound("items/health1.wav");
- precache_model ("progs/armor.mdl"); // regular armor
-
- // quad damage
- precache_model ("progs/quaddama.mdl");
- precache_sound ("items/damage.wav");
- precache_sound ("items/damage2.wav");
- precache_sound ("items/damage3.wav");
-
- // pentagram (invulnerability)
- precache_model ("progs/invulner.mdl");
- precache_sound ("items/protect.wav");
- precache_sound ("items/protect2.wav");
- precache_sound ("items/protect3.wav");
-
- // new horde sounds
- precache_sound("horde/nyom.wav");
-
- // precache keys
- if (world.worldtype == WORLDTYPE_MEDIEVAL || world.worldtype == WORLDTYPE_HUB)
- {
- precache_sound ("misc/medkey.wav");
- precache_model("progs/w_g_key.mdl"); // gold key
- precache_model("progs/w_s_key.mdl"); // silver key
- }
- if (world.worldtype == WORLDTYPE_METAL)
- {
- precache_sound ("misc/runekey.wav");
- precache_model ("progs/m_g_key.mdl");
- precache_model ("progs/m_s_key.mdl");
- }
- if (world.worldtype == WORLDTYPE_BASE)
- {
- precache_sound2 ("misc/basekey.wav");
- precache_model2 ("progs/b_g_key.mdl");
- precache_model2 ("progs/b_s_key.mdl");
- }
- };
- // =================================================================
- void() GibMonster =
- {
- T_Damage(self, world, world, 4000);
- };
- // =================================================================
- /*
- ================
- SpawnMonster
- ================
- */
- entity(string class, vector org, vector temp_angles) SpawnMonster =
- {
- local entity monster;
- monster = spawn();
-
- monster.angles = temp_angles;
- monster.solid = SOLID_SLIDEBOX;
- monster.movetype = MOVETYPE_STEP;
- //monster.target = "horde_manager";
- monster.flags = monster.flags | FL_MONSTER;
- //monster.alpha = 1; // only set monster alpha after they're a corpse
-
- local entity oself; // old self
-
- // perform enemy-type specific loading
- if (class == "knight")
- {
- setmodel (monster, "progs/knight.mdl");
- setsize (monster, '-16 -16 -24', '16 16 40');
- org = org + '0 0 24'; // offset based on size
- monster.classname = "monster_knight";
- monster.health = 75;
- monster.th_stand = knight_stand1;
- monster.th_walk = knight_walk1;
- monster.th_run = knight_run1;
- monster.th_melee = knight_atk1;
- monster.th_pain = knight_pain;
- monster.th_die = knight_die;
- }
- else if (class == "hellknight")
- {
- setmodel (monster, "progs/hknight.mdl");
- setsize (monster, '-16 -16 -24', '16 16 40');
- org = org + '0 0 24';
- monster.classname = "monster_hell_knight";
- monster.health = 250;
- monster.th_stand = hknight_stand1;
- monster.th_walk = hknight_walk1;
- monster.th_run = hknight_run1;
- monster.th_melee = hknight_melee;
- monster.th_missile = hknight_magicc1;
- monster.th_pain = hknight_pain;
- monster.th_die = hknight_die;
- }
- else if (class == "dog")
- {
- setmodel (monster, "progs/dog.mdl");
- setsize (monster, '-32 -32 -24', '32 32 40');
- org = org + '0 0 24';
- monster.classname = "monster_dog";
- monster.health = 25;
- monster.th_stand = dog_stand1;
- monster.th_walk = dog_walk1;
- monster.th_run = dog_run1;
- monster.th_pain = dog_pain;
- monster.th_die = dog_die;
- monster.th_melee = dog_atta1;
- monster.th_missile = dog_leap1;
- }
- else if (class == "demon")
- {
- setmodel (monster, "progs/demon.mdl");
- setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
- monster.classname = "monster_demon1";
- monster.health = 300;
- org = org + '0 0 48';
- monster.th_stand = demon1_stand1;
- monster.th_walk = demon1_walk1;
- monster.th_run = demon1_run1;
- monster.th_die = demon_die;
- monster.th_melee = Demon_MeleeAttack;
- monster.th_missile = demon1_jump1;
- monster.th_pain = demon1_pain;
- }
- else if (class == "ogre")
- {
- setmodel (monster, "progs/ogre.mdl");
- setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
- monster.classname = "monster_ogre";
- monster.health = 200;
- org = org + '0 0 32';
- monster.th_stand = ogre_stand1;
- monster.th_walk = ogre_walk1;
- monster.th_run = ogre_run1;
- monster.th_die = ogre_die;
- monster.th_melee = ogre_melee;
- monster.th_missile = ogre_nail1;
- monster.th_pain = ogre_pain;
- }
- else if (class == "grunt")
- {
- setmodel (monster, "progs/soldier.mdl");
- setsize (monster, '-16 -16 -24', '16 16 40');
- monster.classname = "monster_army";
- monster.health = 30;
- org = org + '0 0 24';
- monster.th_stand = army_stand1;
- monster.th_walk = army_walk1;
- monster.th_run = army_run1;
- monster.th_missile = army_atk1;
- monster.th_pain = army_pain;
- monster.th_die = army_die;
- }
- else if (class == "enforcer")
- {
- setmodel (monster, "progs/enforcer.mdl");
- setsize (monster, '-16 -16 -24', '16 16 40');
- monster.classname = "monster_enforcer";
- monster.health = 80;
- org = org + '0 0 24';
- monster.th_stand = enf_stand1;
- monster.th_walk = enf_walk1;
- monster.th_run = enf_run1;
- monster.th_pain = enf_pain;
- monster.th_die = enf_die;
- monster.th_missile = enf_atk1;
- }
- else if (class == "shambler")
- {
- setmodel (monster, "progs/shambler.mdl");
- setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
- monster.classname = "monster_shambler";
- monster.health = 600;
- org = org + '0 0 32';
- monster.th_stand = sham_stand1;
- monster.th_walk = sham_walk1;
- monster.th_run = sham_run1;
- monster.th_die = sham_die;
- monster.th_melee = sham_melee;
- monster.th_missile = sham_magic1;
- monster.th_pain = sham_pain;
- }
- else if (class == "shalrath")
- {
- setmodel (monster, "progs/shalrath.mdl");
- setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
- monster.classname = "monster_shalrath";
- org = org + '0 0 32';
- monster.health = 400;
- monster.th_stand = shal_stand;
- monster.th_walk = shal_walk1;
- monster.th_run = shal_run1;
- monster.th_die = shalrath_die;
- monster.th_pain = shalrath_pain;
- monster.th_missile = shal_attack1;
- }
- else if (class == "wizard")
- {
- setmodel (monster, "progs/wizard.mdl");
- monster.flags = monster.flags | FL_FLY; // special behavior
- monster.classname = "monster_wizard";
- setsize (monster, VEC_HULL_MIN, VEC_HULL_MAX);
- org = org + '0 0 32';
- monster.health = 80;
- monster.th_stand = wiz_stand1;
- monster.th_walk = wiz_walk1;
- monster.th_run = wiz_run1;
- monster.th_missile = Wiz_Missile;
- monster.th_pain = Wiz_Pain;
- monster.th_die = wiz_die;
- }
- else if (class == "zombie")
- {
- setmodel (monster, "progs/zombie.mdl");
- setsize (monster, '-16 -16 -24', '16 16 40');
- monster.classname = "monster_zombie";
- org = org + '0 0 32';
- monster.health = 60;
- monster.th_stand = zombie_stand1;
- monster.th_walk = zombie_walk1;
- monster.th_run = zombie_run1;
- monster.th_pain = zombie_pain;
- monster.th_die = zombie_die;
- monster.th_missile = zombie_missile;
- monster.target = string_null; // don't target anything!
- }
- else
- {
- dprint("ERROR: No monster provided");
- }
- // finish the shared settings
- setorigin (monster, org);
- monster.origin_z = monster.origin_z + 1; // raise off floor a bit
-
- // temporarily swap self for next funcs
- oself = self;
- self = monster;
-
- if (class != "wizard")
- droptofloor();
-
- if (!walkmove(0,0))
- {
- dprint ("walkmonster in wall at: ");
- dprint (vtos(self.origin));
- dprint ("\n");
- }
-
- // resume normal self behavior
- self = oself;
-
- monster.category = CATEGORY_MONSTER;
- monster.takedamage = DAMAGE_AIM;
- monster.ideal_yaw = monster.angles * '0 1 0';
- if (!monster.yaw_speed)
- monster.yaw_speed = 20;
- monster.view_ofs = '0 0 25';
- monster.use = monster_use;
-
- monster.flags = monster.flags | FL_MONSTER;
-
- monster.enemy = HordeFindTarget(); // find a random living player
- //monster.enemy = find (world, classname, "player");
- monster.think = FoundTarget;
-
- //monster.nextthink = monster.nextthink + 0.1;
- monster.nextthink = time + 0.1;
-
- spawn_tdeath_fast (monster.origin, monster);
- //spawn_tfog(monster.origin);
-
- monster.owner = self;
- if (monster.classname != "monster_zombie") // don't count zombies toward total goal
- total_monsters = total_monsters + 1;
-
-
- return monster;
- };
- void() horde_ammo_touch =
- {
- local entity temp_player, t;
- local float best_weapon;
-
- // early exit touch if not a player or not alive
- if (other.classname != "player")
- return;
- if (other.health <= 0)
- return;
-
- // if the player was using their best weapon, change up to the new one if better
- t = self; // hang on to the ammo entity for a moment
- self = other;
- best_weapon = W_BestWeapon();
- self = t; // set self back to ammo entity
-
- // if player is at max ammo for ammo's type, return
- if (((self.weapon == 1) && (other.ammo_shells >= 100)) || // shotgun
- ((self.weapon == 2) && (other.ammo_nails >= 200)) || // nailgun
- ((self.weapon == 3) && (other.ammo_rockets >= 100)) || // rockets
- ((self.weapon == 4) && (other.ammo_cells >= 100))) // cells
- return;
-
- sprint(other, "$qc_got_item", self.netname);
-
- // ammo touch sound
- sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM);
- stuffcmd (other, "bf\n");
- // change to a better weapon if appropriate
- if ( other.weapon == best_weapon )
- {
- t = self;
- self = other;
- self.weapon = W_BestWeapon();
- W_SetCurrentAmmo ();
- self = t;
- }
- // loop through all players and give ammo
- temp_player = find(world, classname, "player");
- while(temp_player)
- {
- if (temp_player.health > 0) // only give ammo to living players
- {
- if (self.weapon == 1) // shotgun
- temp_player.ammo_shells += self.aflag;
- else if (self.weapon == 2) // spikes
- temp_player.ammo_nails += self.aflag;
- else if (self.weapon == 3) // rockets
- temp_player.ammo_rockets += self.aflag;
- else if (self.weapon == 4) // cells
- temp_player.ammo_cells += self.aflag;
- // temp swap and set bounds
- t = other;
- other = temp_player;
- bound_other_ammo();
- other = t;
-
- // temp swap again and set current ammo
- t = self;
- self = temp_player;
- W_SetCurrentAmmo();
- self = t;
- }
- temp_player = find(temp_player, classname, "player");
- }
-
- // remove
- self.model = string_null;
- self.solid = SOLID_NOT;
- // AY dec06 2021
- //self.owner.wait = FALSE; The Old Way
- self.owner.think = SpawnAmmo;
- self.owner.nextthink = time + HORDE_AMMO_RESPAWN_DELAY;
- // end AY
- remove(self);
- };
- void() horde_health_touch =
- {
- if (other.classname != "player")
- return;
-
- if (!T_Heal(other, self.healamount, 0))
- return;
-
- sprint(other, "$qc_item_health", ftos(self.healamount));
-
- // health touch sound
- sound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM);
- stuffcmd (other, "bf\n");
-
- self.model = string_null;
- self.solid = SOLID_NOT;
- self.owner.wait = FALSE;
- remove(self);
- };
- void() horde_armor_touch =
- {
- local float type, value, bit;
-
- if (other.health <= 0)
- return;
- if (other.classname != "player")
- return;
- if (self.classname == "item_armor1")
- {
- type = 0.3;
- value = 100;
- bit = IT_ARMOR1;
- }
- if (self.classname == "item_armor2")
- {
- type = 0.6;
- value = 150;
- bit = IT_ARMOR2;
- }
- if (self.classname == "item_armorInv")
- {
- type = 0.8;
- value = 200;
- bit = IT_ARMOR3;
- }
- if (other.armortype*other.armorvalue >= type*value)
- return;
-
- other.armortype = type;
- other.armorvalue = value;
- other.items = other.items - (other.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + bit;
- self.solid = SOLID_NOT;
- self.model = string_null;
- sprint(other, "$qc_item_armor");
- // armor touch sound
- sound(other, CHAN_ITEM, "items/armor1.wav", 1, ATTN_NORM);
- stuffcmd (other, "bf\n");
- self.owner.wait = FALSE;
- remove(self);
- };
- void() horde_print_keys =
- {
- dprint(ftos(keys_silver));
- dprint(" silver keys : ");
- dprint(ftos(keys_gold));
- dprint(" gold keys\n");
- };
- void(entity temp_player) horde_set_keys =
- {
- // player has just respawned, reset their keys based on global key values
- if (keys_silver > 0)
- temp_player.items+= IT_KEY1;
-
- if (keys_gold > 0)
- temp_player.items+= IT_KEY2;
- };
-
- void(float key_item) horde_key_give_all =
- {
- local entity temp_player;
- temp_player = find(world, classname, "player");
-
- while (temp_player)
- {
- temp_player.items = temp_player.items + key_item;
- temp_player = find(temp_player, classname, "player");
- }
-
- horde_print_keys();
- };
- void(float key_item) horde_key_remove_all =
- {
- local entity temp_player;
- temp_player = find(world, classname, "player");
-
- while (temp_player)
- {
- temp_player.items = temp_player.items - key_item;
- temp_player = find(temp_player, classname, "player");
- }
-
- horde_print_keys();
- };
- void(float key_item)horde_key_give =
- {
- if (key_item & IT_KEY1)
- {
- keys_silver++;
- if (keys_silver == 1)
- horde_key_give_all(IT_KEY1);
- }
- else if (key_item & IT_KEY2)
- {
- keys_gold++;
- if (keys_gold == 1)
- horde_key_give_all(IT_KEY2);
- }
- horde_print_keys();
- }
- void(float key_item)horde_key_spend =
- {
- if (key_item & IT_KEY1)
- {
- keys_silver--;
- if (keys_silver == 0)
- horde_key_remove_all(IT_KEY1);
- }
- else if (key_item & IT_KEY2)
- {
- keys_gold--;
- if (keys_gold == 0)
- horde_key_remove_all(IT_KEY2);
- }
- horde_print_keys();
- }
- void() horde_key_touch =
- {
- if (other.classname != "player")
- return;
- if (other.health <= 0)
- return;
- if (other.flags & FL_ISBOT)
- return;
-
- sprint(other, "$qc_got_item", self.netname);
-
- sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);
- stuffcmd (other, "bf\n");
-
- horde_key_give(self.items);
-
- // find the horde manager and trigger the next wave
- if (key_spawned)
- {
- dprint("key triggers next wave\n");
- horde_ent.wait = TRUE;
- horde_ent.think = Countdown;
- horde_ent.nextthink = time;
- }
- else
- dprint("key shouldn't trigger next wave!\n");
- /*
- local entity t = find(world, classname, "horde_manager");
- if (t != world)
- {
- t.wait = TRUE;
- t.think = Countdown;
- t.nextthink = time;
- }
- */
- remove(self);
- }
- void() SpawnAmmo =
- {
- local float r, is_big;
- local entity item;
- local vector pos;
- pos = self.origin;
- item = spawn();
-
- // roll a d4, a 1 means big ammo.
- is_big = (random() * 4) <= 1;
-
- if (is_big)
- pos = pos - '16 16 0';
- else
- pos = pos - '12 12 0';
-
- // NEW: roll a d20, 1-8 is shells, 9-14 is spikes, 14-17 is rockets, 18-20 is cells
- // OLD: roll a d12, 1-5 is shells, 6-10 is spikes, 11 is rockets, 12 is cells
- /*
- ITEM New % Old %
- shells 40% 41.66%
- spikes 30% 33.33%
- rockets 20% 8.33 %
- cells 10% 8.33 %
- */
- r = random() * 20;
- if (r <= 7) // shells
- {
- item.classname = "item_shells";
- if (is_big)
- {
- setmodel (item, "maps/b_shell1.bsp");
- item.aflag = 40;
- }
- else
- {
- setmodel (item, "maps/b_shell0.bsp");
- item.aflag = 20;
- }
- item.weapon = 1;
- item.netname = "$qc_shells";
- }
- else if (r <= 14) // spikes
- {
- item.classname = "item_spikes";
- if (is_big)
- {
- setmodel (item, "maps/b_nail1.bsp");
- item.aflag = 50;
- }
- else
- {
- setmodel (item, "maps/b_nail0.bsp");
- item.aflag = 25;
- }
- item.weapon = 2;
- item.netname = "$qc_nails";
- }
- else if (r <= 17) // rockets
- {
- item.classname = "item_rockets";
- if (is_big)
- {
- setmodel (item, "maps/b_rock1.bsp");
- item.aflag = 10;
- }
- else
- {
- setmodel (item, "maps/b_rock0.bsp");
- item.aflag = 5;
- }
- item.weapon = 3;
- item.netname = "$qc_rockets";
- }
- else // cells
- {
- item.classname = "item_cells";
- if (is_big)
- {
- setmodel (item, "maps/b_batt1.bsp");
- item.aflag = 12;
- }
- else
- {
- setmodel (item, "maps/b_batt0.bsp");
- item.aflag = 6;
- }
- item.weapon = 4;
- item.netname = "$qc_cells";
- }
- setorigin (item, pos + '0 0 1');
- setsize (item, '0 0 0', '32 32 56');
- item.owner = self;
- self.wait = TRUE;
- item.movetype = MOVETYPE_TOSS;
- item.solid = SOLID_TRIGGER;
- item.flags = FL_ITEM;
- item.touch = horde_ammo_touch;
-
- spawn_tfog_silent(pos + '0 0 8'); // added aug30
-
- //item.think = PlaceItem;
- //item.nextthink = time + 0.1;
- }
- void() SpawnItem =
- {
- local float r;
- local entity item;
- local vector pos;
- item = spawn();
-
- pos = self.origin; // set to origin, then offset as needed
- r = random() * 6;
- if (r < 3) // rotten health
- {
- item.classname = "item_health";
- item.touch = horde_health_touch;
- setmodel (item, "maps/b_bh10.bsp");
- item.noise = "items/r_item1.wav";
- item.healamount = 15;
- item.healtype = 0;
- setsize (item, '0 0 0', '32 32 56');
- //pos = pos - '16 16 0';
- }
- if (r < 5) // regular health
- {
- item.classname = "item_health";
- item.touch = horde_health_touch;
- setmodel (item, "maps/b_bh25.bsp");
- item.noise = "items/health1.wav";
- item.healamount = 25;
- item.healtype = 1;
- setsize (item, '0 0 0', '32 32 56');
- pos = pos - '16 16 0';
- }
- else // armor
- {
- item.classname = "item_armor1";
- item.touch = horde_armor_touch;
- item.armortype = 0.3;
- item.armorvalue = 100;
- setmodel (item, "progs/armor.mdl");
- item.skin = 0;
- setsize (item, '-16 -16 0', '16 16 56');
- }
- setorigin (item, pos + '0 0 1');
- item.owner = self;
- self.wait = TRUE;
- item.movetype = MOVETYPE_TOSS;
- item.solid = SOLID_TRIGGER;
- item.flags = FL_ITEM;
- //item.think = PlaceItem;
- //item.nextthink = time + 0.1;
- spawn_tfog_silent(pos + '0 0 8'); // added aug30
- };
- /* Spawn Key
- A key should spawn after every boss wave.
- */
- void(float is_gold) SpawnKey =
- {
- local entity item;
- item = spawn();
-
- if (is_gold)
- {
- item.items = IT_KEY2;
-
- // set model and sound based on worldtype
- if (world.worldtype == WORLDTYPE_METAL)
- {
- item.netname = "$qc_gold_runekey";
- item.noise = "misc/runekey.wav";
- setmodel (item, "progs/m_g_key.mdl");
- centerprint_all("$qc_horde_gold_runekey_appears");
- }
- else if (world.worldtype == WORLDTYPE_BASE)
- {
- item.netname = "$qc_gold_keycard";
- item.noise = "misc/basekey.wav";
- setmodel (item, "progs/b_g_key.mdl");
- centerprint_all("$qc_horde_gold_keycard_appears");
- }
- else // assume medieval
- {
- item.netname = "$qc_gold_key";
- item.noise = "misc/medkey.wav";
- setmodel (item, "progs/w_g_key.mdl");
- centerprint_all("$qc_horde_gold_key_appears");
- }
- }
- else
- {
- item.items = IT_KEY1;
-
- // set model and sound based on worldtype
- if (world.worldtype == WORLDTYPE_METAL)
- {
- item.netname = "$qc_silver_runekey";
- item.noise = "misc/runekey.wav";
- setmodel (item, "progs/m_s_key.mdl");
- centerprint_all("$qc_horde_silver_runekey_appears");
- }
- else if (world.worldtype == WORLDTYPE_BASE)
- {
- item.netname = "$qc_silver_keycard";
- item.noise = "misc/basekey.wav";
- setmodel (item, "progs/b_s_key.mdl");
- centerprint_all("$qc_horde_silver_keycard_appears");
- }
- else // assume medieval
- {
- item.netname = "$qc_silver_key";
- item.noise = "misc/medkey.wav";
- setmodel (item, "progs/w_s_key.mdl");
- centerprint_all("$qc_horde_silver_key_appears");
- }
- }
- //item.noise = "misc/medkey.wav";
- setsize (item, '-16 -16 -25', '16 16 32');
- item.touch = horde_key_touch;
- //item.think = PlaceItem;
- //item.nextthink = time + 0.2;
- item.target = "horde_manager"; // when used, retrigger the horde manager
- item.flags = FL_ITEM; // make extra wide
- //item.flags = item.flags & FL_NOBOTS; // AY 11 Nov 2021 so bots can't get keys
- item.solid = SOLID_TRIGGER;
- item.movetype = MOVETYPE_TOSS;
- setorigin (item, self.origin + '0 0 32');
- item.velocity = '0 0 255';
-
- // AY Feb22, 2022, rune keys shouldn't have light effects on them
- if (world.worldtype != WORLDTYPE_METAL)
- item.effects = EF_BRIGHTLIGHT;
-
- //dprint(" spawned key\n");
- spawn_tfog(item.origin);
- key_spawned = 1;
- };
- void() SpawnGoldKey =
- {
- SpawnKey(TRUE);
- };
-
- void() SpawnSilverKey =
- {
- SpawnKey(FALSE);
- };
- // Function get a key given the current wave
- // Wave 3 should return a key with spawnflag "KEY_FIRST"
- // Wave 6 should return a key with spawnflag "KEY_SECOND"
- // Wave 9 should return a key with spawnflag "KEY_THIRD"
- // Wave 9+ should return a key with spawnflag "KEY_FOURTH_PLUS"
- // If a key with the correct spawnflag isn't found, return any key
- void() GetKey =
- {
- local entity t, l;
- t = find(world, classname, "info_horde_key");
- l = t; // save ref for later
-
- // check if any keys exist
- if (t == world)
- {
- dprint("ERROR: No info_horde_key!\n");
- // continue as if player got the key
- self.think = Countdown;
- self.nextthink = time + 4;
- return;
- }
-
- while(t != world)
- {
- if (self.wave <= 3) // looking for KEY_FIRST
- {
- if (t.spawnflags & KEY_FIRST)
- {
- t.think = SpawnSilverKey;
- t.nextthink = time;
- return;
- }
- }
- else if (self.wave <= 6) // looking for KEY_SECOND
- {
- if (t.spawnflags & KEY_SECOND)
- {
- t.think = SpawnSilverKey;
- t.nextthink = time;
- return;
- }
- }
- else if (self.wave <= 9) // looking for KEY_THIRD
- {
- if (t.spawnflags & KEY_THIRD)
- {
- t.think = SpawnGoldKey;
- t.nextthink = time;
- return;
- }
- }
- else // looking for KEY_FOURTH_PLUS
- {
- // check next key
- if (t.spawnflags & KEY_FOURTH_PLUS)
- {
- t.think = SpawnSilverKey;
- t.nextthink = time;
- return;
- }
- }
- t = find(t, classname, "info_horde_key");
- }
-
- // didn't find key with matching spawnflag, return any key
- dprint("didn't find key with matching spawnflag. Return last: ");
- dprint(l.classname);
-
- if (self.wave == 9)
- l.think = SpawnGoldKey;
- else
- l.think = SpawnSilverKey;
- l.nextthink = time;
- };
- //============================================================================
- /*
- SpawnSquad2
- Yoder FEB02 2022
- */
- void(string name, vector org, vector temp_angles) SpawnSquad2 =
- {
- if (name == "3 grunts")
- {
- if (skill > 0)
- {
- SpawnMonster("grunt", org + '0 -40 0', temp_angles);
- SpawnMonster("grunt", org + '40 40 0', temp_angles);
- SpawnMonster("grunt", org + '-40 40 0', temp_angles);
- }
- else
- {
- SpawnMonster("grunt", org + '-40 0 0', temp_angles);
- SpawnMonster("grunt", org + '40 0 0', temp_angles);
- }
- }
- else if (name == "2 grunts, 1 dog")
- {
- SpawnMonster("dog", org + '44 0 0', temp_angles);
- SpawnMonster("grunt", org + '-40 -40 0', temp_angles);
- SpawnMonster("grunt", org + '-40 40 0', temp_angles);
- }
- else if (name == "2 dogs")
- {
- if (skill > 0)
- {
- SpawnMonster("dog", org + '-0 -44 0', temp_angles);
- SpawnMonster("dog", org + '0 44 0', temp_angles);
- }
- else
- SpawnMonster("dog", org + '-0 -44 0', temp_angles);
- }
- else if (name == "1 enforcer")
- {
- SpawnMonster("enforcer", org + '0 0 0', temp_angles);
- }
- else if (name == "2 enforcers")
- {
- if (skill > 0)
- {
- SpawnMonster("enforcer", org + '40 0 0', temp_angles);
- SpawnMonster("enforcer", org + '-40 0 0', temp_angles);
- }
- else
- SpawnMonster("enforcer", org + '0 0 0', temp_angles);
- }
- else if (name == "1 ogre")
- {
- SpawnMonster("ogre", org + '0 0 0', temp_angles);
- }
- else if (name == "2 knights")
- {
- if (skill > 0)
- {
- SpawnMonster("knight", org + '40 0 0', temp_angles);
- SpawnMonster("knight", org + '-40 0 0', temp_angles);
- }
- else
- {
- SpawnMonster("knight", org + '40 0 0', temp_angles);
- }
-
- }
- else if (name == "2 zombies")
- {
- SpawnMonster("zombie", org + '40 0 0', temp_angles);
- SpawnMonster("zombie", org + '-40 0 0', temp_angles);
- }
- else if (name == "1 wizard")
- SpawnMonster("wizard", org + '0 0 0', temp_angles);
- else if (name == "2 hellknights")
- {
- if (skill > 0)
- {
- SpawnMonster("hellknight", org + '0 40 0', temp_angles);
- SpawnMonster("hellknight", org + '0 -40 0', temp_angles);
- }
- else
- SpawnMonster("hellknight", org + '0 40 0', temp_angles);
- }
- else if (name == "2 knights, 1 hellknight")
- {
- SpawnMonster("hellknight", org + '40 0 0', temp_angles);
- SpawnMonster("knight", org + '-40 40 0', temp_angles);
- SpawnMonster("knight", org + '-40 -40 0', temp_angles);
- }
- else if (name == "3 wizards")
- {
- if (skill > 0)
- {
- SpawnMonster("wizard", org + '40 40 40', temp_angles);
- SpawnMonster("wizard", org + '-40 40 40', temp_angles);
- SpawnMonster("wizard", org + '-40 -40 40', temp_angles);
- }
- else
- {
- SpawnMonster("wizard", org + '40 40 40', temp_angles);
- SpawnMonster("wizard", org + '-40 40 40', temp_angles);
- }
- }
- else if (name == "shambler")
- SpawnMonster("shambler", org, temp_angles);
- else if (name == "double demon")
- {
- if (skill >= 3 && random() > 0.8)
- {
- // double shambler
- SpawnMonster("shambler", org + '40 40 0', temp_angles);
- SpawnMonster("shambler", org + '-40 -40 0', temp_angles);
- }
- else if (skill >= 1)
- {
- SpawnMonster("demon", org + '40 40 0', temp_angles);
- SpawnMonster("demon", org + '-40 -40 0', temp_angles);
- }
- else
- SpawnMonster("demon", org, temp_angles);
- }
- else if (name == "shalrath")
- SpawnMonster("shalrath", org, temp_angles);
- else
- dprint("ERROR: unrecognized squad name!\n");
-
- spawn_tfog(org);
- };
- /*
- HordeFindSpawnpoint
- Yoder February 2nd 2022
- */
- float HORDE_SQUAD_TYPE_NORMAL = 0;
- float HORDE_SQUAD_TYPE_RANGED = 1;
- float HORDE_SQUAD_TYPE_FLYING = 2;
- float HORDE_SQUAD_TYPE_BOSS = 3;
- float HORDE_SQUAD_CAT_ERROR = -1;
- float HORDE_SQUAD_CAT_FODDER = 0;
- float HORDE_SQUAD_CAT_ELITE = 1;
- float HORDE_SQUAD_CAT_BOSS = 2;
- entity(float squad_type)HordeFindSpawnpoint =
- {
- local float spawnpoint, temp_spawncount, loopcount, randomspawn;
- local float temp_spawncount_valid; // how many of the spawn points are valid
- local entity t;
- spawnpoint = 0;
- loopcount = 0;
- string squad_class;
-
- if (squad_type == HORDE_SQUAD_TYPE_BOSS)
- squad_class = "info_monster_start_boss";
- else if (squad_type == HORDE_SQUAD_TYPE_FLYING)
- squad_class = "info_monster_start_flying";
- else if (squad_type == HORDE_SQUAD_TYPE_RANGED)
- squad_class = "info_monster_start_ranged";
- else
- squad_class = "info_monster_start";
-
- // STEP 1: count all spawns of spawn type
- temp_spawncount = 0;
- temp_spawncount_valid = 0;
- // count the spawnpoints for squad_class
- t = find(world, classname, squad_class);
- while (t)
- {
- temp_spawncount++;
- if ((time > t.wait) && (!(t.spawnflags & START_OFF)) &&
- (!CheckBlockedSpawn(t)))
- temp_spawncount_valid++;
- t = find(t, classname, squad_class);
- }
-
- // verify against 0 valid spawns
- if (temp_spawncount_valid <= 0)
- {
- if (squad_type == HORDE_SQUAD_TYPE_NORMAL)
- {
- dprint("HordeFindSpawnPoint: FOUND 0 Valid spawns\n");
- return world; // already on fallback option, oops
- }
- else
- {
- squad_type = HORDE_SQUAD_TYPE_NORMAL;
- squad_class = "info_monster_start";
- t = world;
- loopcount = 1;
- }
- }
-
- // count again, but for Normal spawn points?
- if (loopcount)
- {
- temp_spawncount = 0;
- temp_spawncount_valid = 0;
-
- t = find(world, classname, squad_class);
- while (t)
- {
- temp_spawncount++;
- if ((time > t.wait) && (!(t.spawnflags & START_OFF)) &&
- (!CheckBlockedSpawn(t)))
- temp_spawncount_valid++;
- t = find(t, classname, squad_class);
- }
- }
-
- // verify against 0 valid spawns
- if (temp_spawncount_valid <= 0)
- {
- dprint("HordeFindSpawnPoint: FOUND 0 Valid spawns after recheck\n");
- return world; // already on fallback option, oops
- }
-
- // pick random spawncount
- loopcount = 0;
- randomspawn = temp_spawncount * random();
- dprint("randomspawn: ");dprint(ftos(randomspawn));
- dprint(" | whole count: ");dprint(ftos(temp_spawncount));
- dprint(" | valid count: ");dprint(ftos(temp_spawncount_valid));dprint("\n");
- t = find (world, classname, squad_class);
- while(t)
- {
- spawnpoint++;
- if (spawnpoint >= randomspawn)
- {
- if (!(t.spawnflags & START_OFF) && (time > t.wait) && (!(CheckBlockedSpawn(t))))
- {
- dprint("picked spawnpoint: ");dprint(ftos(spawnpoint));dprint("\n");
- return t;
- }
- }
- t = find(t, classname, squad_class);
-
- if (!t)
- {
- if (loopcount)
- dprint("something has gone horribly wrong!\n");
- else
- {
- loopcount = 1;
-
- t = find(t, classname, squad_class);
- spawnpoint = 0;
- randomspawn = 0; // allow any valid spawn
- }
- }
- }
- return world;
- };
- /*
- SpawnWave2
- Instead of finding a spawn point and then spawning a squad,
- decide a squad, and then find a spawn point.
- Yoder February 2nd 2022
- */
- void() SpawnWave2 =
- {
- local entity t, oself; // t is the temp spawn, oself is for temp storage of self
- local float squad_type;
- local string squad_name; // specific squad name
- local float r; // random roll
- local float squad_cat; //
-
- //STEP 1: determine what kind of monster is getting spawned
- if (self.army)
- {
-
- if (self.fodder > 0)
- {
- r = random() * 4;
- if (r < 1)
- squad_name = "3 grunts";
- else if (r < 2)
- squad_name = "2 grunts, 1 dog";
- else if (r < 3.5)
- squad_name = "2 dogs";
- else
- {
- squad_name = "1 enforcer";
- squad_type = HORDE_SQUAD_TYPE_RANGED;
- }
- squad_cat = HORDE_SQUAD_CAT_FODDER;
- }
- else if (self.elites > 0)
- {
- squad_type = HORDE_SQUAD_TYPE_RANGED;
- r = random() * 2;
- if (r < 1.5)
- squad_name = "2 enforcers";
- else
- squad_name = "1 ogre";
-
- squad_cat = HORDE_SQUAD_CAT_ELITE;
- }
- else if (self.bosses > 0)
- {
- dprint("ERROR: should not spawn boss squad in army wave\n");
- self.bosses = 0;
- squad_cat = HORDE_SQUAD_CAT_ERROR;
- }
- }
- else // NON-ARMY
- {
- if (self.fodder > 0)
- {
- r = random() * 4;
- if (r < 2)
- squad_name = "2 knights";
- else if (r < 3)
- squad_name = "2 zombies";
- else
- {
- squad_name = "1 wizard";
- squad_type = HORDE_SQUAD_TYPE_FLYING;
- }
-
- squad_cat = HORDE_SQUAD_CAT_FODDER;
- }
- else if (self.elites > 0)
- {
- r = random() * 4;
- if (r < 1)
- squad_name = "2 hellknights";
- else if (r < 2)
- squad_name = "2 knights, 1 hellknight";
- else if (r < 3)
- {
- squad_name = "1 ogre";
- squad_type = HORDE_SQUAD_TYPE_RANGED;
- }
- else
- {
- squad_name = "3 wizards";
- squad_type = HORDE_SQUAD_TYPE_FLYING;
- }
-
- squad_cat = HORDE_SQUAD_CAT_ELITE;
- }
- else if (self.bosses > 0)
- {
- squad_type = HORDE_SQUAD_TYPE_BOSS;
- r = random() * 3;
- if (r < 1)
- squad_name = "shambler";
- else if (r < 2.5)
- {
- squad_name = "double demon";
- squad_type = HORDE_SQUAD_TYPE_NORMAL;
- }
- else
- squad_name = "shalrath";
-
- squad_cat = HORDE_SQUAD_CAT_BOSS;
- }
- }
-
-
- //STEP 2: find a spawn point for the squad
- t = HordeFindSpawnpoint(squad_type);
- dprint("spawnpoint found was: ");dprint(t.classname);dprint("\n");
- if (t && (squad_cat != HORDE_SQUAD_CAT_ERROR))
- {
- t.wait = time + SPAWN_RESET_TIME; // block spawnpoint from reuse for a duration
-
- // use targets
- oself = self;
- self = t;
- SUB_UseTargets();
- self = oself;
-
- // set angles, just in case
- if (!t.angles)
- t.angles = '0 0 0';
-
- SpawnSquad2(squad_name, t.origin, t.angles);
- WriteByte(MSG_ALL, SVC_UPDATESTAT);
- WriteByte(MSG_ALL, 12); // 12 = STAT_TOTALMONSTERS
- WriteLong(MSG_ALL, total_monsters);
-
- if (squad_cat == HORDE_SQUAD_CAT_FODDER)
- self.fodder--;
- else if (squad_cat == HORDE_SQUAD_CAT_ELITE)
- self.elites--;
- else if (squad_cat == HORDE_SQUAD_CAT_BOSS)
- self.bosses--;
-
- if ((self.fodder + self.elites + self.bosses) <= 0) // max spawns hit
- {
- dprint("wave spawn completed!\n");
- self.wait = FALSE;
- self.think = Wavecheck;
- self.nextthink = time + 30;
- }
- else
- {
- // normal spawnwave wait time
- self.think = SpawnWave2;
- self.nextthink = time + 1 + random() + 1;
- }
- }
- else
- {
- dprint("no valid spawns, wait a moment\n");
- self.think = SpawnWave2;
- self.nextthink = time + 1;
- }
- };
- /* SpawnWavePrep
- called once at the countdown starts
- determines the number of squads by type
- also spawns ammo and items
- */
- float() SpawnWavePrep =
- {
- local entity p;
- local float playercount, playerscalar;
- // Reset Key Spawn
- key_spawned = FALSE;
-
- // Respawn dead players!
- if (HordeGetPlayersAlive() < 1)
- {
- dprint("WARNING: Last player died at round end, don't start new round!\n");
- return 0;
- }
-
- // Items
- p = find (world, classname, "info_horde_item");
- while (p)
- {
- if (!p.wait)
- {
- p.think = SpawnItem;
- p.nextthink = time + random() * 2;
- }
- p = find (p, classname, "info_horde_item");
- }
- self.wave++;
- dprint("wave ");
- dprint(ftos(self.wave));
- dprint("\n");
-
- // See spreadsheet for notes on squad scaling
-
- // AY FEB07 scaling start wave based on difficulty
- local float temp_wave;
- if (skill >= 3)
- temp_wave = self.wave + 6;
- else if (skill >= 2)
- temp_wave = self.wave + 3;
- else
- temp_wave = self.wave;
-
- // Determine if Army wave
- if ((temp_wave + 2) % 3 == 0)
- self.army = TRUE;
- else
- self.army = FALSE;
-
- // player scalar
- playercount = HordeGetPlayersAlive();
- if (playercount >= 4)
- playerscalar = 2;
- else if (playercount >= 3)
- playerscalar = 1.5;
- else if (playercount >= 2)
- playerscalar = 1.25;
- else
- playerscalar = 1;
-
- // Boss Count
- if (temp_wave % 3 == 0)
- {
- self.bosses = floor((temp_wave+1)/4);
- }
- else if ((skill > 1) && (!self.army) && (temp_wave > 9))
- {
- //AY feb07, some extra spice
- self.bosses = floor((temp_wave+1)/8);
- }
-
- // Elite Count
- self.elites = ceil((temp_wave - 1)/3) - floor(self.bosses/2);
- self.elites = floor(self.elites * playerscalar);
-
- // Fodder Count
- self.fodder = (temp_wave + 2) - (self.bosses * 2 + self.elites);
- self.fodder = floor(self.fodder * playerscalar);
-
- // Trigger map entities
- local entity stemp;
- p = find (world, targetname, self.target);
- while(p)
- {
- stemp = self;
- self = p;
- if (self.use != SUB_Null)
- {
- if (self.use)
- self.use ();
- }
- self = stemp;
- p = find (p, targetname, self.target);
- }
- self.wait = TRUE;
- return 1;
- }
- void() Countdown4 =
- {
- if (self.wave % 3 == 0)
- centerprint_all("$qc_horde_boss_wave");
- else
- centerprint_all("$qc_horde_fight");
- SpawnWave2();
- sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
- };
- void() Countdown3 =
- {
- centerprint_all("1");
- self.think = Countdown4;
- self.nextthink = time + 1;
- sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
- };
- void() Countdown2 =
- {
- centerprint_all("2");
- self.think = Countdown3;
- self.nextthink = time + 1;
- sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
- };
- void() Countdown =
- {
- if (!SpawnWavePrep())
- return;
-
- centerprint_all("3");
- self.think = Countdown2;
- self.nextthink = time + 1;
-
- sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
- };
- void() RespawnAllPlayers =
- {
- local entity p, oself;
- p = find (world, classname, "player");
- while (p)
- {
- if (p.deadflag > 0)
- {
- oself = self;
- self = p;
- horde_respawn_teammate();
- self = oself;
- //p.think = horde_respawn_teammate;
- //p.nextthink = time; // next frame
- }
- p = find (p, classname, "player");
- }
- };
- /*
- When each enemy spawned by the horde manager dies, they trigger self.owner.use, which is wavecheck.
- Wavecheck sees if all spawned monsters are dead, so it can determine if it needs to start a new wave.
- */
- void() Wavecheck =
- {
- if (self.wait) // still in the process of spawning monsters, don't wavecheck
- return;
-
- self.think = Wavecheck;
- self.nextthink = time + 10;
-
- dprint("\n===============\n");
- dprint("checking kills: ");
- dprint(ftos(killed_monsters));
- dprint(" of ");
- dprint(ftos(total_monsters));
- dprint("\n");
-
- // debug checking last monster position
- if (killed_monsters + 3 >= total_monsters)
- {
- local entity t;
- t = find(world, category, CATEGORY_MONSTER);
- while(t)
- {
- if (t.health > 0)
- {
- dprint("Position of monster: ");
- dprint(t.classname);
- dprint(" : ");
- dprint(vtos(t.origin));
- dprint("\n");
- }
- else
- t.category = string_null;
- t = find(t, category, CATEGORY_MONSTER);
- }
- }
-
- // Early exit for kill count
- if ((self.wave % 3 == 0) || (self.wave < 3)) // Be exact?
- {
- dprint("check monsters killed as boss wave\n");
- if (HordeGetMonstersAlive() > 0) // testing the new way
- return;
- //if (killed_monsters < total_monsters)
- // return;
- }
- else
- {
- dprint("check monsters killed as any other wave\n");
- if (HordeGetMonstersAlive() > 5)
- return;
- //if (killed_monsters < (total_monsters -5))
- // return;
- }
-
- // Made it this far, means wave completed successfully
- dprint("wavecheck looks good! Respawning players\n");
- RespawnAllPlayers();
-
- self.wait = 1;
- self.think = Countdown;
-
- if (self.wave % 3 == 0) // it was a boss wave
- {
- if(!key_spawned) // key was already spawned, don't spawn another yet!
- GetKey();
- self.nextthink = time + 20; // in case the players are slow to find the key
- }
- else
- {
- self.nextthink = time;
- }
- dprint("wavecheck now completed.\n");
- };
- // setup horde rules
- void() SetHorde =
- {
- dprint("TEST: It is September 23, 2021!\n"); // yoder sanity test
- self.think = Countdown;
- self.nextthink = time + 1;
- }
- // define entity
- void() horde_manager =
- {
- // AY, Dec06 2021, auto set horde cvar
- if (!cvar("horde") && !deathmatch)
- cvar_set("horde", "1");
- // end AY
-
- PrecacheMonsters();
-
- horde_ent = self; // set global reference to the horde manager
-
- if (!self.target)
- self.target = "horde_event"; // default name
- self.targetname = "horde_manager";
- self.think = SetHorde;
- self.nextthink = time + HORDE_START_DELAY;
- self.use = Wavecheck;
-
- self.wait = 1; // waiting before next wave?
- self.delay = 9; // max number of squads to spawn in a wave
- self.wave = 0;
-
- //if (!horde)
- // horde = 1; // in case CVAR isn't already set
-
- };
- void() PowerupFade =
- {
- if (self.alpha > 0)
- {
- self.alpha = self.alpha - 0.25 * frametime;
- self.nextthink = time; // think next frame
- }
- else
- remove(self);
- }
- void() horde_powerup_think =
- {
- if (self.velocity_z < 0)
- {
- dprint("WARNING: Powerup fell out of world. Remove\n");
- remove(self);
- }
- else
- {
- self.alpha = 1;
- self.think = PowerupFade;
- self.nextthink = time;
- }
- };
- void() horde_spawn_powerup =
- {
- if (!powerup_chance)
- powerup_chance = DEFAULT_POWERUP_CHANCE;
-
- if (random() < powerup_chance) // "if(1)" to guarantee powerup drop
- {
- dprint("powerup chance was: ");
- dprint(ftos(powerup_chance));
- dprint("\n");
-
- powerup_chance = DEFAULT_POWERUP_CHANCE;
-
- local entity powerup = spawn();
- setorigin (powerup, self.origin + '0 0 0');
- //setsize (powerup, '-16 -16 -24', '16 16 32');
- powerup.flags = FL_ITEM;
- powerup.solid = SOLID_TRIGGER;
- powerup.movetype = MOVETYPE_BOUNCE;
- powerup.velocity = '0 0 300';
-
- //powerup.velocity_x = crandom() * 64;
- //powerup.velocity_y = crandom() * 64;
- powerup.touch = powerup_touch;
- powerup.think = horde_powerup_think;
- powerup.nextthink = time + 10;
-
- if (random() < 0.25) // spawn pentagram (invulnerability)
- {
- powerup.noise = "items/protect.wav";
- setmodel(powerup, "progs/invulner.mdl");
- powerup.netname = "$qc_pentagram_of_protection";
- powerup.items = IT_INVULNERABILITY;
- powerup.classname = "item_artifact_invulnerability";
- }
- else // spawn quad damage
- {
- powerup.noise = "items/damage.wav";
- setmodel(powerup, "progs/quaddama.mdl");
- powerup.netname = "$qc_quad_damage";
- powerup.items = IT_QUAD;
- powerup.classname = "item_artifact_super_damage";
- }
- //powerup.effects = EF_DIMLIGHT;
-
- setsize (powerup, '-12 -12 -12', '12 12 12');
- }
- else
- powerup_chance = powerup_chance + POWERUP_CHANCE_GAIN;
- };
- // remote wavecheck
- // this is to trigger a wavecheck from outside of horde mode
- void() remote_wavecheck =
- {
- if(!horde_ent || (intermission_running))
- {
- dprint("no wavecheck.");
- if (!horde_ent)
- dprint(" no horde ent found.");
- if (intermission_running)
- dprint(" intermission running.");
- dprint("\n\n");
- return;
- }
- dprint("remote wavecheck from: ");
- dprint(self.classname);
- dprint("\n");
- local entity stemp = self; // the entity that is triggering the remote wavecheck
- self = horde_ent;
- self.use();
-
- self = stemp; // set self back to whatever it was
- };
- /* CheckBlockedSpawn
- AY Feb24, 2022
- Checks the horde spawn against all living players
- Returns true/false if spawn is blocked.
- */
- float(entity spawnpoint) CheckBlockedSpawn =
- {
- local float blocked = FALSE;
- local entity p;
- local vector p_mins, p_maxs, s_mins, s_maxs;
-
- if (spawnpoint.spawnflags & SKIP_BLOCK_CHECK)
- return FALSE;
-
- p = find(world, classname, "player");
- while(p && !blocked)
- {
- if ((p.health > 0) && (p.deadflag <= 0))
- {
- p_mins = p.origin + p.mins;
- p_maxs = p.origin + p.maxs;
- s_mins = spawnpoint.origin + spawnpoint.mins;
- s_maxs = spawnpoint.origin + spawnpoint.maxs;
-
- if (((p_maxs_x > s_mins_x) && (p_mins_x < s_maxs_x)) &&
- ((p_maxs_y > s_mins_y) && (p_mins_y < s_maxs_y)) &&
- ((p_maxs_y > s_mins_y) && (p_mins_y < s_maxs_y)))
- {
- dprint("player blocking spawn\n");
- blocked = TRUE;
- }
- }
- p = find(p, classname, "player");
- }
- return blocked;
- };
|