]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Begin working on a new game-mode: invasion
authorMario <mario.mario@y7mail.com>
Mon, 5 Aug 2013 08:06:15 +0000 (18:06 +1000)
committerMario <mario.mario@y7mail.com>
Mon, 5 Aug 2013 08:06:15 +0000 (18:06 +1000)
gamemodes.cfg
qcsrc/common/mapinfo.qh
qcsrc/server/autocvars.qh
qcsrc/server/mutators/gamemode_invasion.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_invasion.qh [new file with mode: 0644]
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src
qcsrc/server/teamplay.qc

index 2177129195d55faa80a09bcd26d07728c7605bfa..552c8a7c8a26ddde5833e5eb6e954b14c6ac2c35 100644 (file)
@@ -37,7 +37,7 @@ alias cl_hook_gamestart_nb
 alias cl_hook_gamestart_cts
 alias cl_hook_gamestart_ka
 alias cl_hook_gamestart_ft
-alias cl_hook_gamestart_td
+alias cl_hook_gamestart_invasion
 alias cl_hook_gameend
 alias cl_hook_activeweapon
 
@@ -59,7 +59,7 @@ alias sv_hook_gamestart_nb
 alias sv_hook_gamestart_cts
 alias sv_hook_gamestart_ka
 alias sv_hook_gamestart_ft
-alias sv_hook_gamestart_td
+alias sv_hook_gamestart_invasion
 alias sv_hook_gamerestart
 alias sv_hook_gameend
 
@@ -84,6 +84,7 @@ seta g_keyhunt_point_leadlimit -1     "Keyhunt point lead limit overriding the mapin
 seta g_race_laps_limit -1      "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_invasion_round_limit -1 "Invasion round limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 
 
 // =================================
@@ -136,9 +137,9 @@ set g_cts_weapon_stay 2
 set g_ft_respawn_waves 0
 set g_ft_respawn_delay 0
 set g_ft_weapon_stay 0
-set g_td_respawn_waves 0
-set g_td_respawn_delay 0
-set g_td_weapon_stay 0
+set g_invasion_respawn_waves 0
+set g_invasion_respawn_delay 0
+set g_invasion_weapon_stay 0
 
 
 // =======
@@ -426,38 +427,11 @@ set g_race_qualifying_timelimit 0
 set g_race_qualifying_timelimit_override -1
 set g_race_teams 0     "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
 
-// ===============
-//  tower defense
-// ===============
-set g_td 0 "Tower Defense: protect the generator/s from waves of monsters"
-set g_td_majority_factor 0.8
-set g_td_force_settings 0 "if enabled, don't use map settings (monster count, start wave etc.)"
-set g_td_start_wave 1
-set g_td_generator_health 700
-set g_td_generator_damaged_points 20 "player loses this many points if the generator was damaged during the wave"
-set g_td_current_monsters 10 "maximum monsters that can be spawned simultaneously"
-set g_td_monster_count 10
-set g_td_monster_count_increment 5
-set g_td_buildphase_time 20
-set g_td_generator_dontend 0 "don't change maps when a generator is destroyed (only if there is more than 1 generator)"
-set g_td_pvp 0
-set g_td_monsters_skill_start 1 "set to 0 to use g_monsters_skill instead"
-set g_td_monsters_skill_increment 0.1
-set g_td_monsters_spawnshield_time 2
-set g_td_monsters_ignore_turrets 0
-set g_td_max_waves 8
-set g_td_kill_points 5
-set g_td_turretkill_points 3
-set g_td_turret_max 4
-set g_td_turret_plasma_cost 50
-set g_td_turret_mlrs_cost 80
-set g_td_turret_walker_cost 100
-set g_td_turret_towerbuff_cost 70
-set g_td_turret_barricade_cost 20
-set g_td_turret_flac_cost 40
-set g_td_turret_upgrade_cost 100
-set g_td_turret_repair_cost 20
-set g_td_barricade_damage 10
-set g_td_monsters_speed_walk 150
-set g_td_monsters_speed_run 170
-set g_td_monsters_spawn_delay 1.5
+// ==========
+//  invasion
+// ==========
+set g_invasion 0 "Invasion: survive against waves of monsters"
+set g_invasion_round_timelimit 120 "maximum time to kill all monsters"
+set g_invasion_warmup 20 "time between waves to prepare for battle"
+set g_invasion_monster_count 20 "number of monsters on first wave (increments)"
+set g_invasion_zombies_only 0 "only spawn zombies"
index 10a35ee470a7e3cfbdfc70792dcf4b628977a3da..7eaaf2900121ee94ae53a588daa1622789e7329f 100644 (file)
@@ -78,6 +78,9 @@ REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointli
 REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30")
 #define g_keepaway IS_GAMETYPE(KEEPAWAY)
 
+REGISTER_GAMETYPE(_("Invasion"),invasion,g_invasion,INVASION,"timelimit=20 pointlimit=30")
+#define g_invasion IS_GAMETYPE(INVASION)
+
 float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for minstagib-only maps
 float MAPINFO_FEATURE_VEHICLES      = 2;
 float MAPINFO_FEATURE_TURRETS       = 4;
index 25a27cc30976aa4d99b932470c29a6be1fd2f0aa..38e0f0cdfaff4c9680011924e12951b5afdb8d0a 100644 (file)
@@ -1259,3 +1259,8 @@ float autocvar_g_touchexplode_radius;
 float autocvar_g_touchexplode_damage;
 float autocvar_g_touchexplode_edgedamage;
 float autocvar_g_touchexplode_force;
+float autocvar_g_invasion_round_timelimit;
+float autocvar_g_invasion_round_limit;
+float autocvar_g_invasion_warmup;
+float autocvar_g_invasion_monster_count;
+float autocvar_g_invasion_zombies_only;
diff --git a/qcsrc/server/mutators/gamemode_invasion.qc b/qcsrc/server/mutators/gamemode_invasion.qc
new file mode 100644 (file)
index 0000000..a45999c
--- /dev/null
@@ -0,0 +1,236 @@
+void invasion_spawnpoint()
+{
+       if not(g_invasion) { remove(self); return; }
+       
+       self.classname = "invasion_spawnpoint";
+}
+
+float invasion_PickMonster()
+{
+       if(autocvar_g_invasion_zombies_only)
+               return MONSTER_ZOMBIE;
+
+       float i;
+       
+       RandomSelection_Init();
+       
+       for(i = MONSTER_FIRST + 1; i < MONSTER_LAST; ++i)
+       {
+               if(i == MONSTER_STINGRAY || i == MONSTER_WYVERN)
+                       continue; // flying/swimming monsters not yet supported
+               
+               RandomSelection_Add(world, i, "", 1, 1);
+       }
+       
+       return RandomSelection_chosen_float;
+}
+
+entity invasion_PickSpawn()
+{
+       entity e;
+       
+       RandomSelection_Init();
+       
+       for(e = world;(e = find(e, classname, "invasion_spawnpoint")); )
+               RandomSelection_Add(e, 0, string_null, 1, 1);
+               
+       return RandomSelection_chosen_ent;
+}
+
+void invasion_SpawnChosenMonster(float mon)
+{
+       entity spawn_point, monster;
+       
+       spawn_point = invasion_PickSpawn();
+       
+       if(spawn_point == world)
+       {
+               dprint("Warning: couldn't find any invasion_spawnpoint spawnpoints, no monsters will spawn!\n");
+               return;
+       }
+       
+       monster = spawnmonster("", mon, spawn_point, spawn_point, spawn_point.origin, FALSE, 2);
+}
+
+void invasion_SpawnMonsters()
+{
+       float chosen_monster = invasion_PickMonster();
+       
+       invasion_SpawnChosenMonster(chosen_monster);
+}
+
+float Invasion_CheckWinner()
+{
+       if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+       {
+               entity head;
+               FOR_EACH_MONSTER(head)
+               {
+                       WaypointSprite_Kill(head.sprite);
+                       if(head.weaponentity) remove(head.weaponentity);
+                       remove(head);
+               }
+       
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+               round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
+               return 1;
+       }
+
+       if((numspawned - numkilled) < maxspawned)
+       {
+               if(time >= last_check)
+               {
+                       invasion_SpawnMonsters();
+                       last_check = time + 0.5;
+               }
+               
+               return 0;
+       }
+       
+       if(numspawned > 1)
+               return 0;
+       
+       if(roundcnt >= maxrounds)
+       {
+               NextLevel();
+               return 1;
+       }
+
+       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+       
+       round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
+
+       return 1;
+}
+
+float Invasion_CheckPlayers()
+{
+       return TRUE;
+}
+
+void Invasion_RoundStart()
+{
+       entity e;
+       float numplayers = 0;
+       FOR_EACH_PLAYER(e)
+       {
+               ++numplayers;
+               e.player_blocked = 0;
+       }
+               
+       roundcnt += 1;
+       
+       numspawned = 0;
+       numkilled = 0;
+               
+       if(roundcnt > 1)
+               maxspawned = rint(autocvar_g_invasion_monster_count * roundcnt / 0.7);
+       else
+               maxspawned = autocvar_g_invasion_monster_count;
+       
+       monster_skill += 0.01 * numplayers;
+}
+
+MUTATOR_HOOKFUNCTION(invasion_MonsterDies)
+{
+       numkilled += 1;
+       
+       if(IS_PLAYER(frag_attacker))
+               PlayerScore_Add(frag_attacker, SP_INVASION_KILLS, +1);
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(invasion_MonsterSpawn)
+{
+       if(self.realowner == world)
+       {
+               WaypointSprite_Kill(self.sprite);
+               if(self.weaponentity) remove(self.weaponentity);
+               remove(self);
+               return FALSE;
+       }
+       
+       numspawned += 1;
+       
+       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(invasion_PlayerThink)
+{
+       monsters_total = maxspawned; // TODO: make sure numspawned never exceeds maxspawned
+       monsters_killed = numkilled;
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(invasion_PlayerDamage)
+{
+       if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target))
+       {
+               frag_damage = 0;
+               frag_force = '0 0 0';
+       }
+       
+       if(frag_attacker.flags & FL_MONSTER && frag_target.flags & FL_MONSTER)
+               frag_damage = 0;
+       
+       return FALSE;
+}
+
+void invasion_ScoreRules()
+{
+       ScoreRules_basics(0, SFL_SORT_PRIO_SECONDARY, 0, FALSE);
+       ScoreInfo_SetLabel_PlayerScore(SP_INVASION_KILLS, "kills", SFL_SORT_PRIO_PRIMARY);
+       ScoreRules_basics_end();
+}
+
+void invasion_Initialize()
+{
+       invasion_ScoreRules();
+
+       round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
+       round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
+       
+       allowed_to_spawn = TRUE;
+       
+       monster_skill = 0.01;
+       
+       roundcnt = 0;
+}
+
+MUTATOR_DEFINITION(gamemode_invasion)
+{
+       MUTATOR_HOOK(MonsterDies, invasion_MonsterDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MonsterSpawn, invasion_MonsterSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPreThink, invasion_PlayerThink, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDamage_Calculate, invasion_PlayerDamage, CBC_ORDER_ANY);
+       
+       MUTATOR_ONADD
+       {
+               if(time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               invasion_Initialize();
+               
+               cvar_settemp("g_monsters", "1");
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back invasion_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/mutators/gamemode_invasion.qh b/qcsrc/server/mutators/gamemode_invasion.qh
new file mode 100644 (file)
index 0000000..4a327ae
--- /dev/null
@@ -0,0 +1,8 @@
+float numspawned;
+float maxspawned;
+float roundcnt;
+float maxrounds;
+float numkilled;
+float last_check;
+
+#define SP_INVASION_KILLS 0
index 3f9f020affc086c1968fbe80a2bfc64bd9a3d5e3..25da5b1e00930644cbe3f6fe65ff9bf91f94a5d3 100644 (file)
@@ -9,6 +9,7 @@ MUTATOR_DECLARATION(gamemode_nexball);
 MUTATOR_DECLARATION(gamemode_onslaught);
 MUTATOR_DECLARATION(gamemode_domination);
 MUTATOR_DECLARATION(gamemode_lms);
+MUTATOR_DECLARATION(gamemode_invasion);
 
 MUTATOR_DECLARATION(mutator_dodging);
 MUTATOR_DECLARATION(mutator_invincibleprojectiles);
index e7cd6559331cf02a3fb067c726b1d26dcd7d3d2e..7fc907d95ebc7027d81a69cd29c2aec2ed0db412 100644 (file)
@@ -45,6 +45,7 @@ mutators/gamemode_keyhunt.qh // TODO fix this
 mutators/gamemode_keepaway.qh
 mutators/gamemode_nexball.qh 
 mutators/gamemode_lms.qh
+mutators/gamemode_invasion.qh
 mutators/mutator_dodging.qh
 
 //// tZork Turrets ////
@@ -245,6 +246,7 @@ mutators/gamemode_keepaway.qc
 mutators/gamemode_nexball.qc
 mutators/gamemode_onslaught.qc
 mutators/gamemode_lms.qc
+mutators/gamemode_invasion.qc
 mutators/mutator_invincibleproj.qc
 mutators/mutator_new_toys.qc
 mutators/mutator_nix.qc
index 0ba1e7c8481db848019a0f856fdf70ee236e0b32..f9cd2784a65363abab741205143e8bc64a6947d1 100644 (file)
@@ -197,6 +197,15 @@ void InitGameplayMode()
        {
                MUTATOR_ADD(gamemode_keepaway);
        }
+       
+       if(g_invasion)
+       {
+               timelimit_override = 0; // no timelimit in invasion, round based
+               fraglimit_override = autocvar_g_invasion_round_limit;
+               maxrounds = fraglimit_override;
+               fraglimit_override = 0; // also no frag limit
+               MUTATOR_ADD(gamemode_invasion);
+       }
 
        if(teamplay)
                entcs_init();