horde.qc 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074
  1. /* Copyright (C) 1996-2022 id Software LLC
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  13. See file, 'COPYING', for details.
  14. */
  15. .float storednextthink;
  16. .void() storedthink;
  17. void() Countdown;
  18. void() Wavecheck;
  19. void() SpawnAmmo;
  20. .float wave; // what wave of the horde mode we're on
  21. .float fodder; // how many fodder squads to spawn
  22. .float elites; // how many elite squads to spawn
  23. .float bosses; // how many bosses to spawn
  24. .float spawncount; // number of monster spawns
  25. .float army; // Spawn Army? True/fallse
  26. float(entity spawnpoint) CheckBlockedSpawn;
  27. float key_spawned; // TRUE/FALSE, determine if key has already been spawned, prevent dupes.
  28. float SPAWN_RESET_TIME = 5; // amount of time that a spawn point is considered occupied and not valid for use
  29. /*
  30. ================
  31. HordeGetPlayersAlive
  32. added Aug31 2021
  33. Returns a float for the number of living players
  34. ================
  35. */
  36. float() HordeGetPlayersAlive =
  37. {
  38. local float playercount;
  39. local entity e;
  40. // count entities
  41. e = find(world, classname, "player");
  42. while(e)
  43. {
  44. if (e.health > 0)
  45. {
  46. if (e.flags & FL_CLIENT)
  47. playercount++;
  48. else
  49. dprint("Player is no longer a client!\n");
  50. }
  51. e = find(e, classname, "player");
  52. }
  53. dprint("there are ");
  54. dprint(ftos(playercount));
  55. dprint(" players alive.\n");
  56. return playercount;
  57. };
  58. /*
  59. ================
  60. HordeGetMonstersAlive
  61. added Oct28 2021
  62. Manually counts all living monsters
  63. ================
  64. */
  65. float() HordeGetMonstersAlive =
  66. {
  67. local float monstercount;
  68. local entity e;
  69. // count monster entities
  70. e = find(world, category, "monster");
  71. while(e)
  72. {
  73. if ((e.health > 0) && (e.classname != "monster_zombie"))
  74. monstercount++;
  75. e = find(e, category, "monster");
  76. }
  77. dprint("verification count. There are ");
  78. dprint(ftos(monstercount));
  79. dprint(" monsters alive.\n");
  80. return monstercount;
  81. };
  82. /*
  83. ================
  84. HordeFindTarget
  85. added Aug31 2021
  86. Returns a random living player
  87. ================
  88. */
  89. entity() HordeFindTarget =
  90. {
  91. local float playercount = HordeGetPlayersAlive();
  92. if (playercount == 0)
  93. return world;
  94. local entity e;
  95. local float rcount; // random value [0,count]
  96. rcount = random() * playercount;
  97. playercount = 0; // reset for new count
  98. e = find(world, classname, "player");
  99. while(e)
  100. {
  101. if (e.health > 0)
  102. playercount++;
  103. if ((rcount <= playercount) && !(e.flags & FL_NOTARGET))
  104. return e;
  105. else
  106. e = find(e, classname, "player");
  107. }
  108. return world;
  109. };
  110. /*QUAKED info_monster_start START_OFF
  111. if targeted, it will toggle between on or off, like lights
  112. */
  113. float SKIP_BLOCK_CHECK = 2;
  114. void() monster_start_use =
  115. {
  116. if (self.spawnflags & START_OFF)
  117. self.spawnflags = self.spawnflags - START_OFF;
  118. else
  119. self.spawnflags = self.spawnflags + START_OFF;
  120. };
  121. void() info_monster_start =
  122. {
  123. self.wait = FALSE; // used for checking if this is a valid spawn
  124. self.use = monster_start_use;
  125. setorigin (self, self.origin);
  126. setsize(self, '-80 -80 0', '80 80 128');
  127. };
  128. void() info_monster_start_ranged = // used for ogres and enforcers
  129. {
  130. self.wait = FALSE; // used for checking if this is a valid spawn
  131. self.use = monster_start_use;
  132. setorigin (self, self.origin);
  133. setsize(self, '-44 -44 0', '44 44 128');
  134. };
  135. void() info_monster_start_flying = // used exclusively for scrags (wizards)
  136. {
  137. self.wait = FALSE; // used for checking if this is a valid spawn
  138. self.use = monster_start_use;
  139. setorigin (self, self.origin);
  140. setsize(self, '-80 -80 0', '80 80 128');
  141. };
  142. void() info_monster_start_boss = // used for shalraths and shamblers
  143. {
  144. self.wait = FALSE; // used for checking if this is a valid spawn
  145. self.use = monster_start_use;
  146. setorigin (self, self.origin);
  147. setsize(self, '-44 -44 0', '44 44 128');
  148. };
  149. // ENTITY
  150. void() info_horde_ammo =
  151. {
  152. //self.wait = FALSE;
  153. self.think = SpawnAmmo;
  154. self.nextthink = time + HORDE_START_DELAY + random() * 3;
  155. };
  156. // ENTITY
  157. void() info_horde_item =
  158. {
  159. self.wait = FALSE;
  160. };
  161. float KEY_FIRST = 1;
  162. float KEY_SECOND = 2;
  163. float KEY_THIRD = 4;
  164. float KEY_FOURTH_PLUS = 8;
  165. // ENTITY
  166. void() info_horde_key =
  167. {
  168. self.wait = FALSE;
  169. };
  170. //============================================================================
  171. void() PrecacheMonsters =
  172. {
  173. // knight
  174. precache_model ("progs/knight.mdl");
  175. precache_model ("progs/h_knight.mdl");
  176. precache_sound ("knight/kdeath.wav");
  177. precache_sound ("knight/khurt.wav");
  178. precache_sound ("knight/ksight.wav");
  179. precache_sound ("knight/sword1.wav");
  180. precache_sound ("knight/sword2.wav");
  181. precache_sound ("knight/idle.wav");
  182. // hellknight
  183. precache_model2 ("progs/hknight.mdl");
  184. precache_model2 ("progs/k_spike.mdl");
  185. precache_model2 ("progs/h_hellkn.mdl");
  186. precache_sound2 ("hknight/attack1.wav");
  187. precache_sound2 ("hknight/death1.wav");
  188. precache_sound2 ("hknight/pain1.wav");
  189. precache_sound2 ("hknight/sight1.wav");
  190. precache_sound ("hknight/hit.wav"); // used by C code, so don't sound2
  191. precache_sound2 ("hknight/slash1.wav");
  192. precache_sound2 ("hknight/idle.wav");
  193. precache_sound2 ("hknight/grunt.wav");
  194. precache_sound ("knight/sword1.wav");
  195. precache_sound ("knight/sword2.wav");
  196. // dog
  197. precache_model ("progs/h_dog.mdl");
  198. precache_model ("progs/dog.mdl");
  199. precache_sound ("dog/dattack1.wav");
  200. precache_sound ("dog/ddeath.wav");
  201. precache_sound ("dog/dpain1.wav");
  202. precache_sound ("dog/dsight.wav");
  203. precache_sound ("dog/idle.wav");
  204. // demon
  205. precache_model ("progs/demon.mdl");
  206. precache_model ("progs/h_demon.mdl");
  207. precache_sound ("demon/ddeath.wav");
  208. precache_sound ("demon/dhit2.wav");
  209. precache_sound ("demon/djump.wav");
  210. precache_sound ("demon/dpain1.wav");
  211. precache_sound ("demon/idle1.wav");
  212. precache_sound ("demon/sight2.wav");
  213. // ogre
  214. precache_model ("progs/ogre.mdl");
  215. precache_model ("progs/h_ogre.mdl");
  216. precache_model ("progs/grenade.mdl");
  217. precache_sound ("ogre/ogdrag.wav");
  218. precache_sound ("ogre/ogdth.wav");
  219. precache_sound ("ogre/ogidle.wav");
  220. precache_sound ("ogre/ogidle2.wav");
  221. precache_sound ("ogre/ogpain1.wav");
  222. precache_sound ("ogre/ogsawatk.wav");
  223. precache_sound ("ogre/ogwake.wav");
  224. // grunt
  225. precache_model ("progs/soldier.mdl");
  226. precache_model ("progs/h_guard.mdl");
  227. precache_model ("progs/gib1.mdl");
  228. precache_model ("progs/gib2.mdl");
  229. precache_model ("progs/gib3.mdl");
  230. precache_sound ("soldier/death1.wav");
  231. precache_sound ("soldier/idle.wav");
  232. precache_sound ("soldier/pain1.wav");
  233. precache_sound ("soldier/pain2.wav");
  234. precache_sound ("soldier/sattck1.wav");
  235. precache_sound ("soldier/sight1.wav");
  236. precache_sound ("player/udeath.wav"); // gib death
  237. // enforcer
  238. precache_model2 ("progs/enforcer.mdl");
  239. precache_model2 ("progs/h_mega.mdl");
  240. precache_model2 ("progs/laser.mdl");
  241. precache_sound2 ("enforcer/death1.wav");
  242. precache_sound2 ("enforcer/enfire.wav");
  243. precache_sound2 ("enforcer/enfstop.wav");
  244. precache_sound2 ("enforcer/idle1.wav");
  245. precache_sound2 ("enforcer/pain1.wav");
  246. precache_sound2 ("enforcer/pain2.wav");
  247. precache_sound2 ("enforcer/sight1.wav");
  248. precache_sound2 ("enforcer/sight2.wav");
  249. precache_sound2 ("enforcer/sight3.wav");
  250. precache_sound2 ("enforcer/sight4.wav");
  251. // shambler
  252. precache_model ("progs/shambler.mdl");
  253. precache_model ("progs/s_light.mdl");
  254. precache_model ("progs/h_shams.mdl");
  255. precache_model ("progs/bolt.mdl");
  256. precache_sound ("shambler/sattck1.wav");
  257. precache_sound ("shambler/sboom.wav");
  258. precache_sound ("shambler/sdeath.wav");
  259. precache_sound ("shambler/shurt2.wav");
  260. precache_sound ("shambler/sidle.wav");
  261. precache_sound ("shambler/ssight.wav");
  262. precache_sound ("shambler/melee1.wav");
  263. precache_sound ("shambler/melee2.wav");
  264. precache_sound ("shambler/smack.wav");
  265. // shalrath
  266. precache_model2 ("progs/shalrath.mdl");
  267. precache_model2 ("progs/h_shal.mdl");
  268. precache_model2 ("progs/v_spike.mdl");
  269. precache_sound2 ("shalrath/attack.wav");
  270. precache_sound2 ("shalrath/attack2.wav");
  271. precache_sound2 ("shalrath/death.wav");
  272. precache_sound2 ("shalrath/idle.wav");
  273. precache_sound2 ("shalrath/pain.wav");
  274. precache_sound2 ("shalrath/sight.wav");
  275. // wizard
  276. precache_model ("progs/wizard.mdl");
  277. precache_model ("progs/h_wizard.mdl");
  278. precache_model ("progs/w_spike.mdl");
  279. precache_sound ("wizard/hit.wav"); // used by c code
  280. precache_sound ("wizard/wattack.wav");
  281. precache_sound ("wizard/wdeath.wav");
  282. precache_sound ("wizard/widle1.wav");
  283. precache_sound ("wizard/widle2.wav");
  284. precache_sound ("wizard/wpain.wav");
  285. precache_sound ("wizard/wsight.wav");
  286. // zombies
  287. precache_model ("progs/zombie.mdl");
  288. precache_model ("progs/h_zombie.mdl");
  289. precache_model ("progs/zom_gib.mdl");
  290. precache_sound ("zombie/z_idle.wav");
  291. precache_sound ("zombie/z_idle1.wav");
  292. precache_sound ("zombie/z_shot1.wav");
  293. precache_sound ("zombie/z_gib.wav");
  294. precache_sound ("zombie/z_pain.wav");
  295. precache_sound ("zombie/z_pain1.wav");
  296. precache_sound ("zombie/z_fall.wav");
  297. precache_sound ("zombie/z_miss.wav");
  298. precache_sound ("zombie/z_hit.wav");
  299. precache_sound ("zombie/idle_w2.wav");
  300. // ammo
  301. precache_model ("maps/b_shell1.bsp");
  302. precache_model ("maps/b_shell0.bsp");
  303. precache_model ("maps/b_nail1.bsp");
  304. precache_model ("maps/b_nail0.bsp");
  305. precache_model ("maps/b_rock1.bsp");
  306. precache_model ("maps/b_rock0.bsp");
  307. precache_model ("maps/b_batt1.bsp");
  308. precache_model ("maps/b_batt0.bsp");
  309. // items
  310. precache_model("maps/b_bh10.bsp"); // rotten health
  311. precache_sound("items/r_item1.wav");
  312. precache_model("maps/b_bh25.bsp"); // regular health
  313. precache_sound("items/health1.wav");
  314. precache_model ("progs/armor.mdl"); // regular armor
  315. // quad damage
  316. precache_model ("progs/quaddama.mdl");
  317. precache_sound ("items/damage.wav");
  318. precache_sound ("items/damage2.wav");
  319. precache_sound ("items/damage3.wav");
  320. // pentagram (invulnerability)
  321. precache_model ("progs/invulner.mdl");
  322. precache_sound ("items/protect.wav");
  323. precache_sound ("items/protect2.wav");
  324. precache_sound ("items/protect3.wav");
  325. // new horde sounds
  326. precache_sound("horde/nyom.wav");
  327. // precache keys
  328. if (world.worldtype == WORLDTYPE_MEDIEVAL || world.worldtype == WORLDTYPE_HUB)
  329. {
  330. precache_sound ("misc/medkey.wav");
  331. precache_model("progs/w_g_key.mdl"); // gold key
  332. precache_model("progs/w_s_key.mdl"); // silver key
  333. }
  334. if (world.worldtype == WORLDTYPE_METAL)
  335. {
  336. precache_sound ("misc/runekey.wav");
  337. precache_model ("progs/m_g_key.mdl");
  338. precache_model ("progs/m_s_key.mdl");
  339. }
  340. if (world.worldtype == WORLDTYPE_BASE)
  341. {
  342. precache_sound2 ("misc/basekey.wav");
  343. precache_model2 ("progs/b_g_key.mdl");
  344. precache_model2 ("progs/b_s_key.mdl");
  345. }
  346. };
  347. // =================================================================
  348. void() GibMonster =
  349. {
  350. T_Damage(self, world, world, 4000);
  351. };
  352. // =================================================================
  353. /*
  354. ================
  355. SpawnMonster
  356. ================
  357. */
  358. entity(string class, vector org, vector temp_angles) SpawnMonster =
  359. {
  360. local entity monster;
  361. monster = spawn();
  362. monster.angles = temp_angles;
  363. monster.solid = SOLID_SLIDEBOX;
  364. monster.movetype = MOVETYPE_STEP;
  365. //monster.target = "horde_manager";
  366. monster.flags = monster.flags | FL_MONSTER;
  367. //monster.alpha = 1; // only set monster alpha after they're a corpse
  368. local entity oself; // old self
  369. // perform enemy-type specific loading
  370. if (class == "knight")
  371. {
  372. setmodel (monster, "progs/knight.mdl");
  373. setsize (monster, '-16 -16 -24', '16 16 40');
  374. org = org + '0 0 24'; // offset based on size
  375. monster.classname = "monster_knight";
  376. monster.health = 75;
  377. monster.th_stand = knight_stand1;
  378. monster.th_walk = knight_walk1;
  379. monster.th_run = knight_run1;
  380. monster.th_melee = knight_atk1;
  381. monster.th_pain = knight_pain;
  382. monster.th_die = knight_die;
  383. }
  384. else if (class == "hellknight")
  385. {
  386. setmodel (monster, "progs/hknight.mdl");
  387. setsize (monster, '-16 -16 -24', '16 16 40');
  388. org = org + '0 0 24';
  389. monster.classname = "monster_hell_knight";
  390. monster.health = 250;
  391. monster.th_stand = hknight_stand1;
  392. monster.th_walk = hknight_walk1;
  393. monster.th_run = hknight_run1;
  394. monster.th_melee = hknight_melee;
  395. monster.th_missile = hknight_magicc1;
  396. monster.th_pain = hknight_pain;
  397. monster.th_die = hknight_die;
  398. }
  399. else if (class == "dog")
  400. {
  401. setmodel (monster, "progs/dog.mdl");
  402. setsize (monster, '-32 -32 -24', '32 32 40');
  403. org = org + '0 0 24';
  404. monster.classname = "monster_dog";
  405. monster.health = 25;
  406. monster.th_stand = dog_stand1;
  407. monster.th_walk = dog_walk1;
  408. monster.th_run = dog_run1;
  409. monster.th_pain = dog_pain;
  410. monster.th_die = dog_die;
  411. monster.th_melee = dog_atta1;
  412. monster.th_missile = dog_leap1;
  413. }
  414. else if (class == "demon")
  415. {
  416. setmodel (monster, "progs/demon.mdl");
  417. setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
  418. monster.classname = "monster_demon1";
  419. monster.health = 300;
  420. org = org + '0 0 48';
  421. monster.th_stand = demon1_stand1;
  422. monster.th_walk = demon1_walk1;
  423. monster.th_run = demon1_run1;
  424. monster.th_die = demon_die;
  425. monster.th_melee = Demon_MeleeAttack;
  426. monster.th_missile = demon1_jump1;
  427. monster.th_pain = demon1_pain;
  428. }
  429. else if (class == "ogre")
  430. {
  431. setmodel (monster, "progs/ogre.mdl");
  432. setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
  433. monster.classname = "monster_ogre";
  434. monster.health = 200;
  435. org = org + '0 0 32';
  436. monster.th_stand = ogre_stand1;
  437. monster.th_walk = ogre_walk1;
  438. monster.th_run = ogre_run1;
  439. monster.th_die = ogre_die;
  440. monster.th_melee = ogre_melee;
  441. monster.th_missile = ogre_nail1;
  442. monster.th_pain = ogre_pain;
  443. }
  444. else if (class == "grunt")
  445. {
  446. setmodel (monster, "progs/soldier.mdl");
  447. setsize (monster, '-16 -16 -24', '16 16 40');
  448. monster.classname = "monster_army";
  449. monster.health = 30;
  450. org = org + '0 0 24';
  451. monster.th_stand = army_stand1;
  452. monster.th_walk = army_walk1;
  453. monster.th_run = army_run1;
  454. monster.th_missile = army_atk1;
  455. monster.th_pain = army_pain;
  456. monster.th_die = army_die;
  457. }
  458. else if (class == "enforcer")
  459. {
  460. setmodel (monster, "progs/enforcer.mdl");
  461. setsize (monster, '-16 -16 -24', '16 16 40');
  462. monster.classname = "monster_enforcer";
  463. monster.health = 80;
  464. org = org + '0 0 24';
  465. monster.th_stand = enf_stand1;
  466. monster.th_walk = enf_walk1;
  467. monster.th_run = enf_run1;
  468. monster.th_pain = enf_pain;
  469. monster.th_die = enf_die;
  470. monster.th_missile = enf_atk1;
  471. }
  472. else if (class == "shambler")
  473. {
  474. setmodel (monster, "progs/shambler.mdl");
  475. setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
  476. monster.classname = "monster_shambler";
  477. monster.health = 600;
  478. org = org + '0 0 32';
  479. monster.th_stand = sham_stand1;
  480. monster.th_walk = sham_walk1;
  481. monster.th_run = sham_run1;
  482. monster.th_die = sham_die;
  483. monster.th_melee = sham_melee;
  484. monster.th_missile = sham_magic1;
  485. monster.th_pain = sham_pain;
  486. }
  487. else if (class == "shalrath")
  488. {
  489. setmodel (monster, "progs/shalrath.mdl");
  490. setsize (monster, VEC_HULL2_MIN, VEC_HULL2_MAX);
  491. monster.classname = "monster_shalrath";
  492. org = org + '0 0 32';
  493. monster.health = 400;
  494. monster.th_stand = shal_stand;
  495. monster.th_walk = shal_walk1;
  496. monster.th_run = shal_run1;
  497. monster.th_die = shalrath_die;
  498. monster.th_pain = shalrath_pain;
  499. monster.th_missile = shal_attack1;
  500. }
  501. else if (class == "wizard")
  502. {
  503. setmodel (monster, "progs/wizard.mdl");
  504. monster.flags = monster.flags | FL_FLY; // special behavior
  505. monster.classname = "monster_wizard";
  506. setsize (monster, VEC_HULL_MIN, VEC_HULL_MAX);
  507. org = org + '0 0 32';
  508. monster.health = 80;
  509. monster.th_stand = wiz_stand1;
  510. monster.th_walk = wiz_walk1;
  511. monster.th_run = wiz_run1;
  512. monster.th_missile = Wiz_Missile;
  513. monster.th_pain = Wiz_Pain;
  514. monster.th_die = wiz_die;
  515. }
  516. else if (class == "zombie")
  517. {
  518. setmodel (monster, "progs/zombie.mdl");
  519. setsize (monster, '-16 -16 -24', '16 16 40');
  520. monster.classname = "monster_zombie";
  521. org = org + '0 0 32';
  522. monster.health = 60;
  523. monster.th_stand = zombie_stand1;
  524. monster.th_walk = zombie_walk1;
  525. monster.th_run = zombie_run1;
  526. monster.th_pain = zombie_pain;
  527. monster.th_die = zombie_die;
  528. monster.th_missile = zombie_missile;
  529. monster.target = string_null; // don't target anything!
  530. }
  531. else
  532. {
  533. dprint("ERROR: No monster provided");
  534. }
  535. // finish the shared settings
  536. setorigin (monster, org);
  537. monster.origin_z = monster.origin_z + 1; // raise off floor a bit
  538. // temporarily swap self for next funcs
  539. oself = self;
  540. self = monster;
  541. if (class != "wizard")
  542. droptofloor();
  543. if (!walkmove(0,0))
  544. {
  545. dprint ("walkmonster in wall at: ");
  546. dprint (vtos(self.origin));
  547. dprint ("\n");
  548. }
  549. // resume normal self behavior
  550. self = oself;
  551. monster.category = CATEGORY_MONSTER;
  552. monster.takedamage = DAMAGE_AIM;
  553. monster.ideal_yaw = monster.angles * '0 1 0';
  554. if (!monster.yaw_speed)
  555. monster.yaw_speed = 20;
  556. monster.view_ofs = '0 0 25';
  557. monster.use = monster_use;
  558. monster.flags = monster.flags | FL_MONSTER;
  559. monster.enemy = HordeFindTarget(); // find a random living player
  560. //monster.enemy = find (world, classname, "player");
  561. monster.think = FoundTarget;
  562. //monster.nextthink = monster.nextthink + 0.1;
  563. monster.nextthink = time + 0.1;
  564. spawn_tdeath_fast (monster.origin, monster);
  565. //spawn_tfog(monster.origin);
  566. monster.owner = self;
  567. if (monster.classname != "monster_zombie") // don't count zombies toward total goal
  568. total_monsters = total_monsters + 1;
  569. return monster;
  570. };
  571. void() horde_ammo_touch =
  572. {
  573. local entity temp_player, t;
  574. local float best_weapon;
  575. // early exit touch if not a player or not alive
  576. if (other.classname != "player")
  577. return;
  578. if (other.health <= 0)
  579. return;
  580. // if the player was using their best weapon, change up to the new one if better
  581. t = self; // hang on to the ammo entity for a moment
  582. self = other;
  583. best_weapon = W_BestWeapon();
  584. self = t; // set self back to ammo entity
  585. // if player is at max ammo for ammo's type, return
  586. if (((self.weapon == 1) && (other.ammo_shells >= 100)) || // shotgun
  587. ((self.weapon == 2) && (other.ammo_nails >= 200)) || // nailgun
  588. ((self.weapon == 3) && (other.ammo_rockets >= 100)) || // rockets
  589. ((self.weapon == 4) && (other.ammo_cells >= 100))) // cells
  590. return;
  591. sprint(other, "$qc_got_item", self.netname);
  592. // ammo touch sound
  593. sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM);
  594. stuffcmd (other, "bf\n");
  595. // change to a better weapon if appropriate
  596. if ( other.weapon == best_weapon )
  597. {
  598. t = self;
  599. self = other;
  600. self.weapon = W_BestWeapon();
  601. W_SetCurrentAmmo ();
  602. self = t;
  603. }
  604. // loop through all players and give ammo
  605. temp_player = find(world, classname, "player");
  606. while(temp_player)
  607. {
  608. if (temp_player.health > 0) // only give ammo to living players
  609. {
  610. if (self.weapon == 1) // shotgun
  611. temp_player.ammo_shells += self.aflag;
  612. else if (self.weapon == 2) // spikes
  613. temp_player.ammo_nails += self.aflag;
  614. else if (self.weapon == 3) // rockets
  615. temp_player.ammo_rockets += self.aflag;
  616. else if (self.weapon == 4) // cells
  617. temp_player.ammo_cells += self.aflag;
  618. // temp swap and set bounds
  619. t = other;
  620. other = temp_player;
  621. bound_other_ammo();
  622. other = t;
  623. // temp swap again and set current ammo
  624. t = self;
  625. self = temp_player;
  626. W_SetCurrentAmmo();
  627. self = t;
  628. }
  629. temp_player = find(temp_player, classname, "player");
  630. }
  631. // remove
  632. self.model = string_null;
  633. self.solid = SOLID_NOT;
  634. // AY dec06 2021
  635. //self.owner.wait = FALSE; The Old Way
  636. self.owner.think = SpawnAmmo;
  637. self.owner.nextthink = time + HORDE_AMMO_RESPAWN_DELAY;
  638. // end AY
  639. remove(self);
  640. };
  641. void() horde_health_touch =
  642. {
  643. if (other.classname != "player")
  644. return;
  645. if (!T_Heal(other, self.healamount, 0))
  646. return;
  647. sprint(other, "$qc_item_health", ftos(self.healamount));
  648. // health touch sound
  649. sound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM);
  650. stuffcmd (other, "bf\n");
  651. self.model = string_null;
  652. self.solid = SOLID_NOT;
  653. self.owner.wait = FALSE;
  654. remove(self);
  655. };
  656. void() horde_armor_touch =
  657. {
  658. local float type, value, bit;
  659. if (other.health <= 0)
  660. return;
  661. if (other.classname != "player")
  662. return;
  663. if (self.classname == "item_armor1")
  664. {
  665. type = 0.3;
  666. value = 100;
  667. bit = IT_ARMOR1;
  668. }
  669. if (self.classname == "item_armor2")
  670. {
  671. type = 0.6;
  672. value = 150;
  673. bit = IT_ARMOR2;
  674. }
  675. if (self.classname == "item_armorInv")
  676. {
  677. type = 0.8;
  678. value = 200;
  679. bit = IT_ARMOR3;
  680. }
  681. if (other.armortype*other.armorvalue >= type*value)
  682. return;
  683. other.armortype = type;
  684. other.armorvalue = value;
  685. other.items = other.items - (other.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + bit;
  686. self.solid = SOLID_NOT;
  687. self.model = string_null;
  688. sprint(other, "$qc_item_armor");
  689. // armor touch sound
  690. sound(other, CHAN_ITEM, "items/armor1.wav", 1, ATTN_NORM);
  691. stuffcmd (other, "bf\n");
  692. self.owner.wait = FALSE;
  693. remove(self);
  694. };
  695. void() horde_print_keys =
  696. {
  697. dprint(ftos(keys_silver));
  698. dprint(" silver keys : ");
  699. dprint(ftos(keys_gold));
  700. dprint(" gold keys\n");
  701. };
  702. void(entity temp_player) horde_set_keys =
  703. {
  704. // player has just respawned, reset their keys based on global key values
  705. if (keys_silver > 0)
  706. temp_player.items+= IT_KEY1;
  707. if (keys_gold > 0)
  708. temp_player.items+= IT_KEY2;
  709. };
  710. void(float key_item) horde_key_give_all =
  711. {
  712. local entity temp_player;
  713. temp_player = find(world, classname, "player");
  714. while (temp_player)
  715. {
  716. temp_player.items = temp_player.items + key_item;
  717. temp_player = find(temp_player, classname, "player");
  718. }
  719. horde_print_keys();
  720. };
  721. void(float key_item) horde_key_remove_all =
  722. {
  723. local entity temp_player;
  724. temp_player = find(world, classname, "player");
  725. while (temp_player)
  726. {
  727. temp_player.items = temp_player.items - key_item;
  728. temp_player = find(temp_player, classname, "player");
  729. }
  730. horde_print_keys();
  731. };
  732. void(float key_item)horde_key_give =
  733. {
  734. if (key_item & IT_KEY1)
  735. {
  736. keys_silver++;
  737. if (keys_silver == 1)
  738. horde_key_give_all(IT_KEY1);
  739. }
  740. else if (key_item & IT_KEY2)
  741. {
  742. keys_gold++;
  743. if (keys_gold == 1)
  744. horde_key_give_all(IT_KEY2);
  745. }
  746. horde_print_keys();
  747. }
  748. void(float key_item)horde_key_spend =
  749. {
  750. if (key_item & IT_KEY1)
  751. {
  752. keys_silver--;
  753. if (keys_silver == 0)
  754. horde_key_remove_all(IT_KEY1);
  755. }
  756. else if (key_item & IT_KEY2)
  757. {
  758. keys_gold--;
  759. if (keys_gold == 0)
  760. horde_key_remove_all(IT_KEY2);
  761. }
  762. horde_print_keys();
  763. }
  764. void() horde_key_touch =
  765. {
  766. if (other.classname != "player")
  767. return;
  768. if (other.health <= 0)
  769. return;
  770. if (other.flags & FL_ISBOT)
  771. return;
  772. sprint(other, "$qc_got_item", self.netname);
  773. sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);
  774. stuffcmd (other, "bf\n");
  775. horde_key_give(self.items);
  776. // find the horde manager and trigger the next wave
  777. if (key_spawned)
  778. {
  779. dprint("key triggers next wave\n");
  780. horde_ent.wait = TRUE;
  781. horde_ent.think = Countdown;
  782. horde_ent.nextthink = time;
  783. }
  784. else
  785. dprint("key shouldn't trigger next wave!\n");
  786. /*
  787. local entity t = find(world, classname, "horde_manager");
  788. if (t != world)
  789. {
  790. t.wait = TRUE;
  791. t.think = Countdown;
  792. t.nextthink = time;
  793. }
  794. */
  795. remove(self);
  796. }
  797. void() SpawnAmmo =
  798. {
  799. local float r, is_big;
  800. local entity item;
  801. local vector pos;
  802. pos = self.origin;
  803. item = spawn();
  804. // roll a d4, a 1 means big ammo.
  805. is_big = (random() * 4) <= 1;
  806. if (is_big)
  807. pos = pos - '16 16 0';
  808. else
  809. pos = pos - '12 12 0';
  810. // NEW: roll a d20, 1-8 is shells, 9-14 is spikes, 14-17 is rockets, 18-20 is cells
  811. // OLD: roll a d12, 1-5 is shells, 6-10 is spikes, 11 is rockets, 12 is cells
  812. /*
  813. ITEM New % Old %
  814. shells 40% 41.66%
  815. spikes 30% 33.33%
  816. rockets 20% 8.33 %
  817. cells 10% 8.33 %
  818. */
  819. r = random() * 20;
  820. if (r <= 7) // shells
  821. {
  822. item.classname = "item_shells";
  823. if (is_big)
  824. {
  825. setmodel (item, "maps/b_shell1.bsp");
  826. item.aflag = 40;
  827. }
  828. else
  829. {
  830. setmodel (item, "maps/b_shell0.bsp");
  831. item.aflag = 20;
  832. }
  833. item.weapon = 1;
  834. item.netname = "$qc_shells";
  835. }
  836. else if (r <= 14) // spikes
  837. {
  838. item.classname = "item_spikes";
  839. if (is_big)
  840. {
  841. setmodel (item, "maps/b_nail1.bsp");
  842. item.aflag = 50;
  843. }
  844. else
  845. {
  846. setmodel (item, "maps/b_nail0.bsp");
  847. item.aflag = 25;
  848. }
  849. item.weapon = 2;
  850. item.netname = "$qc_nails";
  851. }
  852. else if (r <= 17) // rockets
  853. {
  854. item.classname = "item_rockets";
  855. if (is_big)
  856. {
  857. setmodel (item, "maps/b_rock1.bsp");
  858. item.aflag = 10;
  859. }
  860. else
  861. {
  862. setmodel (item, "maps/b_rock0.bsp");
  863. item.aflag = 5;
  864. }
  865. item.weapon = 3;
  866. item.netname = "$qc_rockets";
  867. }
  868. else // cells
  869. {
  870. item.classname = "item_cells";
  871. if (is_big)
  872. {
  873. setmodel (item, "maps/b_batt1.bsp");
  874. item.aflag = 12;
  875. }
  876. else
  877. {
  878. setmodel (item, "maps/b_batt0.bsp");
  879. item.aflag = 6;
  880. }
  881. item.weapon = 4;
  882. item.netname = "$qc_cells";
  883. }
  884. setorigin (item, pos + '0 0 1');
  885. setsize (item, '0 0 0', '32 32 56');
  886. item.owner = self;
  887. self.wait = TRUE;
  888. item.movetype = MOVETYPE_TOSS;
  889. item.solid = SOLID_TRIGGER;
  890. item.flags = FL_ITEM;
  891. item.touch = horde_ammo_touch;
  892. spawn_tfog_silent(pos + '0 0 8'); // added aug30
  893. //item.think = PlaceItem;
  894. //item.nextthink = time + 0.1;
  895. }
  896. void() SpawnItem =
  897. {
  898. local float r;
  899. local entity item;
  900. local vector pos;
  901. item = spawn();
  902. pos = self.origin; // set to origin, then offset as needed
  903. r = random() * 6;
  904. if (r < 3) // rotten health
  905. {
  906. item.classname = "item_health";
  907. item.touch = horde_health_touch;
  908. setmodel (item, "maps/b_bh10.bsp");
  909. item.noise = "items/r_item1.wav";
  910. item.healamount = 15;
  911. item.healtype = 0;
  912. setsize (item, '0 0 0', '32 32 56');
  913. //pos = pos - '16 16 0';
  914. }
  915. if (r < 5) // regular health
  916. {
  917. item.classname = "item_health";
  918. item.touch = horde_health_touch;
  919. setmodel (item, "maps/b_bh25.bsp");
  920. item.noise = "items/health1.wav";
  921. item.healamount = 25;
  922. item.healtype = 1;
  923. setsize (item, '0 0 0', '32 32 56');
  924. pos = pos - '16 16 0';
  925. }
  926. else // armor
  927. {
  928. item.classname = "item_armor1";
  929. item.touch = horde_armor_touch;
  930. item.armortype = 0.3;
  931. item.armorvalue = 100;
  932. setmodel (item, "progs/armor.mdl");
  933. item.skin = 0;
  934. setsize (item, '-16 -16 0', '16 16 56');
  935. }
  936. setorigin (item, pos + '0 0 1');
  937. item.owner = self;
  938. self.wait = TRUE;
  939. item.movetype = MOVETYPE_TOSS;
  940. item.solid = SOLID_TRIGGER;
  941. item.flags = FL_ITEM;
  942. //item.think = PlaceItem;
  943. //item.nextthink = time + 0.1;
  944. spawn_tfog_silent(pos + '0 0 8'); // added aug30
  945. };
  946. /* Spawn Key
  947. A key should spawn after every boss wave.
  948. */
  949. void(float is_gold) SpawnKey =
  950. {
  951. local entity item;
  952. item = spawn();
  953. if (is_gold)
  954. {
  955. item.items = IT_KEY2;
  956. // set model and sound based on worldtype
  957. if (world.worldtype == WORLDTYPE_METAL)
  958. {
  959. item.netname = "$qc_gold_runekey";
  960. item.noise = "misc/runekey.wav";
  961. setmodel (item, "progs/m_g_key.mdl");
  962. centerprint_all("$qc_horde_gold_runekey_appears");
  963. }
  964. else if (world.worldtype == WORLDTYPE_BASE)
  965. {
  966. item.netname = "$qc_gold_keycard";
  967. item.noise = "misc/basekey.wav";
  968. setmodel (item, "progs/b_g_key.mdl");
  969. centerprint_all("$qc_horde_gold_keycard_appears");
  970. }
  971. else // assume medieval
  972. {
  973. item.netname = "$qc_gold_key";
  974. item.noise = "misc/medkey.wav";
  975. setmodel (item, "progs/w_g_key.mdl");
  976. centerprint_all("$qc_horde_gold_key_appears");
  977. }
  978. }
  979. else
  980. {
  981. item.items = IT_KEY1;
  982. // set model and sound based on worldtype
  983. if (world.worldtype == WORLDTYPE_METAL)
  984. {
  985. item.netname = "$qc_silver_runekey";
  986. item.noise = "misc/runekey.wav";
  987. setmodel (item, "progs/m_s_key.mdl");
  988. centerprint_all("$qc_horde_silver_runekey_appears");
  989. }
  990. else if (world.worldtype == WORLDTYPE_BASE)
  991. {
  992. item.netname = "$qc_silver_keycard";
  993. item.noise = "misc/basekey.wav";
  994. setmodel (item, "progs/b_s_key.mdl");
  995. centerprint_all("$qc_horde_silver_keycard_appears");
  996. }
  997. else // assume medieval
  998. {
  999. item.netname = "$qc_silver_key";
  1000. item.noise = "misc/medkey.wav";
  1001. setmodel (item, "progs/w_s_key.mdl");
  1002. centerprint_all("$qc_horde_silver_key_appears");
  1003. }
  1004. }
  1005. //item.noise = "misc/medkey.wav";
  1006. setsize (item, '-16 -16 -25', '16 16 32');
  1007. item.touch = horde_key_touch;
  1008. //item.think = PlaceItem;
  1009. //item.nextthink = time + 0.2;
  1010. item.target = "horde_manager"; // when used, retrigger the horde manager
  1011. item.flags = FL_ITEM; // make extra wide
  1012. //item.flags = item.flags & FL_NOBOTS; // AY 11 Nov 2021 so bots can't get keys
  1013. item.solid = SOLID_TRIGGER;
  1014. item.movetype = MOVETYPE_TOSS;
  1015. setorigin (item, self.origin + '0 0 32');
  1016. item.velocity = '0 0 255';
  1017. // AY Feb22, 2022, rune keys shouldn't have light effects on them
  1018. if (world.worldtype != WORLDTYPE_METAL)
  1019. item.effects = EF_BRIGHTLIGHT;
  1020. //dprint(" spawned key\n");
  1021. spawn_tfog(item.origin);
  1022. key_spawned = 1;
  1023. };
  1024. void() SpawnGoldKey =
  1025. {
  1026. SpawnKey(TRUE);
  1027. };
  1028. void() SpawnSilverKey =
  1029. {
  1030. SpawnKey(FALSE);
  1031. };
  1032. // Function get a key given the current wave
  1033. // Wave 3 should return a key with spawnflag "KEY_FIRST"
  1034. // Wave 6 should return a key with spawnflag "KEY_SECOND"
  1035. // Wave 9 should return a key with spawnflag "KEY_THIRD"
  1036. // Wave 9+ should return a key with spawnflag "KEY_FOURTH_PLUS"
  1037. // If a key with the correct spawnflag isn't found, return any key
  1038. void() GetKey =
  1039. {
  1040. local entity t, l;
  1041. t = find(world, classname, "info_horde_key");
  1042. l = t; // save ref for later
  1043. // check if any keys exist
  1044. if (t == world)
  1045. {
  1046. dprint("ERROR: No info_horde_key!\n");
  1047. // continue as if player got the key
  1048. self.think = Countdown;
  1049. self.nextthink = time + 4;
  1050. return;
  1051. }
  1052. while(t != world)
  1053. {
  1054. if (self.wave <= 3) // looking for KEY_FIRST
  1055. {
  1056. if (t.spawnflags & KEY_FIRST)
  1057. {
  1058. t.think = SpawnSilverKey;
  1059. t.nextthink = time;
  1060. return;
  1061. }
  1062. }
  1063. else if (self.wave <= 6) // looking for KEY_SECOND
  1064. {
  1065. if (t.spawnflags & KEY_SECOND)
  1066. {
  1067. t.think = SpawnSilverKey;
  1068. t.nextthink = time;
  1069. return;
  1070. }
  1071. }
  1072. else if (self.wave <= 9) // looking for KEY_THIRD
  1073. {
  1074. if (t.spawnflags & KEY_THIRD)
  1075. {
  1076. t.think = SpawnGoldKey;
  1077. t.nextthink = time;
  1078. return;
  1079. }
  1080. }
  1081. else // looking for KEY_FOURTH_PLUS
  1082. {
  1083. // check next key
  1084. if (t.spawnflags & KEY_FOURTH_PLUS)
  1085. {
  1086. t.think = SpawnSilverKey;
  1087. t.nextthink = time;
  1088. return;
  1089. }
  1090. }
  1091. t = find(t, classname, "info_horde_key");
  1092. }
  1093. // didn't find key with matching spawnflag, return any key
  1094. dprint("didn't find key with matching spawnflag. Return last: ");
  1095. dprint(l.classname);
  1096. if (self.wave == 9)
  1097. l.think = SpawnGoldKey;
  1098. else
  1099. l.think = SpawnSilverKey;
  1100. l.nextthink = time;
  1101. };
  1102. //============================================================================
  1103. /*
  1104. SpawnSquad2
  1105. Yoder FEB02 2022
  1106. */
  1107. void(string name, vector org, vector temp_angles) SpawnSquad2 =
  1108. {
  1109. if (name == "3 grunts")
  1110. {
  1111. if (skill > 0)
  1112. {
  1113. SpawnMonster("grunt", org + '0 -40 0', temp_angles);
  1114. SpawnMonster("grunt", org + '40 40 0', temp_angles);
  1115. SpawnMonster("grunt", org + '-40 40 0', temp_angles);
  1116. }
  1117. else
  1118. {
  1119. SpawnMonster("grunt", org + '-40 0 0', temp_angles);
  1120. SpawnMonster("grunt", org + '40 0 0', temp_angles);
  1121. }
  1122. }
  1123. else if (name == "2 grunts, 1 dog")
  1124. {
  1125. SpawnMonster("dog", org + '44 0 0', temp_angles);
  1126. SpawnMonster("grunt", org + '-40 -40 0', temp_angles);
  1127. SpawnMonster("grunt", org + '-40 40 0', temp_angles);
  1128. }
  1129. else if (name == "2 dogs")
  1130. {
  1131. if (skill > 0)
  1132. {
  1133. SpawnMonster("dog", org + '-0 -44 0', temp_angles);
  1134. SpawnMonster("dog", org + '0 44 0', temp_angles);
  1135. }
  1136. else
  1137. SpawnMonster("dog", org + '-0 -44 0', temp_angles);
  1138. }
  1139. else if (name == "1 enforcer")
  1140. {
  1141. SpawnMonster("enforcer", org + '0 0 0', temp_angles);
  1142. }
  1143. else if (name == "2 enforcers")
  1144. {
  1145. if (skill > 0)
  1146. {
  1147. SpawnMonster("enforcer", org + '40 0 0', temp_angles);
  1148. SpawnMonster("enforcer", org + '-40 0 0', temp_angles);
  1149. }
  1150. else
  1151. SpawnMonster("enforcer", org + '0 0 0', temp_angles);
  1152. }
  1153. else if (name == "1 ogre")
  1154. {
  1155. SpawnMonster("ogre", org + '0 0 0', temp_angles);
  1156. }
  1157. else if (name == "2 knights")
  1158. {
  1159. if (skill > 0)
  1160. {
  1161. SpawnMonster("knight", org + '40 0 0', temp_angles);
  1162. SpawnMonster("knight", org + '-40 0 0', temp_angles);
  1163. }
  1164. else
  1165. {
  1166. SpawnMonster("knight", org + '40 0 0', temp_angles);
  1167. }
  1168. }
  1169. else if (name == "2 zombies")
  1170. {
  1171. SpawnMonster("zombie", org + '40 0 0', temp_angles);
  1172. SpawnMonster("zombie", org + '-40 0 0', temp_angles);
  1173. }
  1174. else if (name == "1 wizard")
  1175. SpawnMonster("wizard", org + '0 0 0', temp_angles);
  1176. else if (name == "2 hellknights")
  1177. {
  1178. if (skill > 0)
  1179. {
  1180. SpawnMonster("hellknight", org + '0 40 0', temp_angles);
  1181. SpawnMonster("hellknight", org + '0 -40 0', temp_angles);
  1182. }
  1183. else
  1184. SpawnMonster("hellknight", org + '0 40 0', temp_angles);
  1185. }
  1186. else if (name == "2 knights, 1 hellknight")
  1187. {
  1188. SpawnMonster("hellknight", org + '40 0 0', temp_angles);
  1189. SpawnMonster("knight", org + '-40 40 0', temp_angles);
  1190. SpawnMonster("knight", org + '-40 -40 0', temp_angles);
  1191. }
  1192. else if (name == "3 wizards")
  1193. {
  1194. if (skill > 0)
  1195. {
  1196. SpawnMonster("wizard", org + '40 40 40', temp_angles);
  1197. SpawnMonster("wizard", org + '-40 40 40', temp_angles);
  1198. SpawnMonster("wizard", org + '-40 -40 40', temp_angles);
  1199. }
  1200. else
  1201. {
  1202. SpawnMonster("wizard", org + '40 40 40', temp_angles);
  1203. SpawnMonster("wizard", org + '-40 40 40', temp_angles);
  1204. }
  1205. }
  1206. else if (name == "shambler")
  1207. SpawnMonster("shambler", org, temp_angles);
  1208. else if (name == "double demon")
  1209. {
  1210. if (skill >= 3 && random() > 0.8)
  1211. {
  1212. // double shambler
  1213. SpawnMonster("shambler", org + '40 40 0', temp_angles);
  1214. SpawnMonster("shambler", org + '-40 -40 0', temp_angles);
  1215. }
  1216. else if (skill >= 1)
  1217. {
  1218. SpawnMonster("demon", org + '40 40 0', temp_angles);
  1219. SpawnMonster("demon", org + '-40 -40 0', temp_angles);
  1220. }
  1221. else
  1222. SpawnMonster("demon", org, temp_angles);
  1223. }
  1224. else if (name == "shalrath")
  1225. SpawnMonster("shalrath", org, temp_angles);
  1226. else
  1227. dprint("ERROR: unrecognized squad name!\n");
  1228. spawn_tfog(org);
  1229. };
  1230. /*
  1231. HordeFindSpawnpoint
  1232. Yoder February 2nd 2022
  1233. */
  1234. float HORDE_SQUAD_TYPE_NORMAL = 0;
  1235. float HORDE_SQUAD_TYPE_RANGED = 1;
  1236. float HORDE_SQUAD_TYPE_FLYING = 2;
  1237. float HORDE_SQUAD_TYPE_BOSS = 3;
  1238. float HORDE_SQUAD_CAT_ERROR = -1;
  1239. float HORDE_SQUAD_CAT_FODDER = 0;
  1240. float HORDE_SQUAD_CAT_ELITE = 1;
  1241. float HORDE_SQUAD_CAT_BOSS = 2;
  1242. entity(float squad_type)HordeFindSpawnpoint =
  1243. {
  1244. local float spawnpoint, temp_spawncount, loopcount, randomspawn;
  1245. local float temp_spawncount_valid; // how many of the spawn points are valid
  1246. local entity t;
  1247. spawnpoint = 0;
  1248. loopcount = 0;
  1249. string squad_class;
  1250. if (squad_type == HORDE_SQUAD_TYPE_BOSS)
  1251. squad_class = "info_monster_start_boss";
  1252. else if (squad_type == HORDE_SQUAD_TYPE_FLYING)
  1253. squad_class = "info_monster_start_flying";
  1254. else if (squad_type == HORDE_SQUAD_TYPE_RANGED)
  1255. squad_class = "info_monster_start_ranged";
  1256. else
  1257. squad_class = "info_monster_start";
  1258. // STEP 1: count all spawns of spawn type
  1259. temp_spawncount = 0;
  1260. temp_spawncount_valid = 0;
  1261. // count the spawnpoints for squad_class
  1262. t = find(world, classname, squad_class);
  1263. while (t)
  1264. {
  1265. temp_spawncount++;
  1266. if ((time > t.wait) && (!(t.spawnflags & START_OFF)) &&
  1267. (!CheckBlockedSpawn(t)))
  1268. temp_spawncount_valid++;
  1269. t = find(t, classname, squad_class);
  1270. }
  1271. // verify against 0 valid spawns
  1272. if (temp_spawncount_valid <= 0)
  1273. {
  1274. if (squad_type == HORDE_SQUAD_TYPE_NORMAL)
  1275. {
  1276. dprint("HordeFindSpawnPoint: FOUND 0 Valid spawns\n");
  1277. return world; // already on fallback option, oops
  1278. }
  1279. else
  1280. {
  1281. squad_type = HORDE_SQUAD_TYPE_NORMAL;
  1282. squad_class = "info_monster_start";
  1283. t = world;
  1284. loopcount = 1;
  1285. }
  1286. }
  1287. // count again, but for Normal spawn points?
  1288. if (loopcount)
  1289. {
  1290. temp_spawncount = 0;
  1291. temp_spawncount_valid = 0;
  1292. t = find(world, classname, squad_class);
  1293. while (t)
  1294. {
  1295. temp_spawncount++;
  1296. if ((time > t.wait) && (!(t.spawnflags & START_OFF)) &&
  1297. (!CheckBlockedSpawn(t)))
  1298. temp_spawncount_valid++;
  1299. t = find(t, classname, squad_class);
  1300. }
  1301. }
  1302. // verify against 0 valid spawns
  1303. if (temp_spawncount_valid <= 0)
  1304. {
  1305. dprint("HordeFindSpawnPoint: FOUND 0 Valid spawns after recheck\n");
  1306. return world; // already on fallback option, oops
  1307. }
  1308. // pick random spawncount
  1309. loopcount = 0;
  1310. randomspawn = temp_spawncount * random();
  1311. dprint("randomspawn: ");dprint(ftos(randomspawn));
  1312. dprint(" | whole count: ");dprint(ftos(temp_spawncount));
  1313. dprint(" | valid count: ");dprint(ftos(temp_spawncount_valid));dprint("\n");
  1314. t = find (world, classname, squad_class);
  1315. while(t)
  1316. {
  1317. spawnpoint++;
  1318. if (spawnpoint >= randomspawn)
  1319. {
  1320. if (!(t.spawnflags & START_OFF) && (time > t.wait) && (!(CheckBlockedSpawn(t))))
  1321. {
  1322. dprint("picked spawnpoint: ");dprint(ftos(spawnpoint));dprint("\n");
  1323. return t;
  1324. }
  1325. }
  1326. t = find(t, classname, squad_class);
  1327. if (!t)
  1328. {
  1329. if (loopcount)
  1330. dprint("something has gone horribly wrong!\n");
  1331. else
  1332. {
  1333. loopcount = 1;
  1334. t = find(t, classname, squad_class);
  1335. spawnpoint = 0;
  1336. randomspawn = 0; // allow any valid spawn
  1337. }
  1338. }
  1339. }
  1340. return world;
  1341. };
  1342. /*
  1343. SpawnWave2
  1344. Instead of finding a spawn point and then spawning a squad,
  1345. decide a squad, and then find a spawn point.
  1346. Yoder February 2nd 2022
  1347. */
  1348. void() SpawnWave2 =
  1349. {
  1350. local entity t, oself; // t is the temp spawn, oself is for temp storage of self
  1351. local float squad_type;
  1352. local string squad_name; // specific squad name
  1353. local float r; // random roll
  1354. local float squad_cat; //
  1355. //STEP 1: determine what kind of monster is getting spawned
  1356. if (self.army)
  1357. {
  1358. if (self.fodder > 0)
  1359. {
  1360. r = random() * 4;
  1361. if (r < 1)
  1362. squad_name = "3 grunts";
  1363. else if (r < 2)
  1364. squad_name = "2 grunts, 1 dog";
  1365. else if (r < 3.5)
  1366. squad_name = "2 dogs";
  1367. else
  1368. {
  1369. squad_name = "1 enforcer";
  1370. squad_type = HORDE_SQUAD_TYPE_RANGED;
  1371. }
  1372. squad_cat = HORDE_SQUAD_CAT_FODDER;
  1373. }
  1374. else if (self.elites > 0)
  1375. {
  1376. squad_type = HORDE_SQUAD_TYPE_RANGED;
  1377. r = random() * 2;
  1378. if (r < 1.5)
  1379. squad_name = "2 enforcers";
  1380. else
  1381. squad_name = "1 ogre";
  1382. squad_cat = HORDE_SQUAD_CAT_ELITE;
  1383. }
  1384. else if (self.bosses > 0)
  1385. {
  1386. dprint("ERROR: should not spawn boss squad in army wave\n");
  1387. self.bosses = 0;
  1388. squad_cat = HORDE_SQUAD_CAT_ERROR;
  1389. }
  1390. }
  1391. else // NON-ARMY
  1392. {
  1393. if (self.fodder > 0)
  1394. {
  1395. r = random() * 4;
  1396. if (r < 2)
  1397. squad_name = "2 knights";
  1398. else if (r < 3)
  1399. squad_name = "2 zombies";
  1400. else
  1401. {
  1402. squad_name = "1 wizard";
  1403. squad_type = HORDE_SQUAD_TYPE_FLYING;
  1404. }
  1405. squad_cat = HORDE_SQUAD_CAT_FODDER;
  1406. }
  1407. else if (self.elites > 0)
  1408. {
  1409. r = random() * 4;
  1410. if (r < 1)
  1411. squad_name = "2 hellknights";
  1412. else if (r < 2)
  1413. squad_name = "2 knights, 1 hellknight";
  1414. else if (r < 3)
  1415. {
  1416. squad_name = "1 ogre";
  1417. squad_type = HORDE_SQUAD_TYPE_RANGED;
  1418. }
  1419. else
  1420. {
  1421. squad_name = "3 wizards";
  1422. squad_type = HORDE_SQUAD_TYPE_FLYING;
  1423. }
  1424. squad_cat = HORDE_SQUAD_CAT_ELITE;
  1425. }
  1426. else if (self.bosses > 0)
  1427. {
  1428. squad_type = HORDE_SQUAD_TYPE_BOSS;
  1429. r = random() * 3;
  1430. if (r < 1)
  1431. squad_name = "shambler";
  1432. else if (r < 2.5)
  1433. {
  1434. squad_name = "double demon";
  1435. squad_type = HORDE_SQUAD_TYPE_NORMAL;
  1436. }
  1437. else
  1438. squad_name = "shalrath";
  1439. squad_cat = HORDE_SQUAD_CAT_BOSS;
  1440. }
  1441. }
  1442. //STEP 2: find a spawn point for the squad
  1443. t = HordeFindSpawnpoint(squad_type);
  1444. dprint("spawnpoint found was: ");dprint(t.classname);dprint("\n");
  1445. if (t && (squad_cat != HORDE_SQUAD_CAT_ERROR))
  1446. {
  1447. t.wait = time + SPAWN_RESET_TIME; // block spawnpoint from reuse for a duration
  1448. // use targets
  1449. oself = self;
  1450. self = t;
  1451. SUB_UseTargets();
  1452. self = oself;
  1453. // set angles, just in case
  1454. if (!t.angles)
  1455. t.angles = '0 0 0';
  1456. SpawnSquad2(squad_name, t.origin, t.angles);
  1457. WriteByte(MSG_ALL, SVC_UPDATESTAT);
  1458. WriteByte(MSG_ALL, 12); // 12 = STAT_TOTALMONSTERS
  1459. WriteLong(MSG_ALL, total_monsters);
  1460. if (squad_cat == HORDE_SQUAD_CAT_FODDER)
  1461. self.fodder--;
  1462. else if (squad_cat == HORDE_SQUAD_CAT_ELITE)
  1463. self.elites--;
  1464. else if (squad_cat == HORDE_SQUAD_CAT_BOSS)
  1465. self.bosses--;
  1466. if ((self.fodder + self.elites + self.bosses) <= 0) // max spawns hit
  1467. {
  1468. dprint("wave spawn completed!\n");
  1469. self.wait = FALSE;
  1470. self.think = Wavecheck;
  1471. self.nextthink = time + 30;
  1472. }
  1473. else
  1474. {
  1475. // normal spawnwave wait time
  1476. self.think = SpawnWave2;
  1477. self.nextthink = time + 1 + random() + 1;
  1478. }
  1479. }
  1480. else
  1481. {
  1482. dprint("no valid spawns, wait a moment\n");
  1483. self.think = SpawnWave2;
  1484. self.nextthink = time + 1;
  1485. }
  1486. };
  1487. /* SpawnWavePrep
  1488. called once at the countdown starts
  1489. determines the number of squads by type
  1490. also spawns ammo and items
  1491. */
  1492. float() SpawnWavePrep =
  1493. {
  1494. local entity p;
  1495. local float playercount, playerscalar;
  1496. // Reset Key Spawn
  1497. key_spawned = FALSE;
  1498. // Respawn dead players!
  1499. if (HordeGetPlayersAlive() < 1)
  1500. {
  1501. dprint("WARNING: Last player died at round end, don't start new round!\n");
  1502. return 0;
  1503. }
  1504. // Items
  1505. p = find (world, classname, "info_horde_item");
  1506. while (p)
  1507. {
  1508. if (!p.wait)
  1509. {
  1510. p.think = SpawnItem;
  1511. p.nextthink = time + random() * 2;
  1512. }
  1513. p = find (p, classname, "info_horde_item");
  1514. }
  1515. self.wave++;
  1516. dprint("wave ");
  1517. dprint(ftos(self.wave));
  1518. dprint("\n");
  1519. // See spreadsheet for notes on squad scaling
  1520. // AY FEB07 scaling start wave based on difficulty
  1521. local float temp_wave;
  1522. if (skill >= 3)
  1523. temp_wave = self.wave + 6;
  1524. else if (skill >= 2)
  1525. temp_wave = self.wave + 3;
  1526. else
  1527. temp_wave = self.wave;
  1528. // Determine if Army wave
  1529. if ((temp_wave + 2) % 3 == 0)
  1530. self.army = TRUE;
  1531. else
  1532. self.army = FALSE;
  1533. // player scalar
  1534. playercount = HordeGetPlayersAlive();
  1535. if (playercount >= 4)
  1536. playerscalar = 2;
  1537. else if (playercount >= 3)
  1538. playerscalar = 1.5;
  1539. else if (playercount >= 2)
  1540. playerscalar = 1.25;
  1541. else
  1542. playerscalar = 1;
  1543. // Boss Count
  1544. if (temp_wave % 3 == 0)
  1545. {
  1546. self.bosses = floor((temp_wave+1)/4);
  1547. }
  1548. else if ((skill > 1) && (!self.army) && (temp_wave > 9))
  1549. {
  1550. //AY feb07, some extra spice
  1551. self.bosses = floor((temp_wave+1)/8);
  1552. }
  1553. // Elite Count
  1554. self.elites = ceil((temp_wave - 1)/3) - floor(self.bosses/2);
  1555. self.elites = floor(self.elites * playerscalar);
  1556. // Fodder Count
  1557. self.fodder = (temp_wave + 2) - (self.bosses * 2 + self.elites);
  1558. self.fodder = floor(self.fodder * playerscalar);
  1559. // Trigger map entities
  1560. local entity stemp;
  1561. p = find (world, targetname, self.target);
  1562. while(p)
  1563. {
  1564. stemp = self;
  1565. self = p;
  1566. if (self.use != SUB_Null)
  1567. {
  1568. if (self.use)
  1569. self.use ();
  1570. }
  1571. self = stemp;
  1572. p = find (p, targetname, self.target);
  1573. }
  1574. self.wait = TRUE;
  1575. return 1;
  1576. }
  1577. void() Countdown4 =
  1578. {
  1579. if (self.wave % 3 == 0)
  1580. centerprint_all("$qc_horde_boss_wave");
  1581. else
  1582. centerprint_all("$qc_horde_fight");
  1583. SpawnWave2();
  1584. sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
  1585. };
  1586. void() Countdown3 =
  1587. {
  1588. centerprint_all("1");
  1589. self.think = Countdown4;
  1590. self.nextthink = time + 1;
  1591. sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
  1592. };
  1593. void() Countdown2 =
  1594. {
  1595. centerprint_all("2");
  1596. self.think = Countdown3;
  1597. self.nextthink = time + 1;
  1598. sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
  1599. };
  1600. void() Countdown =
  1601. {
  1602. if (!SpawnWavePrep())
  1603. return;
  1604. centerprint_all("3");
  1605. self.think = Countdown2;
  1606. self.nextthink = time + 1;
  1607. sound(self, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NONE);
  1608. };
  1609. void() RespawnAllPlayers =
  1610. {
  1611. local entity p, oself;
  1612. p = find (world, classname, "player");
  1613. while (p)
  1614. {
  1615. if (p.deadflag > 0)
  1616. {
  1617. oself = self;
  1618. self = p;
  1619. horde_respawn_teammate();
  1620. self = oself;
  1621. //p.think = horde_respawn_teammate;
  1622. //p.nextthink = time; // next frame
  1623. }
  1624. p = find (p, classname, "player");
  1625. }
  1626. };
  1627. /*
  1628. When each enemy spawned by the horde manager dies, they trigger self.owner.use, which is wavecheck.
  1629. Wavecheck sees if all spawned monsters are dead, so it can determine if it needs to start a new wave.
  1630. */
  1631. void() Wavecheck =
  1632. {
  1633. if (self.wait) // still in the process of spawning monsters, don't wavecheck
  1634. return;
  1635. self.think = Wavecheck;
  1636. self.nextthink = time + 10;
  1637. dprint("\n===============\n");
  1638. dprint("checking kills: ");
  1639. dprint(ftos(killed_monsters));
  1640. dprint(" of ");
  1641. dprint(ftos(total_monsters));
  1642. dprint("\n");
  1643. // debug checking last monster position
  1644. if (killed_monsters + 3 >= total_monsters)
  1645. {
  1646. local entity t;
  1647. t = find(world, category, CATEGORY_MONSTER);
  1648. while(t)
  1649. {
  1650. if (t.health > 0)
  1651. {
  1652. dprint("Position of monster: ");
  1653. dprint(t.classname);
  1654. dprint(" : ");
  1655. dprint(vtos(t.origin));
  1656. dprint("\n");
  1657. }
  1658. else
  1659. t.category = string_null;
  1660. t = find(t, category, CATEGORY_MONSTER);
  1661. }
  1662. }
  1663. // Early exit for kill count
  1664. if ((self.wave % 3 == 0) || (self.wave < 3)) // Be exact?
  1665. {
  1666. dprint("check monsters killed as boss wave\n");
  1667. if (HordeGetMonstersAlive() > 0) // testing the new way
  1668. return;
  1669. //if (killed_monsters < total_monsters)
  1670. // return;
  1671. }
  1672. else
  1673. {
  1674. dprint("check monsters killed as any other wave\n");
  1675. if (HordeGetMonstersAlive() > 5)
  1676. return;
  1677. //if (killed_monsters < (total_monsters -5))
  1678. // return;
  1679. }
  1680. // Made it this far, means wave completed successfully
  1681. dprint("wavecheck looks good! Respawning players\n");
  1682. RespawnAllPlayers();
  1683. self.wait = 1;
  1684. self.think = Countdown;
  1685. if (self.wave % 3 == 0) // it was a boss wave
  1686. {
  1687. if(!key_spawned) // key was already spawned, don't spawn another yet!
  1688. GetKey();
  1689. self.nextthink = time + 20; // in case the players are slow to find the key
  1690. }
  1691. else
  1692. {
  1693. self.nextthink = time;
  1694. }
  1695. dprint("wavecheck now completed.\n");
  1696. };
  1697. // setup horde rules
  1698. void() SetHorde =
  1699. {
  1700. dprint("TEST: It is September 23, 2021!\n"); // yoder sanity test
  1701. self.think = Countdown;
  1702. self.nextthink = time + 1;
  1703. }
  1704. // define entity
  1705. void() horde_manager =
  1706. {
  1707. // AY, Dec06 2021, auto set horde cvar
  1708. if (!cvar("horde") && !deathmatch)
  1709. cvar_set("horde", "1");
  1710. // end AY
  1711. PrecacheMonsters();
  1712. horde_ent = self; // set global reference to the horde manager
  1713. if (!self.target)
  1714. self.target = "horde_event"; // default name
  1715. self.targetname = "horde_manager";
  1716. self.think = SetHorde;
  1717. self.nextthink = time + HORDE_START_DELAY;
  1718. self.use = Wavecheck;
  1719. self.wait = 1; // waiting before next wave?
  1720. self.delay = 9; // max number of squads to spawn in a wave
  1721. self.wave = 0;
  1722. //if (!horde)
  1723. // horde = 1; // in case CVAR isn't already set
  1724. };
  1725. void() PowerupFade =
  1726. {
  1727. if (self.alpha > 0)
  1728. {
  1729. self.alpha = self.alpha - 0.25 * frametime;
  1730. self.nextthink = time; // think next frame
  1731. }
  1732. else
  1733. remove(self);
  1734. }
  1735. void() horde_powerup_think =
  1736. {
  1737. if (self.velocity_z < 0)
  1738. {
  1739. dprint("WARNING: Powerup fell out of world. Remove\n");
  1740. remove(self);
  1741. }
  1742. else
  1743. {
  1744. self.alpha = 1;
  1745. self.think = PowerupFade;
  1746. self.nextthink = time;
  1747. }
  1748. };
  1749. void() horde_spawn_powerup =
  1750. {
  1751. if (!powerup_chance)
  1752. powerup_chance = DEFAULT_POWERUP_CHANCE;
  1753. if (random() < powerup_chance) // "if(1)" to guarantee powerup drop
  1754. {
  1755. dprint("powerup chance was: ");
  1756. dprint(ftos(powerup_chance));
  1757. dprint("\n");
  1758. powerup_chance = DEFAULT_POWERUP_CHANCE;
  1759. local entity powerup = spawn();
  1760. setorigin (powerup, self.origin + '0 0 0');
  1761. //setsize (powerup, '-16 -16 -24', '16 16 32');
  1762. powerup.flags = FL_ITEM;
  1763. powerup.solid = SOLID_TRIGGER;
  1764. powerup.movetype = MOVETYPE_BOUNCE;
  1765. powerup.velocity = '0 0 300';
  1766. //powerup.velocity_x = crandom() * 64;
  1767. //powerup.velocity_y = crandom() * 64;
  1768. powerup.touch = powerup_touch;
  1769. powerup.think = horde_powerup_think;
  1770. powerup.nextthink = time + 10;
  1771. if (random() < 0.25) // spawn pentagram (invulnerability)
  1772. {
  1773. powerup.noise = "items/protect.wav";
  1774. setmodel(powerup, "progs/invulner.mdl");
  1775. powerup.netname = "$qc_pentagram_of_protection";
  1776. powerup.items = IT_INVULNERABILITY;
  1777. powerup.classname = "item_artifact_invulnerability";
  1778. }
  1779. else // spawn quad damage
  1780. {
  1781. powerup.noise = "items/damage.wav";
  1782. setmodel(powerup, "progs/quaddama.mdl");
  1783. powerup.netname = "$qc_quad_damage";
  1784. powerup.items = IT_QUAD;
  1785. powerup.classname = "item_artifact_super_damage";
  1786. }
  1787. //powerup.effects = EF_DIMLIGHT;
  1788. setsize (powerup, '-12 -12 -12', '12 12 12');
  1789. }
  1790. else
  1791. powerup_chance = powerup_chance + POWERUP_CHANCE_GAIN;
  1792. };
  1793. // remote wavecheck
  1794. // this is to trigger a wavecheck from outside of horde mode
  1795. void() remote_wavecheck =
  1796. {
  1797. if(!horde_ent || (intermission_running))
  1798. {
  1799. dprint("no wavecheck.");
  1800. if (!horde_ent)
  1801. dprint(" no horde ent found.");
  1802. if (intermission_running)
  1803. dprint(" intermission running.");
  1804. dprint("\n\n");
  1805. return;
  1806. }
  1807. dprint("remote wavecheck from: ");
  1808. dprint(self.classname);
  1809. dprint("\n");
  1810. local entity stemp = self; // the entity that is triggering the remote wavecheck
  1811. self = horde_ent;
  1812. self.use();
  1813. self = stemp; // set self back to whatever it was
  1814. };
  1815. /* CheckBlockedSpawn
  1816. AY Feb24, 2022
  1817. Checks the horde spawn against all living players
  1818. Returns true/false if spawn is blocked.
  1819. */
  1820. float(entity spawnpoint) CheckBlockedSpawn =
  1821. {
  1822. local float blocked = FALSE;
  1823. local entity p;
  1824. local vector p_mins, p_maxs, s_mins, s_maxs;
  1825. if (spawnpoint.spawnflags & SKIP_BLOCK_CHECK)
  1826. return FALSE;
  1827. p = find(world, classname, "player");
  1828. while(p && !blocked)
  1829. {
  1830. if ((p.health > 0) && (p.deadflag <= 0))
  1831. {
  1832. p_mins = p.origin + p.mins;
  1833. p_maxs = p.origin + p.maxs;
  1834. s_mins = spawnpoint.origin + spawnpoint.mins;
  1835. s_maxs = spawnpoint.origin + spawnpoint.maxs;
  1836. if (((p_maxs_x > s_mins_x) && (p_mins_x < s_maxs_x)) &&
  1837. ((p_maxs_y > s_mins_y) && (p_mins_y < s_maxs_y)) &&
  1838. ((p_maxs_y > s_mins_y) && (p_mins_y < s_maxs_y)))
  1839. {
  1840. dprint("player blocking spawn\n");
  1841. blocked = TRUE;
  1842. }
  1843. }
  1844. p = find(p, classname, "player");
  1845. }
  1846. return blocked;
  1847. };