/** * @brief Checks whether there are still actors to fight with left. If none * are the match end will be triggered. * @sa G_MatchEndTrigger */ void G_MatchEndCheck (void) { int activeTeams; int i, last; if (level.intermissionTime > 0.0) /* already decided */ return; if (!level.numplayers) { G_MatchEndTrigger(0, 0); return; } /** @todo count from 0 to get the civilians for objectives */ for (i = 1, activeTeams = 0, last = 0; i < MAX_TEAMS; i++) { edict_t *ent = NULL; /* search for living but not stunned actors - there must at least be one actor * that is still able to attack or defend himself */ while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, i)) != NULL) { if (!G_IsStunned(ent)) { last = i; activeTeams++; break; } } } /** @todo < 2 does not work when we count civilians */ /* prepare for sending results */ if (activeTeams < 2) { const int timeGap = (level.activeTeam == TEAM_ALIEN ? 10.0 : 3.0); G_MatchEndTrigger(activeTeams == 1 ? last : 0, timeGap); } }
/** * @brief Call the end game function with the given team * used to e.g. abort singleplayer games and let the aliens win * @sa G_MatchEndTrigger */ static void SVCmd_Win_f (void) { int team; if (gi.Cmd_Argc() < 3) { gi.DPrintf("Usage: %s <teamnum>\n", gi.Cmd_Argv(1)); return; } team = atoi(gi.Cmd_Argv(2)); if (team > TEAM_CIVILIAN && team < MAX_TEAMS) G_MatchEndTrigger(team, 0); else gi.DPrintf("Bad team number.\n"); }
/** * @brief Next map trigger that is going to get active once all opponents are killed * @sa SP_trigger_nextmap */ static bool Touch_NextMapTrigger (Edict* self, Edict* activator) { if (activator != nullptr && activator->isSameTeamAs(self)) { char command[MAX_VAR]; self->inuse = false; G_ClientPrintf(activator->getPlayer(), PRINT_HUD, _("Switching map!")); Com_sprintf(command, sizeof(command), "map %s %s\n", level.day ? "day" : "night", self->nextmap); level.mapEndCommand = (char*)G_TagMalloc(strlen(command) + 1, TAG_GAME); Q_strncpyz(level.mapEndCommand, command, strlen(command)); level.nextMapSwitch = true; G_MatchEndTrigger(self->getTeam(), 0); } return true; }
/** * @note Think functions are only executed when the match is running * or in other word, the game has started */ void G_MissionThink (Edict* self) { if (!G_MatchIsRunning()) return; /* when every player has joined the match - spawn the mission target * particle (if given) to mark the trigger */ if (self->particle) { self->link = G_SpawnParticle(self->origin, self->spawnflags, self->particle); /* This is automatically freed on map shutdown */ self->particle = nullptr; } Edict* chain = self->groupMaster; if (!chain) chain = self; while (chain) { if (chain->type == ET_MISSION) { if (chain->item) { const Item* ic; G_GetFloorItems(chain); ic = chain->getFloor(); if (!ic) { /* reset the counter if there is no item */ chain->count = 0; return; } for (; ic; ic = ic->getNext()) { const objDef_t* od = ic->def(); assert(od); /* not the item we are looking for */ if (Q_streq(od->id, chain->item)) break; } if (!ic) { /* reset the counter if it's not the searched item */ chain->count = 0; return; } } if (chain->time) { /* Check that the target zone is still occupied (last defender might have died) */ if (!chain->item && !G_MissionIsTouched(chain)) { chain->count = 0; } const int endTime = level.actualRound - chain->count; const int spawnIndex = (chain->getTeam() + level.teamOfs) % MAX_TEAMS; const int currentIndex = (level.activeTeam + level.teamOfs) % MAX_TEAMS; /* not every edict in the group chain has * been occupied long enough */ if (!chain->count || endTime < chain->time || (endTime == chain->time && spawnIndex < currentIndex)) return; } if (chain->target && !chain->time && !chain->item) { if (!G_MissionIsTouched(chain)) return; } } chain = chain->groupChain; } const bool endMission = self->target == nullptr; /* store team before the edict is released */ const int team = self->getTeam(); chain = self->groupMaster; if (!chain) chain = self; while (chain) { if (chain->type == ET_MISSION) { G_UseEdict(chain, nullptr); if (chain->item != nullptr) { Edict* item = G_GetEdictFromPos(chain->pos, ET_ITEM); if (item != nullptr) { if (!G_InventoryRemoveItemByID(chain->item, item, CID_FLOOR)) { Com_Printf("Could not remove item '%s' from floor edict %i\n", chain->item, item->getIdNum()); } else if (!item->getFloor()) { G_EventPerish(*item); G_FreeEdict(item); } } } if (chain->link != nullptr) { Edict* particle = G_GetEdictFromPos(chain->pos, ET_PARTICLE); if (particle != nullptr) { G_AppearPerishEvent(G_VisToPM(particle->visflags), false, *particle, nullptr); G_FreeEdict(particle); } chain->link = nullptr; } /* Display mission message */ if (G_ValidMessage(chain)) { const char* msg = chain->message; if (msg[0] == '_') ++msg; gi.BroadcastPrintf(PRINT_HUD, "%s", msg); } } Edict* ent = chain->groupChain; /* free the group chain */ G_FreeEdict(chain); chain = ent; } if (endMission) G_MatchEndTrigger(team, level.activeTeam == TEAM_ALIEN ? 10 : 3); }
/** * @sa G_PlayerSoldiersCount */ void G_ClientEndRound (Player& player) { Player* p; const int lastTeamIndex = (G_GetActiveTeam() + level.teamOfs) % MAX_TEAMS; if (!G_IsAIPlayer(&player)) { /* inactive players can't end their inactive turn :) */ if (level.activeTeam != player.getTeam()) return; /* check for "team oszillation" */ if (level.framenum < level.nextEndRound) return; level.nextEndRound = level.framenum + 20; } /* only use this for teamplay matches like coopX or fight2on2 and above * also skip this for ai players, this is only called when all ai actors * have finished their 'thinking' */ if (!G_IsAIPlayer(&player) && sv_teamplay->integer) { /* check if all team members are ready */ if (!player.roundDone) { player.roundDone = true; G_EventEndRoundAnnounce(player); G_EventEnd(); } p = nullptr; while ((p = G_PlayerGetNextActiveHuman(p))) if (p->getTeam() == level.activeTeam && !p->roundDone && G_PlayerSoldiersCount(*p) > 0) return; p = nullptr; while ((p = G_PlayerGetNextActiveAI(p))) if (p->getTeam() == level.activeTeam && !p->roundDone && G_PlayerSoldiersCount(*p) > 0) return; } else { player.roundDone = true; } /* clear any remaining reaction fire */ G_ReactionFireOnEndTurn(); if (!G_IsAIPlayer(&player)) { if (g_lastseen->integer > 0) { Edict* ent = nullptr; while ((ent = G_EdictsGetNextActor(ent))) { if (G_IsAI(ent) && G_IsVisibleForTeam(ent, level.activeTeam)) { player.lastSeen = level.actualRound; break; } } if (level.actualRound - player.lastSeen > g_lastseen->integer) { Com_Printf("round end triggered by g_lastseen (player %i (team %i) last seen in round %i of %i rounds)\n", player.getNum(), level.activeTeam, player.lastSeen, level.actualRound); G_MatchEndTrigger(-1, 0); } } } /* let all the invisible players perish now */ G_CheckVisTeamAll(level.activeTeam, VIS_APPEAR, nullptr); G_GetNextActiveTeam(); AI_CheckRespawn(TEAM_ALIEN); /* no other team left? */ if (!G_MatchIsRunning()) return; if (lastTeamIndex > (level.activeTeam + level.teamOfs) % MAX_TEAMS) level.actualRound++; /* communicate next player in row to clients */ G_EventEndRound(); /* store the round start time to be able to abort the round after a give time */ level.roundstartTime = level.time; /* Wounded team members bleed */ G_BleedWounds(level.activeTeam); /* Update the state of stuned team-members. The actual statistics are sent below! */ G_UpdateStunState(level.activeTeam); /* Give the actors of the now active team their TUs. */ G_GiveTimeUnits(level.activeTeam); /* apply morale behaviour, reset reaction fire */ G_ReactionFireReset(level.activeTeam); if (mor_panic->integer) G_MoraleBehaviour(level.activeTeam); G_UpdateCarriedWeight(level.activeTeam); /* start ai - there is only one player for ai teams, and the last pointer must only * be updated for ai players */ p = G_GetPlayerForTeam(level.activeTeam); if (p == nullptr) gi.Error("Could not find player for team %i", level.activeTeam); /* finish off events */ G_EventEnd(); /* reset ready flag for every player on the current team (even ai players) */ p = nullptr; while ((p = G_PlayerGetNextActiveHuman(p))) { if (p->getTeam() == level.activeTeam) { p->roundDone = false; } } p = nullptr; while ((p = G_PlayerGetNextActiveAI(p))) { if (p->getTeam() == level.activeTeam) { p->roundDone = false; } } }
/** * @note Think functions are only executed when the match is running * or in other word, the game has started */ void G_MissionThink (Edict* self) { if (!G_MatchIsRunning()) return; /* when every player has joined the match - spawn the mission target * particle (if given) to mark the trigger */ if (self->particle) { self->link = G_SpawnParticle(self->origin, self->spawnflags, self->particle); /* This is automatically freed on map shutdown */ self->particle = nullptr; } Edict* chain = self->groupMaster; if (!chain) chain = self; while (chain) { if (chain->type == ET_MISSION) { if (chain->item) { const Item* ic; G_GetFloorItems(chain); ic = chain->getFloor(); if (!ic) { /* reset the counter if there is no item */ chain->count = 0; return; } for (; ic; ic = ic->getNext()) { const objDef_t* od = ic->def(); assert(od); /* not the item we are looking for */ if (Q_streq(od->id, chain->item)) break; } if (!ic) { /* reset the counter if it's not the searched item */ chain->count = 0; return; } } if (chain->time) { const int endTime = level.actualRound - chain->count; const int spawnIndex = (self->getTeam() + level.teamOfs) % MAX_TEAMS; const int currentIndex = (level.activeTeam + level.teamOfs) % MAX_TEAMS; /* not every edict in the group chain has * been occupied long enough */ if (!chain->count || endTime < chain->time || (endTime == level.actualRound && spawnIndex <= currentIndex)) return; } /* not destroyed yet */ if ((chain->flags & FL_DESTROYABLE) && chain->HP) return; } chain = chain->groupChain; } if (self->use) self->use(self, nullptr); /* store team before the edict is released */ const int team = self->getTeam(); chain = self->groupMaster; if (!chain) chain = self; while (chain) { if (chain->item != nullptr) { Edict* item = G_GetEdictFromPos(chain->pos, ET_ITEM); if (item != nullptr) { if (!G_InventoryRemoveItemByID(chain->item, item, CID_FLOOR)) { Com_Printf("Could not remove item '%s' from floor edict %i\n", chain->item, item->getIdNum()); } else if (!item->getFloor()) { G_EventPerish(*item); G_FreeEdict(item); } } } if (chain->link != nullptr) { Edict* particle = G_GetEdictFromPos(chain->pos, ET_PARTICLE); if (particle != nullptr) { G_AppearPerishEvent(PM_ALL, false, *particle, nullptr); G_FreeEdict(particle); } chain->link = nullptr; } Edict* ent = chain->groupChain; /* free the trigger */ if (chain->child()) G_FreeEdict(chain->child()); /* free the group chain */ G_FreeEdict(chain); chain = ent; } self = nullptr; /* still active mission edicts left */ Edict* ent = nullptr; while ((ent = G_EdictsGetNextInUse(ent))) if (ent->type == ET_MISSION && ent->getTeam() == team) return; G_MatchEndTrigger(team, 10); }
/** * @note Think functions are only executed when the match is running * or in other word, the game has started */ void G_MissionThink (edict_t *self) { edict_t *chain = self->groupMaster; edict_t *ent; int team; if (!G_MatchIsRunning()) return; /* when every player has joined the match - spawn the mission target * particle (if given) to mark the trigger */ if (self->particle) { G_SpawnParticle(self->origin, self->spawnflags, self->particle); /* This is automatically freed on map shutdown */ self->particle = NULL; } if (!chain) chain = self; while (chain) { if (chain->type == ET_MISSION) { if (chain->item) { const invList_t *ic; G_GetFloorItems(chain); ic = FLOOR(chain); if (!ic) { /* reset the counter if there is no item */ chain->count = 0; return; } for (; ic; ic = ic->next) { const objDef_t *od = ic->item.t; assert(od); /* not the item we are looking for */ if (Q_streq(od->id, chain->item)) break; } if (!ic) { /* reset the counter if it's not the searched item */ chain->count = 0; return; } } if (chain->time) { /* not every edict in the group chain has * been occupied long enough */ if (!chain->count || level.actualRound - chain->count < chain->time) return; } /* not destroyed yet */ if ((chain->flags & FL_DESTROYABLE) && chain->HP) return; } chain = chain->groupChain; } if (self->use) self->use(self, NULL); /* store team before the edict is released */ team = self->team; chain = self->groupMaster; if (!chain) chain = self; while (chain) { if (chain->item != NULL) { edict_t *item = G_GetEdictFromPos(chain->pos, ET_ITEM); if (item != NULL) { if (!G_InventoryRemoveItemByID(chain->item, item, gi.csi->idFloor)) { Com_Printf("Could not remove item '%s' from floor edict %i\n", chain->item, item->number); } else { G_AppearPerishEvent(G_VisToPM(item->visflags), false, item, NULL); } } } if (chain->particle != NULL) { /** @todo not yet working - particle stays active */ edict_t *particle = G_GetEdictFromPos(chain->pos, ET_PARTICLE); if (particle != NULL) { G_AppearPerishEvent(PM_ALL, false, particle, NULL); G_FreeEdict(particle); } } ent = chain->groupChain; /* free the trigger */ if (chain->child) G_FreeEdict(chain->child); /* free the group chain */ G_FreeEdict(chain); chain = ent; } self = NULL; /* still active mission edicts left */ ent = NULL; while ((ent = G_EdictsGetNextInUse(ent))) if (ent->type == ET_MISSION && ent->team == team) return; G_MatchEndTrigger(team, 10); }