]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Begin converting tower defense to a teamplay mode
authorMario <mario.mario@y7mail.com>
Fri, 17 May 2013 14:40:17 +0000 (00:40 +1000)
committerMario <mario.mario@y7mail.com>
Fri, 17 May 2013 14:40:17 +0000 (00:40 +1000)
effectinfo.txt
qcsrc/common/mapinfo.qc
qcsrc/server/autocvars.qh
qcsrc/server/g_world.qc
qcsrc/server/generator.qc
qcsrc/server/monsters/lib/monsters.qc
qcsrc/server/monsters/lib/spawn.qc
qcsrc/server/mutators/gamemode_towerdefense.qc
qcsrc/server/mutators/gamemode_towerdefense.qh
qcsrc/server/mutators/mutators.qh
qcsrc/server/teamplay.qc

index c2f702fa5ab243ac7d032a8583bb07e0c8d82570..05e881c5da6b5e9d9adc122128fe586d0d0dc681 100644 (file)
@@ -7977,3 +7977,43 @@ size 100 100
 alpha 190 190 180
 sizeincrease -80
 color 0xFFFFFF 0xFFFFFF
+
+// waypoint_link_red - red waypoint linking effect
+effect waypoint_link_red
+countabsolute 1
+type beam
+tex 200 200
+size 1 1
+alpha 256 256 64
+color 0xFF0F0F 0xFF0F0F
+sizeincrease 1
+
+// waypoint_link_blue - blue waypoint linking effect
+effect waypoint_link_blue
+countabsolute 1
+type beam
+tex 200 200
+size 1 1
+alpha 256 256 64
+color 0x0F0FFF 0x0F0FFF
+sizeincrease 1
+
+// waypoint_link_yellow - yellow waypoint linking effect
+effect waypoint_link_yellow
+countabsolute 1
+type beam
+tex 200 200
+size 1 1
+alpha 256 256 64
+color 0xFFFF0F 0xFFFF0F
+sizeincrease 1
+
+// waypoint_link_pink - pink waypoint linking effect
+effect waypoint_link_pink
+countabsolute 1
+type beam
+tex 200 200
+size 1 1
+alpha 256 256 64
+color 0xFF0FFF 0xFF0FFF
+sizeincrease 1
index 97ee8cc9bb7cb891fa7791c7e75f5ccbe04611aa..bc557a73ddb90686fcc59be09965c180f6bc0f94 100644 (file)
@@ -313,7 +313,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                                        MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
                                else if(v == "team_CTF_blueflag")
                                        MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
-                               else if(v == "td_generator" || v == "monster_swarm")
+                               else if(v == "td_generator" || v == "td_spawnpoint")
                                        MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TD;
                                else if(v == "target_assault_roundend")
                                        MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
index 8784af9f9c79482677c8a78b7a2c9ae6ba7cd806..4457c603aa7ce2a68b3d9e9751f39ad102ee7de4 100644 (file)
@@ -1230,30 +1230,6 @@ float autocvar_physics_ode;
 float autocvar_g_physical_items;
 float autocvar_g_physical_items_damageforcescale;
 float autocvar_g_physical_items_reset;
-float autocvar_g_td_start_wave;
-float autocvar_g_td_generator_health;
-float autocvar_g_td_current_monsters;
-float autocvar_g_td_generator_damaged_points;
-float autocvar_g_td_monster_count;
-float autocvar_g_td_monster_count_increment;
-float autocvar_g_td_buildphase_time;
-float autocvar_g_td_pvp;
-float autocvar_g_td_max_waves;
-float autocvar_g_td_kill_points;
-float autocvar_g_td_turretkill_points;
-float autocvar_g_td_generator_dontend;
-float autocvar_g_td_force_settings;
-float autocvar_g_td_turret_max;
-float autocvar_g_td_monsters_skill_start;
-float autocvar_g_td_monsters_skill_increment;
-float autocvar_g_td_monsters_speed_walk;
-float autocvar_g_td_monsters_speed_run;
-float autocvar_g_td_monsters_spawn_delay;
-float autocvar_g_td_monsters_spawnshield_time;
-float autocvar_g_td_monsters_ignore_turrets;
-float autocvar_g_td_turret_upgrade_cost;
-float autocvar_g_td_turret_repair_cost;
-float autocvar_g_td_barricade_damage;
 float autocvar_g_monsters;
 float autocvar_g_monsters_think_delay;
 float autocvar_g_monsters_max;
@@ -1283,3 +1259,4 @@ float autocvar_g_touchexplode_radius;
 float autocvar_g_touchexplode_damage;
 float autocvar_g_touchexplode_edgedamage;
 float autocvar_g_touchexplode_force;
+float autocvar_g_td_debug;
index db5b1d44fb94a5770bdaf1374759b1fc4dcc5227..cf2d12612e27e0a648de1d32fcd1df620a618ee9 100644 (file)
@@ -2072,36 +2072,6 @@ float WinningCondition_RanOutOfSpawns()
                return WINNING_NO;
 }
 
-// TD winning condition:
-// game terminates if there are no generators (or 1 dies if td_dont_end is FALSE)
-float gensurvived;
-float WinningCondition_TowerDefense()
-{
-       WinningConditionHelper(); // set worldstatus
-
-       if(inWarmupStage)
-               return WINNING_NO;
-
-       // first check if the game has ended
-       if(gendestroyed == TRUE)
-       if(td_gencount < 1 || !td_dont_end)
-       {
-               ClearWinners();
-               dprint("Everyone lost, ending game.\n");
-               return WINNING_YES;
-       }
-       
-       if(gensurvived)
-       {
-               ClearWinners();
-               checkrules_equality = TRUE;
-               return WINNING_YES;
-       }
-
-       // Two or more teams remain
-       return WINNING_NO;
-}
-
 /*
 ============
 CheckRules_World
@@ -2257,10 +2227,6 @@ void CheckRules_World()
        {
                checkrules_status = WinningCondition_Onslaught(); // TODO remove this?
        }
-       else if(g_td)
-       {
-               checkrules_status = WinningCondition_TowerDefense();
-       }
        else
        {
                checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
index 5bc3d119e51cf5837e735df9a09e6b6053d4a9b8..1588aef6b61e040d3681ba27c52b19bb5421168c 100644 (file)
@@ -237,7 +237,7 @@ void generator_damage(float hp)
 }
 
 void generator_construct()
-{      
+{
        self.netname = "Generator";
 
        setorigin(self, self.origin);
index a42ac1de41f6bbdf751e665268ef7672ac4dfd89..06f9d41879e48b7113f7ef590c886abf07d4a56c 100644 (file)
@@ -446,7 +446,7 @@ vector monster_pickmovetarget(entity targ)
                {
                        self.monster_movestate = MONSTER_MOVE_OWNER;
                        self.last_trace = time + 0.3;
-                       if(self.monster_owner && self.monster_owner.classname != "monster_swarm")
+                       if(self.monster_owner && self.monster_owner.classname != "td_spawnpoint")
                                return self.monster_owner.origin;
                }
                case MONSTER_MOVE_SPAWNLOC:
index 2912f0ad7ebdd90abc41ab44f45131f313225efa..a756fad11b134b7eb960c624668b239c08b3a764 100644 (file)
@@ -33,8 +33,11 @@ entity spawnmonster (string monster, entity spawnedby, entity own, vector orig,
        if(moveflag)
                e.monster_moveflags = moveflag;
        
-       if (spawnedby.classname == "monster_swarm")
-               e.monster_owner = own;  
+       if (spawnedby.classname == "td_spawnpoint")
+       {
+               e.monster_owner = own;
+               e.team = spawnedby.team;
+       }
        else if(IS_PLAYER(spawnedby))
        {
                if(teamplay && autocvar_g_monsters_teams)
index 970373a84fbe4cad8d8fc0b9d6e0a2e30b71a03f..fd178d29eeaaa8780b6776a7d40e07517d2faaf4 100644 (file)
@@ -1,60 +1,76 @@
-// Tower Defense
-// Gamemode by Mario
+float redalive, bluealive, total_alive;
 
-float td_moncount[MONSTER_LAST];
-void spawnfunc_td_controller()
+var float max_monsters = 20;
+var float max_alive = 10;
+
+float total_killed;
+
+var float max_turrets = 3;
+
+.float newfuel; // hack to not give players fuel every time they spawn
+
+float last_check;
+
+.float turret_cnt;
+
+.float level;
+.float last_trace;
+
+void td_debug(string input)
 {
-       if not(g_td) { remove(self); return; }
-       
-       if(autocvar_g_td_force_settings)
+       switch(autocvar_g_td_debug)
+       {
+               case 1: dprint(input); break;
+               case 2: print(input); break;
+       }
+}
+
+void td_waypoint_link(float tm, vector from, vector to)
+{
+       switch(tm)
+       {
+               case NUM_TEAM_1:
+                       WarpZone_TrailParticles(world, particleeffectnum("waypoint_link_red"), from, to);
+                       break;
+               case NUM_TEAM_2:
+                       WarpZone_TrailParticles(world, particleeffectnum("waypoint_link_blue"), from, to);
+                       break;
+       }
+}
+
+void td_waypoint_think()
+{
+       if(gameover)
        {
-               // TODO: find a better way to do this?
-               self.dontend = FALSE;
-               self.maxwaves = 0;
-               self.monstercount = 0;
-               self.startwave = 0;
-               self.maxturrets = 0;
-               self.buildtime = 0;
-               self.mspeed_walk = 0;
-               self.mspeed_run = 0;
-               self.spawndelay = 0;
-               self.maxcurrent = 0;
-               self.ignoreturrets = 0;
+               remove(self);
+               return;
        }
+       
+       if(time >= self.last_trace)
+       {
+               entity e, e2, e3;
                
-       self.netname = "Tower Defense controller entity";
-       self.classname = "td_controller";
+               e = find(world, targetname, self.target);
+               e2 = find(world, target, self.targetname);
+               e3 = find(world, targetname, self.target2);
                
-       gensurvived = FALSE;
-       
-       td_dont_end = ((self.dontend == 0) ? autocvar_g_td_generator_dontend : self.dontend);
-       max_waves = ((self.maxwaves == 0) ? autocvar_g_td_max_waves : self.maxwaves);
-       totalmonsters = ((self.monstercount == 0) ? autocvar_g_td_monster_count : self.monstercount);
-       wave_count = ((self.startwave == 0) ? autocvar_g_td_start_wave : self.startwave);
-       max_turrets = ((self.maxturrets == 0) ? autocvar_g_td_turret_max : self.maxturrets);
-       build_time = ((self.buildtime == 0) ? autocvar_g_td_buildphase_time : self.buildtime);
-       m_speed_walk = ((self.mspeed_walk == 0) ? autocvar_g_td_monsters_speed_walk : self.mspeed_walk);
-       m_speed_run = ((self.mspeed_run == 0) ? autocvar_g_td_monsters_speed_run : self.mspeed_run);
-       spawn_delay = ((self.spawndelay == 0) ? autocvar_g_td_monsters_spawn_delay : self.spawndelay);
-       max_current = ((self.maxcurrent == 0) ? autocvar_g_td_current_monsters : self.maxcurrent);
-       ignore_turrets = ((self.ignoreturrets == 0) ? autocvar_g_td_monsters_ignore_turrets : self.ignoreturrets);
-       
-       if(autocvar_g_td_monsters_skill_start)
-               monster_skill = autocvar_g_td_monsters_skill_start;
+               if(e.classname == "td_waypoint" || e.flags & FL_GENERATOR)
+                       td_waypoint_link(self.team, self.origin, e.origin);
+               if(e2.classname == "td_spawnpoint")
+                       td_waypoint_link(self.team, self.origin, e2.origin);
+               if(e3.classname == "td_waypoint" || e3.flags & FL_GENERATOR)
+                       td_waypoint_link(self.team, self.origin, e3.origin);
                
-       wave_end(TRUE);
+               self.last_trace = time + 0.5;
+       }
+       
+       self.nextthink = time + 0.1;
 }
 
 void td_generator_die() 
 {
        if(autocvar_sv_eventlog)
                GameLogEcho(":gendestroyed");
-               
-       gendestroyed = TRUE;
-       
-       pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1);
-       sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
        
        Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_TD_GENDESTROYED);
        
@@ -62,7 +78,6 @@ void td_generator_die()
        self.takedamage         = DAMAGE_NO;
        self.event_damage   = func_null;
        self.enemy                      = world;
-       td_gencount                     -= 1;
        
        WaypointSprite_Kill(self.sprite);
 }
@@ -72,10 +87,12 @@ void td_generator_damage(entity inflictor, entity attacker, float damage, float
        if(IS_PLAYER(attacker) || attacker.turrcaps_flags & TFL_TURRCAPS_ISTURRET || attacker.vehicle_flags & VHF_ISVEHICLE || self.takedamage == DAMAGE_NO)
                return;
                
+       entity head;
+               
        if (time > self.pain_finished)
        {
                self.pain_finished = time + 10;
-               play2all("onslaught/generator_underattack.wav");
+               play2team(self.team, "onslaught/generator_underattack.wav");
        }
        
        if (random() < 0.5)
@@ -83,14 +100,24 @@ void td_generator_damage(entity inflictor, entity attacker, float damage, float
        else
                spamsound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);
        
-       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_TD_GENDAMAGED);
+       
+       FOR_EACH_REALPLAYER(head)
+       if(!IsDifferentTeam(head, self))
+               Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_TD_GENDAMAGED);
        
        self.health -= damage;
        
        WaypointSprite_UpdateHealth(self.sprite, self.health);
                
-       if(self.health <= 0) 
+       if(self.health <= 0)
+       {
+               FOR_EACH_REALPLAYER(head)
+               if(!IsDifferentTeam(head, attacker))
+                       PlayerScore_Add(head, SP_TD_DESTROYS, 1);
+               
+               TeamScore_AddToTeam(attacker.team, ST_TD_DESTROYS, 1);
                td_generator_die();
+       }
                
        self.SendFlags |= GSF_STATUS;
 }
@@ -99,164 +126,119 @@ void td_generator_setup()
 {
        self.think                      = func_null;
        self.nextthink          = -1;
-       self.solid                  = SOLID_BBOX;
-       self.takedamage     = DAMAGE_AIM;
-       self.event_damage   = td_generator_damage;
-       self.enemy                  = world;
-       self.movetype       = MOVETYPE_NONE;
-       self.monster_attack = TRUE;
+       self.solid                      = SOLID_BBOX;
+       self.takedamage         = DAMAGE_AIM;
+       self.event_damage       = td_generator_damage;
+       self.movetype           = MOVETYPE_NONE;
+       self.monster_attack     = TRUE;
+       self.SendFlags          = GSF_SETUP;
        self.netname            = "Generator";
-       self.SendFlags          = GSF_SETUP;
+       self.SendFlags     |= GSF_STATUS;
        
-       WaypointSprite_SpawnFixed(self.netname, self.origin + '0 0 90', self, sprite, RADARICON_OBJECTIVE, '1 0.5 0');  
+       WaypointSprite_SpawnFixed(self.netname, self.origin + '0 0 90', self, sprite, RADARICON_OBJECTIVE, Team_ColorRGB(self.team));   
        WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
        WaypointSprite_UpdateHealth(self.sprite, self.health);
 }
 
-void spawnfunc_td_generator() 
+void AnnounceSpawn(string anounce)
 {
-       if not(g_td) { remove(self); return; }
-       
-       precache_sound("onslaught/generator_underattack.wav");
-       precache_sound("onslaught/ons_hit1.wav");
-       precache_sound("onslaught/ons_hit2.wav");
-       precache_sound("weapons/rocket_impact.wav");
-       
-       gendestroyed = FALSE;
-       
-       if not(self.health)
-               self.health = autocvar_g_td_generator_health;
-               
-       self.max_health = self.health;
-       
-       self.classname = "td_generator";
-       self.flags = FL_GENERATOR;
-       td_gencount += 1;
-       
-       setsize(self, GENERATOR_MIN, GENERATOR_MAX);
-       
-       setorigin(self, self.origin + '0 0 20');
-       droptofloor();
+       entity e;
+       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_TD_ANNOUNCE_SPAWN, anounce);
        
-       generator_link(td_generator_setup);
-}
-
-entity PickGenerator()
-{
-       entity generator, head;
-       if(td_gencount == 1)
-               generator = findflags(world, flags, FL_GENERATOR);
-       else
-       {
-               RandomSelection_Init();
-               for(head = world;(head = findflags(head, flags, FL_GENERATOR)); )
-               {
-                       RandomSelection_Add(head, 0, string_null, 1, 1);
-               }
-               generator = RandomSelection_chosen_ent; 
-       }
-       return generator;
+       FOR_EACH_REALCLIENT(e) soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTN_NONE);
 }
 
-void spawn_td_fuel(float fuel_size)
+entity PickSpawn (float tm)
 {
-       if not(g_td) {remove(self); return; }
-       
-       self.ammo_fuel = fuel_size * monster_skill;
-       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);
-       
-       self.velocity = randomvec() * 175 + '0 0 325';
-}
+       entity e;
+       RandomSelection_Init();
+       for(e = world;(e = find(e, classname, "td_spawnpoint")); )
+       if(e.team == tm)
+               RandomSelection_Add(e, 0, string_null, 1, 1);
 
-void spawnfunc_td_waypoint() 
-{
-       if not(g_td) { remove(self); return; }
-       
-       self.classname = "td_waypoint";
+       return RandomSelection_chosen_ent;
 }
 
-void spawnfunc_monster_swarm()
+void TD_SpawnMonster(float tm, string mnster)
 {
-       if not(g_td) { remove(self); return; }
-       
-       self.flags = SWARM_NORMAL; // marked as a spawnpoint
-       self.classname = "monster_swarm";
-       
-       if(self.spawntype == SWARM_SWIM) waterspawns_count += 1;
-       if(self.spawntype == SWARM_FLY) flyspawns_count += 1;
-       
-       WaypointSprite_SpawnFixed("Monsters", self.origin + '0 0 60', self, sprite, RADARICON_HERE, '0 0 1');
+       entity e, mon;
        
-       if(self.target == "")
-               dprint("Warning: monster_swarm entity without a set target\n");
+       e = PickSpawn(tm);
+               
+       if(e == world)
+       {
+               td_debug("Warning: couldn't find any td_spawnpoint spawnpoints, no monsters will spawn!\n");
+               return;
+       }
+  
+       mon = spawnmonster(mnster, e, e, e.origin, FALSE, 2);
+       if(e.target2)
+       {
+               if(random() <= 0.5 && e.target)
+                       mon.target2 = e.target;
+               else
+                       mon.target2 = e.target2;
+       }
+       else
+               mon.target2 = e.target;
 }
 
-void barricade_touch()
+string monster_type2string(float mnster)
 {
-       if not(other.flags & FL_MONSTER)
-               return;
-               
-       if(time < self.dmg_time)
-               return;
-               
-       Damage(other, self, self, autocvar_g_td_barricade_damage, DEATH_HURTTRIGGER, self.origin, '0 0 0');
-       
-       self.dmg_time = time + 1;
+       switch(mnster)
+       {
+               case MONSTER_ZOMBIE: return "zombie";
+               case MONSTER_BRUTE: return "brute";
+               case MONSTER_ANIMUS: return "animus";
+               case MONSTER_SHAMBLER: return "shambler";
+               case MONSTER_BRUISER: return "bruiser";
+               case MONSTER_WYVERN: return "wyvern";
+               case MONSTER_CERBERUS: return "cerberus";
+               case MONSTER_SLIME: return "slime";
+               case MONSTER_KNIGHT: return "knight";
+               case MONSTER_STINGRAY: return "stingray";
+               case MONSTER_MAGE: return "mage";
+               case MONSTER_SPIDER: return "spider";
+               default: return "";
+       }
 }
 
-void barricade_die()
+float RandomMonster()
 {
-       self.takedamage = DAMAGE_NO;
-       self.event_damage = func_null;
-       
-       WaypointSprite_Kill(self.sprite);
+       RandomSelection_Init();
        
-       pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1);
-       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+       float i;
        
-       if(self.realowner)
-               self.realowner.turret_cnt -= 1;
+       for(i = MONSTER_FIRST + 1; i < MONSTER_LAST; ++i)
+       {
+               if(i == MONSTER_STINGRAY || i == MONSTER_WYVERN)
+                       continue; // flying/swimming monsters not yet supported
                
-       self.think = SUB_Remove;
-       self.nextthink = time;
+               RandomSelection_Add(world, i, "", 1, 1);
+       }
+       
+       return RandomSelection_chosen_float;
 }
 
-void barricade_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+void SpawnMonsters(float tm)
 {
-       if not(attacker.flags & FL_MONSTER) return;
+       float whichmon;
        
-       self.health -= damage;
-       
-       WaypointSprite_UpdateHealth(self.sprite, self.health);
+       whichmon = RandomMonster();
        
-       if(self.health < 1)
-               barricade_die();
+       TD_SpawnMonster(tm, monster_type2string(whichmon));
 }
 
-void spawn_barricade()
+entity PickGenerator(float tm)
 {
-       self.health = 2000;
-       self.max_health = self.health;
-       self.dmg_time = time;
-       self.touch = barricade_touch;
-       self.think = func_null;
-       self.nextthink = -1;
-       self.takedamage = DAMAGE_AIM;
-       self.turrcaps_flags = TFL_TURRCAPS_ISTURRET; // for turretremove commands etc.
-       self.solid = SOLID_CORPSE; // hax
-       self.event_damage = barricade_damage;
-       self.netname = "Barricade";
-       
-       WaypointSprite_Spawn(self.netname, 0, 1200, self, '0 0 110', world, 0, self, sprite, FALSE, RADARICON_DOMPOINT, '1 1 0');       
-       WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
-       WaypointSprite_UpdateHealth(self.sprite, self.health);
-       
-       precache_model("models/td/barricade.md3");
-       setmodel(self, "models/td/barricade.md3");
+       entity head;
        
-       droptofloor();
+       RandomSelection_Init();
+       for(head = world;(head = findflags(head, flags, FL_GENERATOR)); )
+       if(head.team != tm)
+               RandomSelection_Add(head, 0, string_null, 1, 1);
        
-       self.movetype = MOVETYPE_NONE;
+       return RandomSelection_chosen_ent;
 }
 
 float td_checkfuel(entity ent, string tur)
@@ -291,8 +273,7 @@ void spawnturret(entity spawnedby, entity own, string turet, vector orig)
        self.playerid = own.playerid;
        self.angles_y = spawnedby.v_angle_y;
        spawnedby.turret_cnt += 1;
-       self.colormap = spawnedby.colormap;
-       self.colormod = '1 1 1';
+       self.team = own.team;
        
        switch(turet)
        {
@@ -301,7 +282,6 @@ void spawnturret(entity spawnedby, entity own, string turet, vector orig)
                case "walker": spawnfunc_turret_walker(); break;
                case "flac": spawnfunc_turret_flac(); break;
                case "towerbuff": spawnfunc_turret_fusionreactor(); break;
-               case "barricade": spawn_barricade(); break;
                default: Send_Notification(NOTIF_ONE, spawnedby, MSG_INFO, INFO_TD_INVALID); remove(self); self = oldself; return;
        }
        
@@ -310,352 +290,185 @@ void spawnturret(entity spawnedby, entity own, string turet, vector orig)
        self = oldself;
 }
 
-void buffturret (entity tur, float buff)
+void spawn_td_fuel(float fuel_size)
 {
-       tur.turret_buff           += 1;
-       tur.max_health            *= buff;
-       tur.tur_health             = tur.max_health;
-       tur.health                         = tur.max_health;
-       tur.ammo_max              *= buff;
-       tur.ammo_recharge     *= buff;
-    tur.shot_dmg          *= buff;
-    tur.shot_refire       -= buff * 0.2;
-    tur.shot_radius       *= buff;
-    tur.shot_speed        *= buff;
-    tur.shot_spread       *= buff;
-    tur.shot_force        *= buff;
+       if not(g_td) {remove(self); return; }
+       
+       self.ammo_fuel = fuel_size * monster_skill;
+       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);
+       
+       self.velocity = randomvec() * 175 + '0 0 325';
 }
 
-void AnnounceSpawn(string anounce)
+// round handling
+#define TD_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0))
+#define TD_ALIVE_TEAMS_OK() (TD_ALIVE_TEAMS() == 2)
+void TD_RoundStart()
 {
-       entity e;
-       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_TD_ANNOUNCE_SPAWN, anounce);
-       
-       FOR_EACH_REALCLIENT(e) soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTN_NONE);
+       allowed_to_spawn = TRUE;
 }
 
-entity PickSpawn (float strngth, float type)
+void TD_count_alive_monsters()
 {
-       entity e;
-       RandomSelection_Init();
-       for(e = world;(e = find(e, classname, "monster_swarm")); )
+       entity head;
+       
+       total_alive = 0;
+       
+       FOR_EACH_MONSTER(head)
        {
-               if(flyspawns_count > 0 && type == SWARM_FLY && e.spawntype != SWARM_FLY) continue;
-               if(waterspawns_count > 0 && type == SWARM_SWIM && e.spawntype != SWARM_SWIM) continue;
+               if(head.health <= 0) continue;
                
-               RandomSelection_Add(e, 0, string_null, 1, 1);
+               ++total_alive;
+               
+               switch(head.team)
+               {
+                       case NUM_TEAM_1:
+                               ++redalive;
+                       case NUM_TEAM_2:
+                               ++bluealive;
+               }
        }
+}
 
-       return RandomSelection_chosen_ent;
+float TD_GetWinnerTeam()
+{
+       float winner_team = 0;
+       if(redalive >= 1)
+               winner_team = NUM_TEAM_1;
+       if(bluealive >= 1)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_2;
+       }
+       if(winner_team)
+               return winner_team;
+       return -1; // no monster left
 }
 
-void TD_SpawnMonster(string mnster, float strngth, float type)
+float TD_CheckWinner()
 {
-       entity e, mon;
+       if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+               round_handler_Init(5, 1, 180);
+               return 1;
+       }
        
-       e = PickSpawn(strngth, type);
+       TD_count_alive_monsters();
        
-       if(e == world) // couldn't find anything for our class, so check for normal spawns
-               e = PickSpawn(SWARM_NORMAL, SWARM_NORMAL);
-               
-       if(e == world)
+       if(total_alive < max_alive && time >= last_check && total_killed < max_monsters)
        {
-               dprint("Warning: couldn't find any monster_swarm spawnpoints, no monsters will spawn!\n");
-               return;
+               SpawnMonsters(NUM_TEAM_1);
+               SpawnMonsters(NUM_TEAM_2);
+               
+               last_check = time + 0.5;
        }
-  
-       mon = spawnmonster(mnster, e, e, e.origin, FALSE, 2);
-       if(e.target2)
+       
+       if(total_killed < 1)
+               return 0; // nothing has died, can't be a tie
+       
+       if(TD_ALIVE_TEAMS() > 1)
+               return 0;
+
+       float winner_team = TD_GetWinnerTeam();
+       if(winner_team > 0)
        {
-               if(random() <= 0.5 && e.target)
-                       mon.target2 = e.target;
-               else
-                       mon.target2 = e.target2;
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
+               TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
        }
-       else
-               mon.target2 = e.target;
-}
-
-float Monster_GetStrength(float mnster)
-{
-       switch(mnster)
+       else if(winner_team == -1)
        {
-               default:
-               case MONSTER_BRUISER:
-               case MONSTER_ZOMBIE:
-               case MONSTER_SPIDER:
-               case MONSTER_SLIME:
-               case MONSTER_CERBERUS:
-               case MONSTER_WYVERN:
-               case MONSTER_STINGRAY:
-                       return SWARM_WEAK;
-               case MONSTER_KNIGHT:
-               case MONSTER_BRUTE:
-               case MONSTER_SHAMBLER:
-               case MONSTER_MAGE:
-               case MONSTER_ANIMUS:
-                       return SWARM_STRONG;
-               default: return SWARM_NORMAL;
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
        }
+
+       round_handler_Init(5, 1, 180);
+       return 1;
 }
 
-string monster_type2string(float mnster)
+float TD_CheckTeams()
 {
-       switch(mnster)
-       {
-               case MONSTER_ZOMBIE: return "zombie";
-               case MONSTER_BRUTE: return "brute";
-               case MONSTER_ANIMUS: return "animus";
-               case MONSTER_SHAMBLER: return "shambler";
-               case MONSTER_BRUISER: return "bruiser";
-               case MONSTER_WYVERN: return "wyvern";
-               case MONSTER_CERBERUS: return "cerberus";
-               case MONSTER_SLIME: return "slime";
-               case MONSTER_KNIGHT: return "knight";
-               case MONSTER_STINGRAY: return "stingray";
-               case MONSTER_MAGE: return "mage";
-               case MONSTER_SPIDER: return "spider";
-               default: return "";
-       }
+       allowed_to_spawn = TRUE;
+       
+       return TRUE;
 }
 
-float Monster_GetType(float mnster)
+// spawnfuncs  
+void spawnfunc_td_generator()
 {
-       switch(mnster)
+       if not(g_td) { remove(self); return; }
+       if not(self.team)
        {
-               default:
-               case MONSTER_BRUISER:
-               case MONSTER_ZOMBIE:
-               case MONSTER_SPIDER:
-               case MONSTER_SLIME:
-               case MONSTER_CERBERUS:
-               case MONSTER_BRUTE:
-               case MONSTER_SHAMBLER:
-               case MONSTER_MAGE:
-               case MONSTER_KNIGHT:
-               case MONSTER_ANIMUS:
-                       return SWARM_NORMAL;
-               case MONSTER_WYVERN:
-                       return SWARM_FLY;
-               case MONSTER_STINGRAY:
-                       return SWARM_SWIM;
+               td_debug("Generator without a team, removing it.\n");
+               remove(self);
+               return;
        }
-}
-
-float RandomMonster()
-{
-       RandomSelection_Init();
        
-       float i;
+       precache_sound("onslaught/generator_underattack.wav");
+       precache_sound("onslaught/ons_hit1.wav");
+       precache_sound("onslaught/ons_hit2.wav");
+       precache_sound("weapons/rocket_impact.wav");
        
-       for(i = MONSTER_FIRST + 1; i < MONSTER_LAST; ++i)
-       if(td_moncount[i] > 0)
-       if(i == MONSTER_STINGRAY || i == MONSTER_SHAMBLER || i == MONSTER_SLIME)
-               RandomSelection_Add(world, i, "", 0.2, 0.2);
-       else
-               RandomSelection_Add(world, i, "", 1, 1);
+       if not(self.health)
+               self.health = 1000;
+               
+       self.max_health = self.health;
+       self.classname = "td_generator";
+       self.flags = FL_GENERATOR;
        
-       return RandomSelection_chosen_float;
+       setsize(self, GENERATOR_MIN, GENERATOR_MAX);
+       
+       setorigin(self, self.origin + '0 0 20');
+       droptofloor();
+       
+       generator_link(td_generator_setup);
 }
 
-void combat_phase()
+void spawnfunc_td_waypoint()
 {
-       float mstrength, montype, whichmon;
-       
-       current_phase = PHASE_COMBAT;
-       
-       if(monster_count <= 0)
+       if not(g_td) { remove(self); return; }
+       if not(self.team)
        {
-               wave_end(FALSE);
+               td_debug("Tower Defense waypoint without a team, removing it.\n");
+               remove(self);
                return;
        }
        
-       self.think = combat_phase;
+       setsize(self, '-6 -6 -6', '6 6 6');
        
-       whichmon        = RandomMonster();
-       mstrength       = Monster_GetStrength(whichmon);
-       montype         = Monster_GetType(whichmon);
-       
-       if(current_monsters <= max_current && whichmon)
+       if not(self.noalign)
        {
-               TD_SpawnMonster(monster_type2string(whichmon), mstrength, montype);
-               self.nextthink = time + spawn_delay;
+               setorigin(self, self.origin + '0 0 20');
+               droptofloor();
        }
-       else
-               self.nextthink = time + 6;
-}
-
-void queue_monsters(float maxmonsters)
-{
-       float mc = 9; // note: shambler + slime = 1
-       
-       if(waterspawns_count > 0)
-               mc += 1;
-       if(flyspawns_count > 0)
-               mc += 1;
-               
-       DistributeEvenly_Init(maxmonsters, mc);
-       
-       float i;
-       
-       for(i = MONSTER_FIRST + 1; i < MONSTER_LAST; ++i)
-       {
-               if(i == MONSTER_WYVERN)
-               if(flyspawns_count < 1)
-                       continue;
-                       
-               if(i == MONSTER_STINGRAY)
-               if(waterspawns_count < 1)
-                       continue;
-       
-               if(i == MONSTER_SLIME)
-                       td_moncount[i] = DistributeEvenly_Get(0.7);
-               else if(i == MONSTER_SHAMBLER)
-                       td_moncount[i] = DistributeEvenly_Get(0.3);
-               else
-                       td_moncount[i] = DistributeEvenly_Get(1);
-       }
-}
-
-void combat_phase_begin()
-{
-       monster_count = totalmonsters;
-       entity gen;
-       
-       Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_TD_PHASE_COMBAT);
-       
-       if(autocvar_sv_eventlog)
-               GameLogEcho(":combatphase");
-               
-       self.think = combat_phase;
-       self.nextthink = time + 1;
        
-       for(gen = world;(gen = findflags(gen, flags, FL_GENERATOR)); )
-               gen.takedamage = DAMAGE_AIM;
-}
-
-float cphase_updates;
-void combat_phase_announce() // TODO: clean up these fail nextthinks...
-{
-       cphase_updates += 1;
-       
-       if(cphase_updates == 0)
-               Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_PREPARE);
-       else if(cphase_updates == 3)
-               Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_3);
-       else if(cphase_updates == 4)
-               Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_2);
-       else if(cphase_updates == 5)
-               Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_1);
-       else if(cphase_updates == 6)
-       {
-               Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_BEGIN);
-               combat_phase_begin();
-       }
-       
-       if(cphase_updates >= 6)
-               return;
-
-       self.think = combat_phase_announce;
-       self.nextthink = time + 1;
+       self.classname = "td_waypoint";
+       self.think = td_waypoint_think;
+       self.nextthink = time + 0.1;
 }
 
-void build_phase()
+void spawnfunc_td_controller()
 {
-       entity head;
-       float n_players = 0, gen_washealed = FALSE, mcount, mskill;
-       
-       current_phase = PHASE_BUILD;
-       
-       for(head = world;(head = findflags(head, flags, FL_GENERATOR)); )
-       {
-               if(head.health < head.max_health)
-               {
-                       gen_washealed = TRUE;
-                       pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1);
-                       head.health = head.max_health;
-                       WaypointSprite_UpdateHealth(head.sprite, head.health);
-                       head.SendFlags |= GSF_STATUS;
-               }
-               head.takedamage = DAMAGE_NO;
-       }
-       
-       FOR_EACH_PLAYER(head)
-       {
-               if(head.health < 100) head.health = 100;
-               if(gen_washealed) PlayerScore_Add(head, SP_TD_SCORE, -autocvar_g_td_generator_damaged_points);
-                       
-        n_players += 1;
-       }
-       
-       mcount = autocvar_g_td_monster_count_increment * wave_count;
-       mskill = n_players * 0.02;
-               
-       totalmonsters += mcount;
-       monster_skill += autocvar_g_td_monsters_skill_increment;
-       monster_skill += mskill;
-       
-       if(monster_skill < 1) monster_skill = 1;
-       if(totalmonsters < 1) totalmonsters = ((autocvar_g_td_monster_count > 0) ? autocvar_g_td_monster_count : 10);
-       if(wave_count < 1) wave_count = 1;
-       
-       Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_TD_PHASE_BUILD, wave_count, totalmonsters, build_time);
-    
-    FOR_EACH_MONSTER(head)
-    {
-               if(head.health <= 0)
-                       continue;
-                       
-        dprint(strcat("Warning: Monster still alive during build phase! Monster name: ", head.netname, "\n"));
-               
-               WaypointSprite_Kill(head.sprite);
-        remove(head);
-    }
-       
-       monsters_total = totalmonsters;
-       monsters_killed = 0;
-               
-       queue_monsters(totalmonsters);
-       
-       cphase_updates = -1;
-       
-       if(autocvar_sv_eventlog)
-        GameLogEcho(strcat(":buildphase:", ftos(wave_count), ":", ftos(totalmonsters)));
-       
-       self.think = combat_phase_announce;
-       self.nextthink = time + build_time - 6;
+       if not(g_td) { remove(self); return; }
 }
 
-void wave_end(float starting)
+void spawnfunc_td_spawnpoint()
 {
-       if not(starting)
-       {
-               Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_TD_VICTORY, ((wave_count >= max_waves) ? "Level" : "Wave"));
-               
-               if(autocvar_sv_eventlog)
-            GameLogEcho(strcat(":wave:", ftos(wave_count), ":victory"));
-       }
+       if not(g_td) { remove(self); return; }
        
-       if(wave_count >= max_waves)
-       {
-               gensurvived = TRUE;
-               return;
-       }
+       self.classname = "td_spawnpoint";
        
-       if not(starting)
-               wave_count += 1;
-               
-       self.think = build_phase;
-       self.nextthink = time + 3;
+       self.effects = EF_STARDUST;
 }
 
+// initialization stuff
 void td_ScoreRules()
 {
-       ScoreInfo_SetLabel_PlayerScore(SP_TD_SCORE,             "score",         SFL_SORT_PRIO_PRIMARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_TD_KILLS,             "kills",         SFL_LOWER_IS_BETTER);
-       ScoreInfo_SetLabel_PlayerScore(SP_TD_TURKILLS,  "frags",         SFL_LOWER_IS_BETTER);
-       ScoreInfo_SetLabel_PlayerScore(SP_TD_DEATHS,    "deaths",    SFL_LOWER_IS_BETTER);
-       ScoreInfo_SetLabel_PlayerScore(SP_TD_SUICIDES,  "suicides",  SFL_LOWER_IS_BETTER | SFL_ALLOW_HIDE);
+       ScoreRules_basics(2, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, TRUE);
+       ScoreInfo_SetLabel_TeamScore(ST_TD_DESTROYS,  "destroyed", SFL_SORT_PRIO_PRIMARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_TD_DESTROYS,"destroyed", SFL_SORT_PRIO_PRIMARY);
        ScoreRules_basics_end();
 }
 
@@ -682,117 +495,79 @@ void td_DelayedInit()
 void td_Initialize()
 {
        InitializeEntity(world, td_DelayedInit, INITPRIO_GAMETYPE);
+       
+       round_handler_Spawn(TD_CheckTeams, TD_CheckWinner, TD_RoundStart);
+       round_handler_Init(5, 10, 180);
 }
 
-MUTATOR_HOOKFUNCTION(td_TurretValidateTarget)
+// mutator hooks
+MUTATOR_HOOKFUNCTION(td_TurretSpawn)
 {
-       if(time < game_starttime || current_phase != PHASE_COMBAT || gameover)
-       {
-               turret_target = world;
-               return FALSE; // battle hasn't started
-       }
-
-       if(turret_flags & TFL_TARGETSELECT_MISSILESONLY)
-    if(turret_target.flags & FL_PROJECTILE)
-       if(turret_target.owner.flags & FL_MONSTER)
-        return TRUE; // flac support
-                       
-       if(turret.turrcaps_flags & TFL_TURRCAPS_SUPPORT && turret_target.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+       if(self.realowner == world)
                return TRUE;
-       if not(turret_target.flags & FL_MONSTER)
-               turret_target = world;
                
-       return FALSE;
-}
-
-MUTATOR_HOOKFUNCTION(td_PlayerThink)
-{
-       self.stat_current_wave = wave_count;
-       self.stat_totalwaves = max_waves;
+       self.bot_attack = FALSE;
        
        return FALSE;
 }
 
-MUTATOR_HOOKFUNCTION(td_PlayerSpawn)
+MUTATOR_HOOKFUNCTION(td_MonsterSpawn)
 {
-       self.bot_attack = FALSE;
+       if(!self.team || !self.realowner)
+       {
+               td_debug(strcat("Removed monster ", self.netname, " with team ", ftos(self.team), "\n"));
+               WaypointSprite_Kill(self.sprite);
+               if(self.weaponentity) remove(self.weaponentity);
+               remove(self);
+               return FALSE;
+       }
        
-       Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_TD_PROTECT);
+       self.candrop = FALSE;
+       self.bot_attack = FALSE;
+       self.ammo_fuel = bound(20, 20 * self.level, 100);
+       self.target_range = 300;
+       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_CORPSE | DPCONTENTS_MONSTERCLIP;
        
        return FALSE;
 }
 
-MUTATOR_HOOKFUNCTION(td_PlayerDies)
-{
-       if(frag_attacker.flags & FL_MONSTER)
-               PlayerScore_Add(frag_target, SP_TD_DEATHS, 1);
-               
-       if(frag_target == frag_attacker)
-               PlayerScore_Add(frag_attacker, SP_TD_SUICIDES, 1);
-
-       return FALSE;
-}
-
-MUTATOR_HOOKFUNCTION(td_GiveFragsForKill)
-{
-       frag_score = 0;
-               
-       return TRUE; // no frags counted in td
-}
-
-MUTATOR_HOOKFUNCTION(td_PlayerDamage)
+MUTATOR_HOOKFUNCTION(td_MonsterDies)
 {
-       if(frag_attacker.realowner == frag_target)
-               frag_damage = 0;
-               
-       if(frag_target.flags & FL_MONSTER && time < frag_target.spawnshieldtime)
-               frag_damage = 0;
-               
-       if(frag_target.vehicle_flags & VHF_ISVEHICLE && !(frag_attacker.flags & FL_MONSTER))
-               frag_damage = 0;
-               
-       if(frag_attacker.vehicle_flags & VHF_ISVEHICLE && !(frag_target.flags & FL_MONSTER))
-               frag_damage = 0;
-               
-       if(!autocvar_g_td_pvp && frag_attacker != frag_target && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
+       vector backuporigin;
+       entity oldself;
+       
+       total_killed++;
+       
+       backuporigin = self.origin;
+       oldself = self;
+       self = spawn();
+       
+       self.gravity = 1;
+       setorigin(self, backuporigin + '0 0 5');
+       spawn_td_fuel(oldself.ammo_fuel);
+       self.touch = M_Item_Touch;
+       if(self == world)
        {
-               frag_attacker.typehitsound += 1;
-               frag_damage = 0;
+               self = oldself;
+               return FALSE;
        }
-               
-       if(frag_attacker.turrcaps_flags & TFL_TURRCAPS_ISTURRET && IS_PLAYER(frag_target))
-               frag_damage = 0;
-               
-       if((frag_target.turrcaps_flags & TFL_TURRCAPS_ISTURRET) && !(frag_attacker.flags & FL_MONSTER || frag_attacker.turrcaps_flags & TFL_TURRCAPS_SUPPORT))
-               frag_damage = 0;
-               
-       return TRUE;
-}
-
-MUTATOR_HOOKFUNCTION(td_TurretDies)
-{
-       if(self.realowner)
-               self.realowner.turret_cnt -= 1;
-                       
+       SUB_SetFade(self, time + 5, 1);
+       
+       self = oldself;
+       
        return FALSE;
 }
 
-MUTATOR_HOOKFUNCTION(td_MonsterCheckBossFlag)
-{
-       // No minibosses in tower defense
-       return TRUE;
-}
-
-MUTATOR_HOOKFUNCTION(td_MonsterMove)
+MUTATOR_HOOKFUNCTION(td_MonsterThink)
 {
-       entity head;
-       float n_players = 0;
-       
-       FOR_EACH_PLAYER(head) { ++n_players; }
-       if(n_players < 1) return TRUE;
+       if(time <= game_starttime && round_handler_IsActive())
+               return TRUE;
+               
+       if(IS_PLAYER(self.enemy))
+               self.enemy = world;
 
        if not(self.enemy) // don't change targets while attacking
-       if((vlen(monster_target.origin - self.origin) <= 100 && monster_target.classname == "td_waypoint") || (vlen(monster_target.origin - self.origin) <= 200 && (self.flags & FL_FLY) && monster_target.classname == "td_waypoint"))
+       if(vlen(monster_target.origin - self.origin) <= 100)
        {
                if(monster_target.target2)
                {
@@ -807,150 +582,81 @@ MUTATOR_HOOKFUNCTION(td_MonsterMove)
                monster_target = find(world, targetname, self.target2);
                
                if(monster_target == world)
-                       monster_target = PickGenerator();
-       }
-       
-       monster_speed_run = (m_speed_run + random() * 4) * monster_skill;
-       monster_speed_walk = (m_speed_walk + random() * 4) * monster_skill;
-       
-       return FALSE;
-}
-
-MUTATOR_HOOKFUNCTION(td_MonsterSpawn)
-{
-       if(self.realowner == world) // nothing spawned it, so kill it
-       {
-               WaypointSprite_Kill(self.sprite);
-               remove(self.weaponentity);
-               remove(self);
-               return TRUE;
+                       monster_target = PickGenerator(self.team);
+                       
+               if(monster_target == world)
+                       return TRUE; // no generators or waypoints?!
        }
        
-       current_monsters += 1;
-       
-       self.spawnshieldtime = time + autocvar_g_td_monsters_spawnshield_time;
+       td_debug(sprintf("Monster target: %s. Monster target2: %s. Monster target entity: %s.\n", self.target, self.target2, etos(monster_target)));
        
-       self.drop_size = bound(5, self.health * 0.05, autocvar_g_pickup_fuel_max);
+       monster_speed_run = (150 + random() * 4) * monster_skill;
+       monster_speed_walk = (100 + random() * 4) * monster_skill;
        
-       self.target_range = 600;
-       
-       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_CORPSE | DPCONTENTS_MONSTERCLIP;
-       
-       td_moncount[self.monsterid] -= 1;
-       
-       return TRUE;
-}
-
-MUTATOR_HOOKFUNCTION(td_MonsterDies)
-{
-       entity oldself;
-       vector backuporigin;
-
-       monster_count -= 1;
-       current_monsters -= 1;
-       monsters_killed += 1;
-       
-       if(IS_PLAYER(frag_attacker))
-       {
-               PlayerScore_Add(frag_attacker, SP_TD_SCORE, autocvar_g_td_kill_points);
-               PlayerScore_Add(frag_attacker, SP_TD_KILLS, 1);
-       }
-       else if(IS_PLAYER(frag_attacker.realowner))
-       if(frag_attacker.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
-       {
-               PlayerScore_Add(frag_attacker.realowner, SP_TD_SCORE, autocvar_g_td_turretkill_points);
-               PlayerScore_Add(frag_attacker.realowner, SP_TD_TURKILLS, 1);
-       }
-
-       backuporigin = self.origin;
-       oldself = self;
-       self = spawn();
-       
-       self.gravity = 1;
-       setorigin(self, backuporigin + '0 0 5');
-       spawn_td_fuel(oldself.drop_size);
-       self.touch = M_Item_Touch;
-       if(self == world)
-       {
-               self = oldself;
-               return FALSE;
-       }
-       SUB_SetFade(self, time + autocvar_g_monsters_drop_time, 1);
-       
-       self = oldself;
-
        return FALSE;
 }
 
 MUTATOR_HOOKFUNCTION(td_MonsterFindTarget)
 {
-       float n_players = 0;
-       entity player;
-       local entity e;
-       
-       FOR_EACH_PLAYER(player) { ++n_players; }
-       
-       if(n_players < 1) // no players online, so do nothing
-       {
-               self.enemy = world;
-               return TRUE;
-       }
+       entity e;
        
        for(e = world;(e = findflags(e, monster_attack, TRUE)); ) 
        {
                if(ignore_turrets)
                if(e.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
                        continue;
+                       
+               if(e.flags & FL_MONSTER)
+                       continue; // don't attack other monsters?
                
                if(monster_isvalidtarget(e, self))
-               if((vlen(trace_endpos - self.origin) < 200 && e.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (vlen(trace_endpos - self.origin) < 200 && !(e.flags & FL_GENERATOR)) || (vlen(trace_endpos - self.origin) < 500 && e.flags & FL_GENERATOR))
-               {
                        self.enemy = e;
-               }
        }
        
        return TRUE;
 }
 
-MUTATOR_HOOKFUNCTION(td_SetStartItems)
+MUTATOR_HOOKFUNCTION(td_PlayerSpawn)
 {
-       start_ammo_fuel = 150; // to be nice...
+       self.monster_attack = FALSE;
+       self.bot_attack = FALSE;
+       
+       if(self.newfuel)
+       {
+               self.ammo_fuel = self.newfuel;
+               self.newfuel = 0;
+       }
        
        return FALSE;
 }
 
-MUTATOR_HOOKFUNCTION(td_SetModname)
-{
-       // TODO: find out why td_Initialize doesn't work for TD stats...
-       addstat(STAT_CURRENT_WAVE, AS_FLOAT, stat_current_wave);
-       addstat(STAT_TOTALWAVES, AS_FLOAT, stat_totalwaves);
-               
-       return FALSE;
-}
-
-MUTATOR_HOOKFUNCTION(td_TurretSpawn)
+MUTATOR_HOOKFUNCTION(td_Damage)
 {
-       if(self.realowner == world)
-               return TRUE; // wasn't spawned by a player
+       if(IS_PLAYER(frag_attacker))
+       if(frag_target.flags & FL_MONSTER)
+               frag_damage = 0;
                
-       self.bot_attack = FALSE;
-       buffturret(self, 0.5);
+       if(IS_PLAYER(frag_target))
+       {
+               frag_damage = 0;
+               if(frag_attacker != frag_target)
+                       frag_force = '0 0 0';
+       }
        
        return FALSE;
 }
 
-MUTATOR_HOOKFUNCTION(td_DisableVehicles)
-{
-       // you shall not spawn!
-       return TRUE;
-}
-
 MUTATOR_HOOKFUNCTION(td_PlayerCommand)
 {
        if(MUTATOR_RETURNVALUE) { return FALSE; } // command was already handled?
        
+       vector org;
+       
        makevectors(self.v_angle);
-       WarpZone_TraceLine(self.origin, self.origin + v_forward * 100, MOVE_HITMODEL, self);
+       
+       org = self.origin + self.view_ofs + v_forward * 100;
+       
+       tracebox(self.origin + self.view_ofs, '-16 -16 -16', '16 16 16', org, MOVE_NORMAL, self);
        entity targ = trace_ent;
        if(targ.owner.realowner == self)
                targ = targ.owner;
@@ -959,7 +665,7 @@ MUTATOR_HOOKFUNCTION(td_PlayerCommand)
        {
                if(argv(1) == "list")
                {
-                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_LIST, "mlrs walker plasma towerbuff flac barricade");
+                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_LIST, "mlrs walker plasma towerbuff flac");
                        return TRUE;
                }
                if(!IS_PLAYER(self) || self.health <= 0)
@@ -982,58 +688,6 @@ MUTATOR_HOOKFUNCTION(td_PlayerCommand)
                
                return TRUE;
        }
-       if(cmd_name == "repairturret")
-       {
-               if((targ.playerid != self.playerid || targ.realowner != self) || !(targ.turrcaps_flags & TFL_TURRCAPS_ISTURRET))
-               {
-                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_AIM_REPAIR);
-                       return TRUE;
-               }
-               if(self.ammo_fuel < autocvar_g_td_turret_repair_cost)   
-               {
-                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_NOFUEL_REPAIR, autocvar_g_td_turret_repair_cost);
-                       return TRUE;
-               }
-               if(targ.health >= targ.max_health)
-               {
-                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_MAXHEALTH);
-                       return TRUE;
-               }
-               
-               self.ammo_fuel -= autocvar_g_td_turret_repair_cost;
-               targ.SendFlags |= TNSF_STATUS;
-               targ.health = bound(1, targ.health + 100, targ.max_health);
-               WaypointSprite_UpdateHealth(targ.sprite, targ.health);
-               Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_REPAIR);
-               
-               return TRUE;
-       }
-       if(cmd_name == "buffturret")
-       {
-               if((targ.playerid != self.playerid || targ.realowner != self) || !(targ.turrcaps_flags & TFL_TURRCAPS_ISTURRET))
-               {
-                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_AIM_UPGRADE);
-                       return TRUE;
-               }
-               if(self.ammo_fuel < autocvar_g_td_turret_upgrade_cost)  
-               {
-                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_NOFUEL_UPGRADE, autocvar_g_td_turret_upgrade_cost);
-                       return TRUE;
-               }
-               if(targ.turret_buff >= 5)
-               {
-                       Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_MAXPOWER);
-                       return TRUE;
-               }
-               
-               self.ammo_fuel -= autocvar_g_td_turret_upgrade_cost;
-               targ.SendFlags |= TNSF_STATUS;
-               buffturret(targ, 1.2);
-               WaypointSprite_UpdateHealth(targ.sprite, targ.health);
-               Send_Notification(NOTIF_ONE, self, MSG_MULTI, MULTI_TD_UPGRADE);
-               
-               return TRUE;
-       }
        if(cmd_name == "turretremove")
        {
                if((targ.turrcaps_flags & TFL_TURRCAPS_ISTURRET) && (targ.playerid == self.playerid || targ.realowner == self))
@@ -1054,6 +708,8 @@ MUTATOR_HOOKFUNCTION(td_PlayerCommand)
 
 MUTATOR_HOOKFUNCTION(td_ClientConnect)
 {
+       self.newfuel = 75;
+       
        entity t;
        
        self.turret_cnt = 0;
@@ -1062,33 +718,80 @@ MUTATOR_HOOKFUNCTION(td_ClientConnect)
        if(t.playerid == self.playerid)
        {
                t.realowner = self;
-               self.turret_cnt += 1;
+               self.turret_cnt += 1; // nice try
+       }
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(td_DisableVehicles)
+{
+       return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(td_SetModname)
+{
+       g_cloaked = 1;
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(td_TurretValidateTarget)
+{
+       if(time < game_starttime || (time <= game_starttime && round_handler_IsActive()) || gameover)
+       {
+               turret_target = world;
+               return FALSE; // battle hasn't started
        }
 
+       if(turret_flags & TFL_TARGETSELECT_MISSILESONLY)
+    if(turret_target.flags & FL_PROJECTILE)
+       if(turret_target.owner.flags & FL_MONSTER)
+        return TRUE; // flac support
+                       
+       if(turret.turrcaps_flags & TFL_TURRCAPS_SUPPORT && turret_target.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+               return TRUE;
+       if not(turret_target.flags & FL_MONSTER)
+               turret_target = world;
+               
+       if(!IsDifferentTeam(turret_target, turret))
+               turret_target = world;
+               
        return FALSE;
 }
 
-MUTATOR_DEFINITION(gamemode_td)
+MUTATOR_HOOKFUNCTION(td_TurretDies)
 {
+       if(self.realowner)
+               self.realowner.turret_cnt -= 1;
+                       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(td_GetTeamCount)
+{
+       ret_float = 2;
+       
+       return FALSE;
+}
+
+MUTATOR_DEFINITION(gamemode_towerdefense)
+{
+       MUTATOR_HOOK(TurretSpawn, td_TurretSpawn, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterSpawn, td_MonsterSpawn, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterDies, td_MonsterDies, CBC_ORDER_ANY);
-       MUTATOR_HOOK(MonsterMove, td_MonsterMove, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MonsterMove, td_MonsterThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterFindTarget, td_MonsterFindTarget, CBC_ORDER_ANY);
-       MUTATOR_HOOK(MonsterCheckBossFlag, td_MonsterCheckBossFlag, CBC_ORDER_ANY);
-       MUTATOR_HOOK(SetStartItems, td_SetStartItems, CBC_ORDER_ANY);
-       MUTATOR_HOOK(SetModname, td_SetModname, CBC_ORDER_ANY);
-       MUTATOR_HOOK(TurretValidateTarget, td_TurretValidateTarget, CBC_ORDER_ANY);
-       MUTATOR_HOOK(TurretSpawn, td_TurretSpawn, CBC_ORDER_ANY);
-       MUTATOR_HOOK(TurretDies, td_TurretDies, CBC_ORDER_ANY);
-       MUTATOR_HOOK(GiveFragsForKill, td_GiveFragsForKill, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerPreThink, td_PlayerThink, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerDies, td_PlayerDies, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerDamage_Calculate, td_PlayerDamage, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, td_PlayerSpawn, CBC_ORDER_ANY);
-       MUTATOR_HOOK(VehicleSpawn, td_DisableVehicles, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDamage_Calculate, td_Damage, CBC_ORDER_ANY);
        MUTATOR_HOOK(SV_ParseClientCommand, td_PlayerCommand, CBC_ORDER_ANY);
        MUTATOR_HOOK(ClientConnect, td_ClientConnect, CBC_ORDER_ANY);
-       
+       MUTATOR_HOOK(VehicleSpawn, td_DisableVehicles, CBC_ORDER_ANY);
+       MUTATOR_HOOK(SetModname, td_SetModname, CBC_ORDER_ANY);
+       MUTATOR_HOOK(TurretValidateTarget, td_TurretValidateTarget, CBC_ORDER_ANY);
+       MUTATOR_HOOK(TurretDies, td_TurretDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GetTeamCount, td_GetTeamCount, CBC_ORDER_ANY);
+
        MUTATOR_ONADD
        {
                if(time > 1) // game loads at time 1
index 8864ee75a2ccf08bca93a6f65373aed49cbab90f..30b08d6f34191f35a02aa8888d744db32cc86a3c 100644 (file)
@@ -1,60 +1,6 @@
-// Counters
-float monster_count, totalmonsters;
-float current_monsters;
-float waterspawns_count, flyspawns_count;
-float wave_count, max_waves;
-float max_turrets;
+float FL_GENERATOR = 2048;
 
-// Monster defs
-.float drop_size;
-float m_speed_run;
-float m_speed_walk;
-
-// Turret defs
-.float turret_buff;
-
-// TD defs
-.float stat_current_wave;
-.float stat_totalwaves;
-.float spawntype;
-float spawn_delay;
-float max_current;
 float ignore_turrets;
-float SWARM_NORMAL     = 0;
-float SWARM_WEAK       = 1;
-float SWARM_STRONG     = 2;
-float SWARM_FLY                = 3;
-float SWARM_SWIM       = 4;
-float build_time;
-float td_dont_end;
-void(float starting) wave_end;
-.float turret_cnt;
-float td_gencount;
-void() spawnfunc_td_controller;
-float current_phase;
-#define PHASE_BUILD    1
-#define PHASE_COMBAT   2
-
-// Scores
-#define SP_TD_KILLS    0
-#define SP_TD_TURKILLS         2
-#define SP_TD_SCORE    4
-#define SP_TD_DEATHS   6
-#define SP_TD_SUICIDES         8
-
-// Controller
-.float maxwaves;
-.float monstercount;
-.float startwave;
-.float dontend;
-.float maxturrets;
-.float buildtime;
-.float mspeed_run;
-.float mspeed_walk;
-.float spawndelay;
-.float maxcurrent;
-.float ignoreturrets;
 
-// Generator
-float gendestroyed;
-float FL_GENERATOR = 2048;
\ No newline at end of file
+#define SP_TD_DESTROYS 4
+#define ST_TD_DESTROYS 1
\ No newline at end of file
index f58fdcccb53001525e5160a8fdca0095bc050f01..808d91c14ab3d737912d257bf0245f4efc161f94 100644 (file)
@@ -7,7 +7,7 @@ MUTATOR_DECLARATION(gamemode_keepaway);
 MUTATOR_DECLARATION(gamemode_ctf);
 MUTATOR_DECLARATION(gamemode_nexball);
 MUTATOR_DECLARATION(gamemode_onslaught);
-MUTATOR_DECLARATION(gamemode_td);
+MUTATOR_DECLARATION(gamemode_towerdefense);
 MUTATOR_DECLARATION(gamemode_domination);
 MUTATOR_DECLARATION(gamemode_lms);
 
index e2805b913b702d4ec17f9149be9c237a2ab06de3..6c9f64bcb8d31ed7b6ac35fd093ff0612b024f01 100644 (file)
@@ -108,10 +108,11 @@ void InitGameplayMode()
     
        if(g_td)
        {
-               fraglimit_override = 0; // no limits in TD - it's a survival mode
+               ActivateTeamplay();
+               fraglimit_override = 0; // not supported by TD
                leadlimit_override = 0;
                timelimit_override = 0;
-               MUTATOR_ADD(gamemode_td);
+               MUTATOR_ADD(gamemode_towerdefense);
        }
 
        if(g_lms)