From 40b7b8b8f776769118ccdf728e717352d76028e6 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 8 May 2014 06:27:09 +1000 Subject: [PATCH] Some improvements to invasion (allow team based invasion if map specifies, allow more monsters to spawn at the same time, precache monsters only if they're used) --- gamemodes.cfg | 5 +- qcsrc/common/mapinfo.qc | 1 + qcsrc/common/mapinfo.qh | 2 +- qcsrc/server/autocvars.qh | 3 + qcsrc/server/mutators/gamemode_invasion.qc | 188 ++++++++++++++++++--- qcsrc/server/mutators/gamemode_invasion.qh | 5 + qcsrc/server/teamplay.qc | 6 + 7 files changed, 184 insertions(+), 26 deletions(-) diff --git a/gamemodes.cfg b/gamemodes.cfg index c67f29d81..dfc240536 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -475,4 +475,7 @@ set g_invasion_round_timelimit 120 "maximum time to kill all monsters" set g_invasion_warmup 10 "time between waves to prepare for battle" set g_invasion_monster_count 10 "number of monsters on first wave (increments)" set g_invasion_zombies_only 0 "only spawn zombies" -set g_invasion_spawn_delay 2 "spawn more monsters after this delay" +set g_invasion_spawn_delay 0.25 +set g_invasion_spawnpoint_spawn_delay 0.5 +set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)" +set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode" diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 01cd2927d..b8fe58b34 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -633,6 +633,7 @@ void _MapInfo_Map_ApplyGametypeEx(string s, float pWantedType, float pThisType) cvar_set("g_freezetag_teams", v); cvar_set("g_keyhunt_teams", v); cvar_set("g_domination_default_teams", v); + cvar_set("g_invasion_teams", v); } else if(k == "qualifying_timelimit") { diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index 97b3349f8..3c0afec98 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -75,7 +75,7 @@ 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"),inv,g_invasion,INVASION,"pointlimit=50"); +REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50 teams=0"); #define g_invasion IS_GAMETYPE(INVASION) const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for minstagib-only maps diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 53fc5f530..ee7d24d8b 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -1245,6 +1245,9 @@ 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_teams; +float autocvar_g_invasion_team_spawns; +float autocvar_g_invasion_spawnpoint_spawn_delay; #define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit") float autocvar_g_invasion_warmup; float autocvar_g_invasion_monster_count; diff --git a/qcsrc/server/mutators/gamemode_invasion.qc b/qcsrc/server/mutators/gamemode_invasion.qc index 7a3f0c058..8448a215e 100644 --- a/qcsrc/server/mutators/gamemode_invasion.qc +++ b/qcsrc/server/mutators/gamemode_invasion.qc @@ -1,8 +1,12 @@ -void invasion_spawnpoint() +void spawnfunc_invasion_spawnpoint() { if(!g_invasion) { remove(self); return; } self.classname = "invasion_spawnpoint"; + + if(autocvar_g_invasion_zombies_only) // precache only if it hasn't been already + if(self.monsterid) + MON_ACTION(self.monsterid, MR_PRECACHE); } float invasion_PickMonster(float supermonster_count) @@ -21,7 +25,7 @@ float invasion_PickMonster(float supermonster_count) if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM) || ((mon.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1)) continue; // flying/swimming monsters not yet supported - RandomSelection_Add(world, i, "", 1, 1); + RandomSelection_Add(world, i, string_null, 1, 1); } return RandomSelection_chosen_float; @@ -35,11 +39,8 @@ entity invasion_PickSpawn() for(e = world;(e = find(e, classname, "invasion_spawnpoint")); ) { - if(time >= e.spawnshieldtime) - { - e.spawnshieldtime = time + 2; - RandomSelection_Add(e, 0, string_null, 1, 1); - } + RandomSelection_Add(e, 0, string_null, 1, ((time >= e.spawnshieldtime) ? 0.2 : 1)); // give recently used spawnpoints a very low rating + e.spawnshieldtime = time + autocvar_g_invasion_spawnpoint_spawn_delay; } return RandomSelection_chosen_ent; @@ -53,14 +54,52 @@ void invasion_SpawnChosenMonster(float mon) if(spawn_point == world) { - dprint("Warning: couldn't find any invasion_spawnpoint spawnpoints, no monsters will spawn!\n"); - return; - } + dprint("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations\n"); + entity e = spawn(); + setsize(e, (get_monsterinfo(mon)).mins, (get_monsterinfo(mon)).maxs); - monster = spawnmonster("", ((spawn_point.monsterid) ? spawn_point.monsterid : mon), spawn_point, spawn_point, spawn_point.origin, FALSE, FALSE, 2); + if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) + monster = spawnmonster("", mon, world, world, e.origin, FALSE, FALSE, 2); + else return; + + e.think = SUB_Remove; + e.nextthink = time + 0.1; + } + else + monster = spawnmonster("", ((spawn_point.monsterid) ? spawn_point.monsterid : mon), spawn_point, spawn_point, spawn_point.origin, FALSE, FALSE, 2); - monster.target2 = spawn_point.target2; + if(spawn_point) monster.target2 = spawn_point.target2; monster.spawnshieldtime = time; + if(spawn_point && spawn_point.target_range) monster.target_range = spawn_point.target_range; + + if(teamplay) + if(spawn_point && spawn_point.team && inv_monsters_perteam[spawn_point.team] > 0) + monster.team = spawn_point.team; + else + { + RandomSelection_Init(); + if(inv_monsters_perteam[NUM_TEAM_1] > 0) RandomSelection_Add(world, NUM_TEAM_1, string_null, 1, 1); + if(inv_monsters_perteam[NUM_TEAM_2] > 0) RandomSelection_Add(world, NUM_TEAM_2, string_null, 1, 1); + if(invasion_teams >= 3) if(inv_monsters_perteam[NUM_TEAM_3] > 0) { RandomSelection_Add(world, NUM_TEAM_3, string_null, 1, 1); } + if(invasion_teams >= 4) if(inv_monsters_perteam[NUM_TEAM_4] > 0) { RandomSelection_Add(world, NUM_TEAM_4, string_null, 1, 1); } + + monster.team = RandomSelection_chosen_float; + } + + if(teamplay) + { + monster_setupcolors(monster); + + if(monster.sprite) + { + WaypointSprite_UpdateTeamRadar(monster.sprite, RADARICON_DANGER, ((monster.team) ? Team_ColorRGB(monster.team) : '1 0 0')); + + monster.sprite.team = 0; + monster.sprite.SendFlags |= 1; + } + } + + monster.monster_attack = FALSE; // it's the player's job to kill all the monsters if(inv_roundcnt >= inv_maxrounds) monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses @@ -87,13 +126,22 @@ float Invasion_CheckWinner() return 1; } - float total_alive_monsters = 0, supermonster_count = 0; + float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0;; FOR_EACH_MONSTER(head) if(head.health > 0) { if((get_monsterinfo(head.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) ++supermonster_count; ++total_alive_monsters; + + if(teamplay) + switch(head.team) + { + case NUM_TEAM_1: ++red_alive; break; + case NUM_TEAM_2: ++blue_alive; break; + case NUM_TEAM_3: ++yellow_alive; break; + case NUM_TEAM_4: ++pink_alive; break; + } } if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned) @@ -107,12 +155,35 @@ float Invasion_CheckWinner() return 0; } - if(inv_numspawned < 1 || inv_numkilled < inv_maxspawned) - return 0; // nothing has spawned yet, or there are still alive monsters + if(inv_numspawned < 1) + return 0; // nothing has spawned yet + + if(teamplay) + { + if(((red_alive > 0) + (blue_alive > 0) + (yellow_alive > 0) + (pink_alive > 0)) > 1) + return 0; + } + else if(inv_numkilled < inv_maxspawned) + return 0; entity winner = world; - float winning_score = 0; + float winning_score = 0, winner_team = 0; + + if(teamplay) + { + if(red_alive > 0) { winner_team = NUM_TEAM_1; } + if(blue_alive > 0) + if(winner_team) { winner_team = 0; } + else { winner_team = NUM_TEAM_2; } + if(yellow_alive > 0) + if(winner_team) { winner_team = 0; } + else { winner_team = NUM_TEAM_3; } + if(pink_alive > 0) + if(winner_team) { winner_team = 0; } + else { winner_team = NUM_TEAM_4; } + } + else FOR_EACH_PLAYER(head) { float cs = PlayerScore_Add(head, SP_KILLS, 0); @@ -126,7 +197,15 @@ float Invasion_CheckWinner() FOR_EACH_MONSTER(head) monster_remove(head); - if(winner) + if(teamplay) + { + if(winner_team) + { + 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_)); + } + } + else if(winner) { Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, winner.netname); Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, winner.netname); @@ -162,6 +241,15 @@ void Invasion_RoundStart() inv_numkilled = 0; inv_maxspawned = rint(max(autocvar_g_invasion_monster_count, autocvar_g_invasion_monster_count * (inv_roundcnt * 0.5))); + + if(teamplay) + { + DistributeEvenly_Init(inv_maxspawned, invasion_teams); + inv_monsters_perteam[NUM_TEAM_1] = DistributeEvenly_Get(1); + inv_monsters_perteam[NUM_TEAM_2] = DistributeEvenly_Get(1); + if(invasion_teams >= 3) inv_monsters_perteam[NUM_TEAM_3] = DistributeEvenly_Get(1); + if(invasion_teams >= 4) inv_monsters_perteam[NUM_TEAM_4] = DistributeEvenly_Get(1); + } } MUTATOR_HOOKFUNCTION(invasion_MonsterDies) @@ -170,9 +258,17 @@ MUTATOR_HOOKFUNCTION(invasion_MonsterDies) { inv_numkilled += 1; inv_maxcurrent -= 1; + if(teamplay) { inv_monsters_perteam[self.team] -= 1; } if(IS_PLAYER(frag_attacker)) + if(SAME_TEAM(frag_attacker, self)) // in non-teamplay modes, same team = same player, so this works + PlayerScore_Add(frag_attacker, SP_KILLS, -1); + else + { PlayerScore_Add(frag_attacker, SP_KILLS, +1); + if(teamplay) + TeamScore_AddToTeam(frag_attacker.team, ST_INV_KILLS, +1); + } } return FALSE; @@ -208,7 +304,7 @@ MUTATOR_HOOKFUNCTION(invasion_OnEntityPreSpawn) return FALSE; } -MUTATOR_HOOKFUNCTION(invasion_PlayerThink) +MUTATOR_HOOKFUNCTION(invasion_StartFrame) { monsters_total = inv_maxspawned; // TODO: make sure numspawned never exceeds maxspawned monsters_killed = inv_numkilled; @@ -260,6 +356,14 @@ MUTATOR_HOOKFUNCTION(invasion_PlayerCommand) return FALSE; } +MUTATOR_HOOKFUNCTION(invasion_BotShouldAttack) +{ + if(!(checkentity.flags & FL_MONSTER)) + return TRUE; + + return FALSE; +} + MUTATOR_HOOKFUNCTION(invasion_SetStartItems) { start_health = 200; @@ -281,18 +385,31 @@ MUTATOR_HOOKFUNCTION(invasion_AllowMobSpawning) return TRUE; } -void invasion_ScoreRules() +MUTATOR_HOOKFUNCTION(invasion_GetTeamCount) +{ + ret_float = invasion_teams; + return FALSE; +} + +void invasion_ScoreRules(float inv_teams) { - ScoreRules_basics(0, 0, 0, FALSE); - ScoreInfo_SetLabel_PlayerScore(SP_KILLS, "frags", SFL_SORT_PRIO_PRIMARY); + if(inv_teams) { CheckAllowedTeams(world); } + ScoreRules_basics(inv_teams, 0, 0, FALSE); + if(inv_teams) ScoreInfo_SetLabel_TeamScore(ST_INV_KILLS, "frags", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_KILLS, "frags", ((inv_teams) ? SFL_SORT_PRIO_SECONDARY : SFL_SORT_PRIO_PRIMARY)); ScoreRules_basics_end(); } -void invasion_Initialize() +void invasion_DelayedInit() // Do this check with a delay so we can wait for teams to be set up. { + if(autocvar_g_invasion_teams) + invasion_teams = bound(2, autocvar_g_invasion_teams, 4); + else + invasion_teams = 0; + independent_players = 1; // to disable extra useless scores - invasion_ScoreRules(); + invasion_ScoreRules(invasion_teams); independent_players = 0; @@ -305,19 +422,42 @@ void invasion_Initialize() inv_maxrounds = 15; // 15? } +void invasion_Initialize() +{ + if(autocvar_g_invasion_zombies_only) + MON_ACTION(MON_ZOMBIE, MR_PRECACHE); + else + { + float i; + entity mon; + for(i = MON_FIRST; i <= MON_LAST; ++i) + { + mon = get_monsterinfo(i); + if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM)) + continue; // flying/swimming monsters not yet supported + + MON_ACTION(i, MR_PRECACHE); + } + } + + InitializeEntity(world, invasion_DelayedInit, INITPRIO_GAMETYPE); +} + MUTATOR_DEFINITION(gamemode_invasion) { MUTATOR_HOOK(MonsterDies, invasion_MonsterDies, CBC_ORDER_ANY); MUTATOR_HOOK(MonsterSpawn, invasion_MonsterSpawn, CBC_ORDER_ANY); MUTATOR_HOOK(OnEntityPreSpawn, invasion_OnEntityPreSpawn, CBC_ORDER_ANY); - MUTATOR_HOOK(PlayerPreThink, invasion_PlayerThink, CBC_ORDER_ANY); + MUTATOR_HOOK(SV_StartFrame, invasion_StartFrame, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerRegen, invasion_PlayerRegen, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerSpawn, invasion_PlayerSpawn, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerDamage_Calculate, invasion_PlayerDamage, CBC_ORDER_ANY); MUTATOR_HOOK(SV_ParseClientCommand, invasion_PlayerCommand, CBC_ORDER_ANY); + MUTATOR_HOOK(BotShouldAttack, invasion_BotShouldAttack, CBC_ORDER_ANY); MUTATOR_HOOK(SetStartItems, invasion_SetStartItems, CBC_ORDER_ANY); MUTATOR_HOOK(AccuracyTargetValid, invasion_AccuracyTargetValid, CBC_ORDER_ANY); MUTATOR_HOOK(AllowMobSpawning, invasion_AllowMobSpawning, CBC_ORDER_ANY); + MUTATOR_HOOK(GetTeamCount, invasion_GetTeamCount, CBC_ORDER_ANY); MUTATOR_ONADD { diff --git a/qcsrc/server/mutators/gamemode_invasion.qh b/qcsrc/server/mutators/gamemode_invasion.qh index 3438f4724..04492d4a3 100644 --- a/qcsrc/server/mutators/gamemode_invasion.qh +++ b/qcsrc/server/mutators/gamemode_invasion.qh @@ -6,4 +6,9 @@ float inv_numkilled; float inv_lastcheck; float inv_maxcurrent; +float invasion_teams; +float inv_monsters_perteam[17]; + float inv_monsterskill; + +#define ST_INV_KILLS 1 diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 932dcc12a..a0c80dbd2 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -198,6 +198,12 @@ void InitGameplayMode() if(g_invasion) { fraglimit_override = autocvar_g_invasion_point_limit; + if(autocvar_g_invasion_teams >= 2) + { + ActivateTeamplay(); + if(autocvar_g_invasion_team_spawns) + have_team_spawns = -1; // request team spawns + } MUTATOR_ADD(gamemode_invasion); } -- 2.39.2