From 177dfda250c373e5f2dc27945234d3609d597f1c Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 5 Aug 2013 18:06:15 +1000 Subject: [PATCH] Begin working on a new game-mode: invasion --- gamemodes.cfg | 54 ++--- qcsrc/common/mapinfo.qh | 3 + qcsrc/server/autocvars.qh | 5 + qcsrc/server/mutators/gamemode_invasion.qc | 236 +++++++++++++++++++++ qcsrc/server/mutators/gamemode_invasion.qh | 8 + qcsrc/server/mutators/mutators.qh | 1 + qcsrc/server/progs.src | 2 + qcsrc/server/teamplay.qc | 9 + 8 files changed, 278 insertions(+), 40 deletions(-) create mode 100644 qcsrc/server/mutators/gamemode_invasion.qc create mode 100644 qcsrc/server/mutators/gamemode_invasion.qh diff --git a/gamemodes.cfg b/gamemodes.cfg index 217712919..552c8a7c8 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -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" diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index 10a35ee47..7eaaf2900 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -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; diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 25a27cc30..38e0f0cdf 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -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 index 000000000..a45999cac --- /dev/null +++ b/qcsrc/server/mutators/gamemode_invasion.qc @@ -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 index 000000000..4a327ae87 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_invasion.qh @@ -0,0 +1,8 @@ +float numspawned; +float maxspawned; +float roundcnt; +float maxrounds; +float numkilled; +float last_check; + +#define SP_INVASION_KILLS 0 diff --git a/qcsrc/server/mutators/mutators.qh b/qcsrc/server/mutators/mutators.qh index 3f9f020af..25da5b1e0 100644 --- a/qcsrc/server/mutators/mutators.qh +++ b/qcsrc/server/mutators/mutators.qh @@ -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); diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index e7cd65593..7fc907d95 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -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 diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 0ba1e7c84..f9cd2784a 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -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(); -- 2.39.2