4 void spawnfunc_td_controller()
11 if(autocvar_g_td_force_settings)
15 self.monstercount = 0;
20 self.netname = "Tower Defense controller entity";
21 self.classname = "td_controller";
24 td_dont_end = ((self.dontend) ? self.dontend : autocvar_g_td_generator_dontend);
25 max_waves = ((self.maxwaves) ? self.maxwaves : autocvar_g_td_max_waves);
26 totalmonsters = ((self.monstercount) ? self.monstercount : autocvar_g_td_monster_count);
27 wave_count = ((self.startwave) ? self.startwave : autocvar_g_td_start_wave);
28 max_turrets = ((self.maxturrets) ? self.maxturrets : autocvar_g_td_turret_max);
29 build_time = ((self.buildtime) ? self.buildtime : autocvar_g_td_buildphase_time);
30 m_speed_walk = ((self.mspeed_walk) ? self.mspeed_walk : autocvar_g_td_monsters_speed_walk);
31 m_speed_run = ((self.mspeed_run) ? self.mspeed_run : autocvar_g_td_monsters_speed_run);
32 spawn_delay = ((self.spawndelay) ? self.spawndelay : autocvar_g_td_monsters_spawn_delay);
33 max_current = ((self.maxcurrent) ? self.maxcurrent : autocvar_g_td_current_monsters);
38 void td_generator_die()
42 print((td_gencount > 1) ? "A generator was destroyed!\n" : "The generator was destroyed.\n");
44 if(autocvar_sv_eventlog)
45 GameLogEcho(":gendestroyed");
51 Send_CSQC_Centerprint_Generic(tail, CPID_KH_MSG, ((td_gencount > 1) ? "A generator was destroyed!" : "The generator was destroyed."), 0, 0);
54 setmodel(self, "models/onslaught/generator_dead.md3");
55 self.solid = SOLID_NOT;
56 self.takedamage = DAMAGE_NO;
57 self.event_damage = func_null;
61 pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1);
63 WaypointSprite_Kill(self.sprite);
66 void td_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
68 if(attacker.classname == STR_PLAYER || attacker.turrcaps_flags & TFL_TURRCAPS_ISTURRET || attacker.vehicle_flags & VHF_ISVEHICLE)
75 Send_CSQC_Centerprint_Generic(tail, CPID_KH_MSG, "The generator is under attack!", 0, 0);
78 self.health -= damage;
80 WaypointSprite_UpdateHealth(self.sprite, self.health);
86 void spawnfunc_td_generator()
97 self.health = autocvar_g_td_generator_health;
99 // precache generator model
100 precache_model("models/onslaught/generator.md3");
101 precache_model("models/onslaught/generator_dead.md3");
103 self.model = "models/onslaught/generator.md3";
104 setmodel(self, self.model);
105 self.classname = "td_generator";
106 self.solid = SOLID_BBOX;
107 self.takedamage = DAMAGE_AIM;
108 self.event_damage = td_generator_damage;
111 self.think = func_null;
112 self.max_health = self.health;
113 self.movetype = MOVETYPE_NONE;
114 self.monster_attack = TRUE;
116 self.netname = "Generator";
118 setsize(self, GENERATOR_MIN, GENERATOR_MAX);
122 WaypointSprite_SpawnFixed(self.netname, self.origin + '0 0 60', self, sprite, RADARICON_OBJECTIVE, '1 0.5 0');
123 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
124 WaypointSprite_UpdateHealth(self.sprite, self.health);
127 entity PickGenerator()
129 entity generator, head;
131 generator = find(world, classname, "td_generator");
134 RandomSelection_Init();
135 for(head = world;(head = find(head, classname, "td_generator")); )
137 RandomSelection_Add(head, 0, string_null, 1, 1);
139 generator = RandomSelection_chosen_ent;
144 void spawn_td_fuel(float fuel_size)
151 self.ammo_fuel = fuel_size * monster_skill;
152 StartItem("models/items/g_fuel.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "Turret Fuel", IT_FUEL, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
154 self.velocity = randomvec() * 175 + '0 0 325';
157 void spawnfunc_td_waypoint()
164 string t1 = self.target;
166 self.classname = "td_waypoint";
168 if(self.target2 != "")
170 RandomSelection_Init();
171 RandomSelection_Add(world, 0, t1, 1, 1);
172 RandomSelection_Add(world, 0, self.target2, 1, 1);
174 self.target = RandomSelection_chosen_string;
178 void spawnfunc_monster_swarm()
186 string t1 = self.target;
188 switch(self.spawntype)
191 waterspawns_count += 1; break;
193 flyspawns_count += 1; break;
198 switch(self.spawnflags)
201 self.classname = "swarm_strong"; break;
203 self.classname = "swarm_weak"; break;
205 self.classname = "monster_swarm"; break;
208 if(self.target2 != "")
210 RandomSelection_Init();
211 RandomSelection_Add(world, 0, t1, 1, 1);
212 RandomSelection_Add(world, 0, self.target2, 1, 1);
214 self.target = RandomSelection_chosen_string;
217 WaypointSprite_SpawnFixed("Monsters", self.origin + '0 0 60', self, sprite, RADARICON_HERE, '1 0.5 0');
219 if(self.target == "")
220 print("Warning: monster_swarm entity without a valid target\n");
223 void spawnturret(entity spawnedby, entity own, string turet, vector orig)
225 if(spawnedby.classname != STR_PLAYER)
227 print("Warning: A non-player entity tried to spawn a turret\n");
236 setorigin(self, orig);
237 self.spawnflags = TSL_NO_RESPAWN;
238 self.monster_attack = TRUE;
239 self.realowner = own;
240 self.angles_y = spawnedby.v_angle_y;
241 spawnedby.turret_cnt += 1;
242 self.colormap = spawnedby.colormap;
247 case "plasma": spawnfunc_turret_plasma(); break;
248 case "mlrs": spawnfunc_turret_mlrs(); break;
249 case "phaser": spawnfunc_turret_phaser(); break;
250 case "hellion": spawnfunc_turret_hellion(); break;
251 case "walker": spawnfunc_turret_walker(); break;
252 case "flac": spawnfunc_turret_flac(); break;
253 case "tesla": spawnfunc_turret_tesla(); break;
254 case "fusionreactor": spawnfunc_turret_fusionreactor(); break;
260 void buffturret (entity tur, float buff)
262 tur.turret_buff += 1;
263 tur.max_health *= buff;
264 tur.tur_health = tur.max_health;
265 tur.health = tur.max_health;
266 tur.ammo_max *= buff;
267 tur.ammo_recharge *= buff;
268 tur.shot_dmg *= buff;
269 tur.shot_refire -= buff * 0.2;
270 tur.shot_radius *= buff;
271 tur.shot_speed *= buff;
272 tur.shot_spread *= buff;
273 tur.shot_force *= buff;
276 void AnnounceSpawn(string anounce)
279 FOR_EACH_PLAYER(tail)
281 Send_CSQC_Centerprint_Generic(tail, CPID_KH_MSG, strcat("^1A ", anounce, " has arrived!"), 0, 0);
285 entity PickSpawn (string strngth, string type)
288 RandomSelection_Init();
289 for(e = world;(e = find(e, classname, strngth)); )
291 RandomSelection_Add(e, 0, string_null, 1, 1);
294 return RandomSelection_chosen_ent;
297 void TD_SpawnMonster(string mnster, string strngth, string type)
301 e = PickSpawn(strngth, type);
304 e = PickSpawn("monster_swarm", "");
306 mon = spawnmonster(mnster, e, e, e.origin, FALSE, 0);
307 mon.target = e.target;
310 string Monster_GetStrength(string mnster)
329 return "swarm_strong";
331 return "monster_swarm";
335 string Monster_GetType(string mnster)
352 return "monster_swarm";
354 return "monster_fly";
356 return "monster_swim";
360 string RandomMonster()
362 RandomSelection_Init();
364 if(n_demons) RandomSelection_Add(world, 0, "demon", 1, 1);
365 if(n_shalraths) RandomSelection_Add(world, 0, "vore", 1, 1);
366 if(n_soldiers) RandomSelection_Add(world, 0, "soldier", 1, 1);
367 if(n_hknights) RandomSelection_Add(world, 0, "hellknight", 1, 1);
368 if(n_enforcers) RandomSelection_Add(world, 0, "enforcer", 1, 1);
369 if(n_zombies) RandomSelection_Add(world, 0, "zombie", 1, 1);
370 if(n_spiders) RandomSelection_Add(world, 0, "spider", 1, 1);
371 if(n_ogres) RandomSelection_Add(world, 0, "ogre", 1, 1);
372 if(n_dogs) RandomSelection_Add(world, 0, "dog", 1, 1);
373 if(n_knights) RandomSelection_Add(world, 0, "knight", 1, 1);
374 if(n_shamblers) RandomSelection_Add(world, 0, "shambler", 0.2, 0.2);
375 if(n_tarbabies) RandomSelection_Add(world, 0, "spawn", 0.2, 0.2);
376 if(n_wizards && flyspawns_count) RandomSelection_Add(world, 0, "scrag", 1, 1);
377 if(n_fish && waterspawns_count) RandomSelection_Add(world, 0, "fish", 0.2, 0.2);
379 return RandomSelection_chosen_string;
384 string monstrngth, whichmon, montype;
386 current_phase = PHASE_COMBAT;
388 if(monster_count <= 0)
394 self.think = combat_phase;
396 whichmon = RandomMonster();
398 monstrngth = Monster_GetStrength(whichmon);
399 montype = Monster_GetType(whichmon);
401 if(current_monsters <= max_current && whichmon != "")
403 TD_SpawnMonster(whichmon, monstrngth, montype);
404 self.nextthink = time + spawn_delay;
407 self.nextthink = time + 6;
410 void queue_monsters(float maxmonsters)
412 float mc = 11; // note: shambler + tarbaby = 1
414 if(waterspawns_count > 0)
416 if(flyspawns_count > 0)
419 DistributeEvenly_Init(maxmonsters, mc);
420 n_demons = DistributeEvenly_Get(1);
421 n_ogres = DistributeEvenly_Get(1);
422 n_dogs = DistributeEvenly_Get(1);
423 n_knights = DistributeEvenly_Get(1);
424 n_shalraths = DistributeEvenly_Get(1);
425 n_soldiers = DistributeEvenly_Get(1);
426 n_hknights = DistributeEvenly_Get(1);
427 n_enforcers = DistributeEvenly_Get(1);
428 n_zombies = DistributeEvenly_Get(1);
429 n_spiders = DistributeEvenly_Get(1);
430 n_tarbabies = DistributeEvenly_Get(0.7);
431 n_shamblers = DistributeEvenly_Get(0.3);
432 if(flyspawns_count > 0)
433 n_wizards = DistributeEvenly_Get(1);
434 if(waterspawns_count > 0)
435 n_fish = DistributeEvenly_Get(1);
438 void combat_phase_begin()
440 monster_count = totalmonsters;
443 print("^1Combat phase!\n");
444 FOR_EACH_PLAYER(tail)
446 Send_CSQC_Centerprint_Generic(tail, CPID_KH_MSG, "^1Combat phase!", 0, 0);
448 if(autocvar_sv_eventlog)
449 GameLogEcho(":combatphase");
450 self.think = combat_phase;
451 self.nextthink = time + 1;
453 for(head = world;(head = find(head, classname, "td_generator")); )
455 head.takedamage = DAMAGE_AIM;
459 float cphase_updates;
460 void combat_phase_announce() // TODO: clean up these fail nextthinks...
464 if(cphase_updates == 0)
465 Announce("prepareforbattle");
466 else if(cphase_updates == 3)
468 else if(cphase_updates == 4)
470 else if(cphase_updates == 5)
472 else if(cphase_updates == 6)
475 combat_phase_begin();
478 if(cphase_updates >= 6)
481 self.think = combat_phase_announce;
482 self.nextthink = time + 1;
488 float n_players = 0, gen_washealed = FALSE, player_washealed = FALSE;
489 string buildmsg, healmsg, countmsg, startmsg, genhealmsg;
491 current_phase = PHASE_BUILD;
493 for(head = world;(head = find(head, classname, "td_generator")); )
495 if(head.health <= 5 && head.max_health > 10)
496 Announce("lastsecond");
498 if(head.health < head.max_health)
500 gen_washealed = TRUE;
501 head.health = head.max_health;
502 WaypointSprite_UpdateHealth(head.sprite, head.health);
504 head.takedamage = DAMAGE_NO;
507 FOR_EACH_PLAYER(head)
509 if(head.health < 100)
511 player_washealed = TRUE;
512 break; // we found 1, so no need to check the others
516 totalmonsters += autocvar_g_td_monster_count_increment * wave_count;
517 monster_skill += autocvar_g_td_monsters_skill_increment;
519 if(wave_count < 1) wave_count = 1;
521 genhealmsg = (gen_washealed) ? ((td_gencount == 1) ? " and generator " : " and generators ") : "";
522 buildmsg = sprintf("%s build phase... ", (wave_count == max_waves) ? "^1Final wave^3" : sprintf("Wave %d", wave_count));
523 healmsg = (player_washealed) ? sprintf("All players %shealed. ", genhealmsg) : "";
524 countmsg = sprintf("Next monsters: %d. ", totalmonsters);
525 startmsg = sprintf("Wave starts in %d seconds", autocvar_g_td_buildphase_time);
527 FOR_EACH_PLAYER(head)
529 if(head.health < 100)
533 PlayerScore_Add(head, SP_TD_SCORE, -autocvar_g_td_generator_damaged_points);
536 Send_CSQC_Centerprint_Generic(head, CPID_KH_MSG, strcat(buildmsg, healmsg, countmsg, startmsg), 5, 0);
539 FOR_EACH_MONSTER(head)
543 print(strcat("Warning: Monster still alive during build phase! Monster name: ", head.netname, "\n"));
545 WaypointSprite_Kill(head.sprite);
551 totalmonsters += n_players;
552 monster_skill += n_players * 0.05;
555 if(monster_skill < 1) monster_skill = 1;
557 if(totalmonsters < 1) totalmonsters = ((autocvar_g_td_monster_count > 0) ? autocvar_g_td_monster_count : 10);
559 monsters_total = totalmonsters;
562 print(strcat(buildmsg, healmsg, countmsg, startmsg, "\n"));
564 queue_monsters(totalmonsters);
568 if(autocvar_sv_eventlog)
569 GameLogEcho(sprintf(":buildphase:%d:%d", wave_count, totalmonsters));
571 self.think = combat_phase_announce;
572 self.nextthink = time + build_time - 6;
575 void wave_end(float starting)
578 FOR_EACH_PLAYER(tail)
581 Send_CSQC_Centerprint_Generic(tail, CPID_KH_MSG, "Defend the generator from waves of monsters!", 0, 0);
583 Send_CSQC_Centerprint_Generic(tail, CPID_KH_MSG, ((wave_count >= max_waves) ? "Level victory!" : "Wave victory!"), 0, 0);
588 print((wave_count >= max_waves) ? "^2Level victory!\n" : "^2Wave victory!\n");
589 if(autocvar_sv_eventlog)
590 GameLogEcho(sprintf(":wave:%d:victory", wave_count));
593 if(wave_count >= max_waves)
601 if(autocvar_g_td_monsters_skill_start)
602 monster_skill = autocvar_g_td_monsters_skill_start;
607 self.think = build_phase;
608 self.nextthink = time + 3;
613 ScoreInfo_SetLabel_PlayerScore(SP_TD_SCORE, "score", SFL_SORT_PRIO_PRIMARY);
614 ScoreInfo_SetLabel_PlayerScore(SP_TD_KILLS, "kills", SFL_LOWER_IS_BETTER);
615 ScoreInfo_SetLabel_PlayerScore(SP_TD_TURKILLS, "frags", SFL_LOWER_IS_BETTER);
616 ScoreInfo_SetLabel_PlayerScore(SP_TD_DEATHS, "deaths", SFL_LOWER_IS_BETTER);
617 ScoreInfo_SetLabel_PlayerScore(SP_TD_SUICIDES, "suicides", SFL_LOWER_IS_BETTER | SFL_ALLOW_HIDE);
618 ScoreRules_basics_end();
621 void td_SpawnController()
623 entity oldself = self;
625 self.classname = "td_controller";
626 spawnfunc_td_controller();
630 void td_DelayedInit()
632 if(find(world, classname, "td_controller") == world)
634 print("No ""td_controller"" entity found on this map, creating it anyway.\n");
635 td_SpawnController();
643 InitializeEntity(world, td_DelayedInit, INITPRIO_GAMETYPE);
646 MUTATOR_HOOKFUNCTION(td_TurretValidateTarget)
648 if(turret_flags & TFL_TARGETSELECT_MISSILESONLY)
649 if(turret_target.flags & FL_PROJECTILE)
650 if(turret_target.owner.flags & FL_MONSTER)
651 return TRUE; // flac support
653 if(turret.turrcaps_flags & TFL_TURRCAPS_SUPPORT && turret_target.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
655 if not(turret_target.flags & FL_MONSTER)
656 turret_target = world;
661 MUTATOR_HOOKFUNCTION(td_PlayerThink)
663 self.stat_current_wave = wave_count;
664 self.stat_totalwaves = max_waves;
669 MUTATOR_HOOKFUNCTION(td_PlayerSpawn)
671 self.bot_attack = FALSE;
675 MUTATOR_HOOKFUNCTION(td_PlayerDies)
677 if(frag_attacker.flags & FL_MONSTER)
678 PlayerScore_Add(frag_target, SP_TD_DEATHS, 1);
680 if(frag_target == frag_attacker)
681 PlayerScore_Add(frag_attacker, SP_TD_SUICIDES, 1);
686 MUTATOR_HOOKFUNCTION(td_GiveFragsForKill)
690 return TRUE; // no frags counted in td
693 MUTATOR_HOOKFUNCTION(td_PlayerDamage_Calculate)
695 if(frag_attacker.realowner == frag_target)
698 if(frag_target.flags & FL_MONSTER && time < frag_target.spawnshieldtime)
701 if(frag_target.vehicle_flags & VHF_ISVEHICLE && !(frag_attacker.flags & FL_MONSTER))
704 if(frag_attacker.vehicle_flags & VHF_ISVEHICLE && !(frag_target.flags & FL_MONSTER))
707 if(!autocvar_g_td_pvp && frag_attacker != frag_target && frag_target.classname == STR_PLAYER && frag_attacker.classname == STR_PLAYER)
710 if(frag_attacker.turrcaps_flags & TFL_TURRCAPS_ISTURRET && frag_target.classname == STR_PLAYER)
713 if((frag_target.turrcaps_flags & TFL_TURRCAPS_ISTURRET) && !(frag_attacker.flags & FL_MONSTER || frag_attacker.turrcaps_flags & TFL_TURRCAPS_SUPPORT))
719 MUTATOR_HOOKFUNCTION(td_TurretDies)
722 self.realowner.turret_cnt -= 1;
727 MUTATOR_HOOKFUNCTION(td_MonsterCheckBossFlag)
729 // No minibosses in tower defense
733 MUTATOR_HOOKFUNCTION(td_MonsterMove)
737 FOR_EACH_PLAYER(player) { ++n_players; }
739 if(n_players < 1) // no players online, so do nothing
741 monster_target = world;
742 monster_speed_run = monster_speed_walk = 0;
746 if((vlen(self.goalentity.origin - self.origin) <= 100 && self.goalentity.classname == "td_waypoint") || (vlen(self.goalentity.origin - self.origin) <= 200 && self.flags & FL_FLY && self.goalentity.classname == "td_waypoint"))
748 self.target = self.goalentity.target;
749 self.goalentity = find(world, targetname, self.target);
752 if(self.goalentity == world)
753 self.goalentity = PickGenerator();
755 monster_speed_run = m_speed_run * monster_skill;
756 monster_speed_walk = m_speed_walk * monster_skill;
761 MUTATOR_HOOKFUNCTION(td_MonsterSpawn)
763 if(self.realowner && self.realowner.flags & FL_CLIENT)
765 sprint(self.realowner, "You can't spawn monsters in Tower Defense mode. Removed monster.\n");
767 WaypointSprite_Kill(self.sprite);
772 if(self.realowner == world) // nothing spawned it, so kill it
775 WaypointSprite_Kill(self.sprite);
780 current_monsters += 1;
782 self.spawnshieldtime = time + autocvar_g_td_monsters_spawnshield_time;
784 self.drop_size = self.health * 0.05;
786 if(self.drop_size < 1) self.drop_size = 1;
788 if(self.target) // follow target if available
789 self.goalentity = find(world, targetname, self.target);
791 self.origin += '0 0 25'; // hopefully this fixes monsters falling through the floor
793 switch(self.classname)
795 case "monster_knight": n_knights -= 1; break;
796 case "monster_dog": n_dogs -= 1; break;
797 case "monster_ogre": n_ogres -= 1; break;
798 case "monster_shambler": n_shamblers -= 1; AnnounceSpawn("Shambler"); break;
799 case "monster_wizard": n_wizards -= 1; break;
800 case "monster_shalrath": n_shalraths -= 1; break;
801 case "monster_soldier": n_soldiers -= 1; break;
802 case "monster_hellknight": n_hknights -= 1; break;
803 case "monster_enforcer": n_enforcers -= 1; break;
804 case "monster_demon": n_demons -= 1; break;
805 case "monster_zombie": n_zombies -= 1; break;
806 case "monster_spider": n_spiders -= 1; break;
807 case "monster_tarbaby": n_tarbabies -= 1; break;
813 MUTATOR_HOOKFUNCTION(td_MonsterDies)
819 current_monsters -= 1;
820 monsters_killed += 1;
822 if(frag_attacker.classname == STR_PLAYER)
824 PlayerScore_Add(frag_attacker, SP_TD_SCORE, autocvar_g_td_kill_points);
825 PlayerScore_Add(frag_attacker, SP_TD_KILLS, 1);
827 else if(frag_attacker.realowner.classname == STR_PLAYER)
829 PlayerScore_Add(frag_attacker.realowner, SP_TD_SCORE, autocvar_g_td_turretkill_points);
830 PlayerScore_Add(frag_attacker.realowner, SP_TD_TURKILLS, 1);
833 backuporigin = self.origin;
838 setorigin(self, backuporigin + '0 0 5');
839 spawn_td_fuel(oldself.drop_size);
840 self.touch = M_Item_Touch;
846 SUB_SetFade(self, time + 5, 1);
853 MUTATOR_HOOKFUNCTION(td_MonsterFindTarget)
859 FOR_EACH_PLAYER(player) { ++n_players; }
861 if(n_players < 1) // no players online, so do nothing
867 for(e = world;(e = findflags(e, monster_attack, TRUE)); )
869 if(monster_isvalidtarget(e, self, FALSE))
870 if((vlen(trace_endpos - self.origin) < 100 && e.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (vlen(trace_endpos - self.origin) < 200 && e.classname != "td_generator") || (vlen(trace_endpos - self.origin) < 500 && e.classname == "td_generator"))
879 MUTATOR_HOOKFUNCTION(td_SetStartItems)
881 start_ammo_fuel = 150; // to be nice...
886 MUTATOR_HOOKFUNCTION(td_TurretSpawn)
888 if(self.realowner == world)
889 return TRUE; // wasn't spawned by a player
891 self.bot_attack = FALSE;
892 self.turret_buff = 1;
897 MUTATOR_HOOKFUNCTION(td_DisableVehicles)
899 // you shall not spawn!
903 MUTATOR_HOOKFUNCTION(td_PlayerCommand)
905 if(MUTATOR_RETURNVALUE) { return FALSE; } // command was already handled?
906 makevectors(self.v_angle);
907 WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self);
908 if(cmd_name == "turretspawn")
910 if(argv(1) == "list")
912 sprint(self, "Available turrets:\n");
913 sprint(self, "^3mlrs walker plasma towerbuff\n");
916 if(self.classname != STR_PLAYER || self.health <= 0)
918 sprint(self, "Can't spawn turrets while spectating/dead\n");
921 if(self.turret_cnt >= max_turrets)
923 sprint(self, strcat("Can't spawn more than ", ftos(max_turrets), " turrets\n"));
930 if(self.ammo_fuel < autocvar_g_td_turret_plasma_cost) break;
931 self.ammo_fuel -= autocvar_g_td_turret_plasma_cost;
932 spawnturret(self, self, "plasma", trace_endpos);
933 sprint(self, "Spawned 1 plasma turret", "\n");
938 if(self.ammo_fuel < autocvar_g_td_turret_mlrs_cost) break;
939 self.ammo_fuel -= autocvar_g_td_turret_mlrs_cost;
940 spawnturret(self, self, "mlrs", trace_endpos);
941 sprint(self, "Spawned 1 MLRS turret", "\n");
946 if(self.ammo_fuel < autocvar_g_td_turret_flac_cost) break;
947 self.ammo_fuel -= autocvar_g_td_turret_flac_cost;
948 spawnturret(self, self, "flac", trace_endpos);
949 sprint(self, "Spawned 1 FLAC Cannon", "\n");
954 if(self.ammo_fuel < autocvar_g_td_turret_walker_cost) break;
955 self.ammo_fuel -= autocvar_g_td_turret_walker_cost;
956 spawnturret(self, self, "walker", trace_endpos);
957 sprint(self, "Spawned 1 walker turret", "\n");
962 if(self.ammo_fuel < autocvar_g_td_tower_buff_cost) break;
963 self.ammo_fuel -= autocvar_g_td_tower_buff_cost;
964 spawnturret(self, self, "fusionreactor", trace_endpos);
965 sprint(self, "Spawned 1 tower buff turret\n");
970 sprint(self, "Invalid turret. type 'cmd turret list' to see a list of all available turrets.\n");
974 sprint(self, strcat("You do not have enough fuel to spawn a ", argv(1), " turret\n"));
977 if(cmd_name == "buffturret")
979 if(trace_ent.realowner != self || !(trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET))
981 sprint(self, "You need to aim at your turret to upgrade it\n");
984 if(self.ammo_fuel < autocvar_g_td_turret_upgrade_cost)
986 sprint(self, strcat("You need ", ftos(autocvar_g_td_turret_upgrade_cost), " fuel to increase this turret's power\n"));
989 if(trace_ent.turret_buff >= 3)
991 sprint(self, "This turret cannot be buffed up any higher\n");
995 self.ammo_fuel -= autocvar_g_td_turret_upgrade_cost;
996 trace_ent.SendFlags |= TNSF_STATUS;
997 buffturret(trace_ent, 1.2);
998 sprint(self, "Turret power increased by 20%!\n");
1002 if(cmd_name == "turretremove")
1004 if((trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET) && trace_ent.realowner == self)
1006 self.turret_cnt -= 1;
1007 sprint(self, strcat("You removed your ", trace_ent.netname, "\n"));
1008 remove(trace_ent.tur_head);
1012 sprint(self, "You need to aim at your turret to remove it\n");
1015 if(cmd_name == "debugmonsters")
1017 sprint(self, strcat("^3Current wave: ^1", ftos(wave_count), "\n"));
1018 sprint(self, strcat("^3Maximum waves: ^1", ftos(max_waves), "\n"));
1019 sprint(self, strcat("^3Monster skill: ^1", ftos(monster_skill), "\n"));
1020 sprint(self, strcat("^3Current monsters: ^1", ftos(monster_count), "\n"));
1021 sprint(self, strcat("^3Maximum monsters: ^1", ftos(totalmonsters), "\n"));
1022 sprint(self, strcat("^3Current ogres: ^1", ftos(n_ogres), "\n"));
1023 sprint(self, strcat("^3Current knights: ^1", ftos(n_knights), "\n"));
1024 sprint(self, strcat("^3Current dogs: ^1", ftos(n_dogs), "\n"));
1025 sprint(self, strcat("^3Current shamblers: ^1", ftos(n_shamblers), "\n"));
1026 sprint(self, strcat("^3Current scrags: ^1", ftos(n_wizards), "\n"));
1027 sprint(self, strcat("^3Current vores: ^1", ftos(n_shalraths), "\n"));
1028 sprint(self, strcat("^3Current grunts: ^1", ftos(n_soldiers), "\n"));
1029 sprint(self, strcat("^3Current hell knights: ^1", ftos(n_hknights), "\n"));
1030 sprint(self, strcat("^3Current enforcers: ^1", ftos(n_enforcers), "\n"));
1031 sprint(self, strcat("^3Current fiends: ^1", ftos(n_demons), "\n"));
1032 sprint(self, strcat("^3Current zombies: ^1", ftos(n_zombies), "\n"));
1033 sprint(self, strcat("^3Current spawns: ^1", ftos(n_tarbabies), "\n"));
1034 sprint(self, strcat("^3Current rotfish: ^1", ftos(n_fish), "\n"));
1035 sprint(self, strcat("^3Current spiders: ^1", ftos(n_spiders), "\n"));
1042 MUTATOR_DEFINITION(gamemode_td)
1044 MUTATOR_HOOK(MonsterSpawn, td_MonsterSpawn, CBC_ORDER_ANY);
1045 MUTATOR_HOOK(MonsterDies, td_MonsterDies, CBC_ORDER_ANY);
1046 MUTATOR_HOOK(MonsterMove, td_MonsterMove, CBC_ORDER_ANY);
1047 MUTATOR_HOOK(MonsterFindTarget, td_MonsterFindTarget, CBC_ORDER_ANY);
1048 MUTATOR_HOOK(MonsterCheckBossFlag, td_MonsterCheckBossFlag, CBC_ORDER_ANY);
1049 MUTATOR_HOOK(SetStartItems, td_SetStartItems, CBC_ORDER_ANY);
1050 MUTATOR_HOOK(TurretValidateTarget, td_TurretValidateTarget, CBC_ORDER_ANY);
1051 MUTATOR_HOOK(TurretSpawn, td_TurretSpawn, CBC_ORDER_ANY);
1052 MUTATOR_HOOK(TurretDies, td_TurretDies, CBC_ORDER_ANY);
1053 MUTATOR_HOOK(GiveFragsForKill, td_GiveFragsForKill, CBC_ORDER_ANY);
1054 MUTATOR_HOOK(PlayerPreThink, td_PlayerThink, CBC_ORDER_ANY);
1055 MUTATOR_HOOK(PlayerDies, td_PlayerDies, CBC_ORDER_ANY);
1056 MUTATOR_HOOK(PlayerDamage_Calculate, td_PlayerDamage_Calculate, CBC_ORDER_ANY);
1057 MUTATOR_HOOK(PlayerSpawn, td_PlayerSpawn, CBC_ORDER_ANY);
1058 MUTATOR_HOOK(VehicleSpawn, td_DisableVehicles, CBC_ORDER_ANY);
1059 MUTATOR_HOOK(SV_ParseClientCommand, td_PlayerCommand, CBC_ORDER_ANY);
1063 if(time > 1) // game loads at time 1
1064 error("This is a game type and it cannot be added at runtime.");
1065 cvar_settemp("g_monsters", "1");
1066 cvar_settemp("g_turrets", "1");
1072 error("This is a game type and it cannot be removed at runtime.");