X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fmutators%2Fgamemode_invasion.qc;h=5720e55d85d68a710d29fc1774e597298a05dfe6;hb=6f37a8f8076a572097afb13de2c367a72717c927;hp=494afc48baa77bee80e882afa66d74ac7f68f9f3;hpb=490131c400cb22079f9713ecb2d2830b84118987;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/mutators/gamemode_invasion.qc b/qcsrc/server/mutators/gamemode_invasion.qc index 494afc48b..5720e55d8 100644 --- a/qcsrc/server/mutators/gamemode_invasion.qc +++ b/qcsrc/server/mutators/gamemode_invasion.qc @@ -1,8 +1,20 @@ -void invasion_spawnpoint() +#include "gamemode_invasion.qh" +#include "../_.qh" + +#include "gamemode.qh" + +#include "../../common/monsters/spawn.qh" +#include "../../common/monsters/sv_monsters.qh" + +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 +33,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; @@ -34,7 +46,10 @@ entity invasion_PickSpawn() RandomSelection_Init(); for(e = world;(e = find(e, classname, "invasion_spawnpoint")); ) - 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; } @@ -47,14 +62,55 @@ 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); + + 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); + + 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 = spawnmonster("", mon, spawn_point, spawn_point, spawn_point.origin, FALSE, 2); + monster.monster_attack = false; // it's the player's job to kill all the monsters - if(roundcnt >= maxrounds) - monster.spawnflags |= MONSTERFLAG_MINIBOSS; + if(inv_roundcnt >= inv_maxrounds) + monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses } void invasion_SpawnMonsters(float supermonster_count) @@ -72,62 +128,70 @@ float Invasion_CheckWinner() FOR_EACH_MONSTER(head) monster_remove(head); - 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; } - // boss round - if(roundcnt >= maxrounds) + 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(numspawned < 1) + if((get_monsterinfo(head.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) + ++supermonster_count; + ++total_alive_monsters; + + if(teamplay) + switch(head.team) { - maxspawned = 1; - invasion_SpawnMonsters(0); + 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; } } - else - { - float total_alive_monsters = 0, supermonster_count = 0; - FOR_EACH_MONSTER(head) if(head.health > 0) + if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned) + { + if(time >= inv_lastcheck) { - if((get_monsterinfo(head.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) - ++supermonster_count; - ++total_alive_monsters; + invasion_SpawnMonsters(supermonster_count); + inv_lastcheck = time + autocvar_g_invasion_spawn_delay; } - if((total_alive_monsters + numkilled) < maxspawned && maxcurrent < 10) // 10 at a time should be plenty - { - if(time >= last_check) - { - invasion_SpawnMonsters(supermonster_count); - last_check = time + 2; - } - - return 0; - } + return 0; } - if(numspawned < 1 || numkilled < maxspawned) - return 0; // nothing has spawned yet, or there are still alive monsters + if(inv_numspawned < 1) + return 0; // nothing has spawned yet - if(roundcnt >= maxrounds) + if(teamplay) { - NextLevel(); - return 1; + 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); @@ -141,7 +205,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); @@ -154,7 +226,7 @@ float Invasion_CheckWinner() float Invasion_CheckPlayers() { - return TRUE; + return true; } void Invasion_RoundStart() @@ -167,79 +239,97 @@ void Invasion_RoundStart() ++numplayers; } - roundcnt += 1; + if(inv_roundcnt < inv_maxrounds) + inv_roundcnt += 1; // a limiter to stop crazy counts - invasion_monsterskill = roundcnt + (numplayers * 0.3); + inv_monsterskill = inv_roundcnt + max(1, numplayers * 0.3); - maxcurrent = 0; - numspawned = 0; - numkilled = 0; + inv_maxcurrent = 0; + inv_numspawned = 0; + inv_numkilled = 0; - if(roundcnt > 1) - maxspawned = rint(autocvar_g_invasion_monster_count * (roundcnt * 0.5)); - else - maxspawned = autocvar_g_invasion_monster_count; + 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) { if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) { - numkilled += 1; - maxcurrent -= 1; + 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; + return false; } MUTATOR_HOOKFUNCTION(invasion_MonsterSpawn) { if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) - { - monster_remove(self); - return FALSE; - } - - if(roundcnt < maxrounds && self.spawnflags & MONSTERFLAG_MINIBOSS) - self.spawnflags &= ~MONSTERFLAG_MINIBOSS; + return true; if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) { - numspawned += 1; - maxcurrent += 1; + inv_numspawned += 1; + inv_maxcurrent += 1; } - self.monster_skill = invasion_monsterskill; + self.monster_skill = inv_monsterskill; if((get_monsterinfo(self.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, M_NAME(self.monsterid)); self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP; - return FALSE; + return false; +} + +MUTATOR_HOOKFUNCTION(invasion_OnEntityPreSpawn) +{ + if(startsWith(self.classname, "monster_")) + if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) + return true; + + return false; } -MUTATOR_HOOKFUNCTION(invasion_PlayerThink) +MUTATOR_HOOKFUNCTION(invasion_StartFrame) { - monsters_total = maxspawned; // TODO: make sure numspawned never exceeds maxspawned - monsters_killed = numkilled; + monsters_total = inv_maxspawned; // TODO: make sure numspawned never exceeds maxspawned + monsters_killed = inv_numkilled; - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(invasion_PlayerRegen) { // no regeneration in invasion - return TRUE; + return true; } MUTATOR_HOOKFUNCTION(invasion_PlayerSpawn) { - self.bot_attack = FALSE; - return FALSE; + self.bot_attack = false; + return false; } MUTATOR_HOOKFUNCTION(invasion_PlayerDamage) @@ -250,28 +340,36 @@ MUTATOR_HOOKFUNCTION(invasion_PlayerDamage) frag_force = '0 0 0'; } - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(invasion_PlayerCommand) { if(MUTATOR_RETURNVALUE) // command was already handled? - return FALSE; + return false; if(cmd_name == "debuginvasion") { - sprint(self, strcat("maxspawned = ", ftos(maxspawned), "\n")); - sprint(self, strcat("numspawned = ", ftos(numspawned), "\n")); - sprint(self, strcat("numkilled = ", ftos(numkilled), "\n")); - sprint(self, strcat("roundcnt = ", ftos(roundcnt), "\n")); + sprint(self, strcat("inv_maxspawned = ", ftos(inv_maxspawned), "\n")); + sprint(self, strcat("inv_numspawned = ", ftos(inv_numspawned), "\n")); + sprint(self, strcat("inv_numkilled = ", ftos(inv_numkilled), "\n")); + sprint(self, strcat("inv_roundcnt = ", ftos(inv_roundcnt), "\n")); sprint(self, strcat("monsters_total = ", ftos(monsters_total), "\n")); sprint(self, strcat("monsters_killed = ", ftos(monsters_killed), "\n")); - sprint(self, strcat("invasion_monsterskill = ", ftos(invasion_monsterskill), "\n")); + sprint(self, strcat("inv_monsterskill = ", ftos(inv_monsterskill), "\n")); - return TRUE; + return true; } - return FALSE; + return false; +} + +MUTATOR_HOOKFUNCTION(invasion_BotShouldAttack) +{ + if(!(checkentity.flags & FL_MONSTER)) + return true; + + return false; } MUTATOR_HOOKFUNCTION(invasion_SetStartItems) @@ -279,7 +377,7 @@ MUTATOR_HOOKFUNCTION(invasion_SetStartItems) start_health = 200; start_armorvalue = 200; - return FALSE; + return false; } MUTATOR_HOOKFUNCTION(invasion_AccuracyTargetValid) @@ -292,44 +390,80 @@ MUTATOR_HOOKFUNCTION(invasion_AccuracyTargetValid) MUTATOR_HOOKFUNCTION(invasion_AllowMobSpawning) { // monster spawning disabled during an invasion - return TRUE; + return true; +} + +MUTATOR_HOOKFUNCTION(invasion_GetTeamCount) +{ + ret_float = invasion_teams; + return false; } -void invasion_ScoreRules() +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; 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; + inv_roundcnt = 0; + 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); + } + } - roundcnt = 0; + 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(PlayerPreThink, invasion_PlayerThink, CBC_ORDER_ANY); + MUTATOR_HOOK(OnEntityPreSpawn, invasion_OnEntityPreSpawn, 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 {