/** * @param ent The actor to set the reaction fire for * @return @c true if the needed settings could have been made or settings are * already valid, @c false otherwise. */ static bool G_ReactionFireSetDefault (edict_t *ent) { const objDef_t *weapon; const invList_t *invList; actorHands_t hand = ACTOR_HAND_RIGHT; if (G_ActorHasWorkingFireModeSet(ent)) return true; invList = ACTOR_GET_INV(ent, hand); if (!invList) { hand = ACTOR_HAND_LEFT; invList = ACTOR_GET_INV(ent, hand); } weapon = INVSH_HasReactionFireEnabledWeapon(invList); if (!weapon) return false; ent->chr.RFmode.fmIdx = 0; ent->chr.RFmode.hand = hand; ent->chr.RFmode.weapon = weapon; if (!G_IsAI(ent)) G_EventReactionFireChange(ent); return true; }
/** * @param ent The actor to set the reaction fire for * @return @c true if the needed settings could have been made or settings are * already valid, @c false otherwise. */ static bool G_ReactionFireSetDefault (edict_t *ent) { if (G_ActorHasWorkingFireModeSet(ent)) return true; actorHands_t hand = ACTOR_HAND_RIGHT; const invList_t *invList = ACTOR_GET_INV(ent, hand); if (!invList) { hand = ACTOR_HAND_LEFT; invList = ACTOR_GET_INV(ent, hand); } const objDef_t *weapon = INVSH_HasReactionFireEnabledWeapon(invList); if (!weapon) return false; ent->chr.RFmode.set(hand, 0, weapon); /* no special firemode */ if (!G_ActorHasWorkingFireModeSet(ent)) return false; if (!G_IsAI(ent)) G_EventReactionFireChange(ent); return true; }
/** * @brief Build the forbidden list for the pathfinding (server side). * @param[in] team The team number if the list should be calculated from the eyes of that team. Use 0 to ignore team. * @param[in] movingActor The moving actor to build the forbidden list for. If this is an AI actor, everything other actor will be * included in the forbidden list - even the invisible ones. This is needed to ensure that they are not walking into each other * (civilians <=> aliens, aliens <=> civilians) * @sa G_MoveCalc * @sa Grid_CheckForbidden * @sa CL_BuildForbiddenList <- shares quite some code * @note This is used for pathfinding. * It is a list of where the selected unit can not move to because others are standing there already. */ static void G_BuildForbiddenList (int team, const edict_t *movingActor) { edict_t *ent = NULL; int visMask; forbiddenListLength = 0; /* team visibility */ if (team) visMask = G_TeamToVisMask(team); else visMask = TEAM_ALL; while ((ent = G_EdictsGetNextInUse(ent))) { /* Dead 2x2 unit will stop walking, too. */ if (G_IsBlockingMovementActor(ent) && (G_IsAI(movingActor) || (ent->visflags & visMask))) { forbiddenList[forbiddenListLength++] = ent->pos; forbiddenList[forbiddenListLength++] = (byte*) &ent->fieldSize; } else if (ent->type == ET_SOLID) { int j; for (j = 0; j < ent->forbiddenListSize; j++) { forbiddenList[forbiddenListLength++] = ent->forbiddenListPos[j]; forbiddenList[forbiddenListLength++] = (byte*) &ent->fieldSize; } } } if (forbiddenListLength > MAX_FORBIDDENLIST) gi.Error("G_BuildForbiddenList: list too long\n"); }
/** * @param ent The actor to set the reaction fire for * @return @c true if the needed settings could have been made or settings are * already valid, @c false otherwise. */ static bool G_ReactionFireSettingsSetDefault (Edict* ent) { if (G_ActorHasWorkingFireModeSet(ent)) return true; actorHands_t hand = ACTOR_HAND_RIGHT; const Item* item = ent->getHandItem(hand); if (!item) { hand = ACTOR_HAND_LEFT; item = ent->getHandItem(hand); } if (!item) return false; const objDef_t* weapon = item->getReactionFireWeaponType(); if (!weapon) return false; ent->chr.RFmode.set(hand, 0, weapon); /* no special firemode */ if (!G_ActorHasWorkingFireModeSet(ent)) return false; if (!G_IsAI(ent)) G_EventReactionFireChange(*ent); return true; }
/** * @brief Trigger to open the door we are standing in front of it * @sa CL_DoorOpen * @sa LE_CloseOpen * @sa CL_ActorDoorAction * @sa AI_CheckUsingDoor */ static bool Touch_DoorTrigger (edict_t *self, edict_t *activator) { if (self->owner && self->owner->inuse) { if (G_IsAI(activator)) { /* let the ai interact with the door */ if (self->flags & FL_GROUPSLAVE) self = self->groupMaster; if (AI_CheckUsingDoor(activator, self->owner)) G_ActorUseDoor(activator, self->owner); /* we don't want the client action stuff to be send for ai actors */ return false; } else { /* prepare for client action */ G_ActorSetClientAction(activator, self->owner); return true; } } return false; }
/** * @brief Make the actor use (as in open/close) a door edict * @note Will also check whether the door is still reachable (this might have * changed due to the rotation) after the usage * @param actor The actor that is using the door * @param door The door that should be opened/closed */ void G_ActorUseDoor (Edict* actor, Edict* door) { /* check whether it's part of an edict group but not the master */ if (door->flags & FL_GROUPSLAVE) door = door->groupMaster; if (!G_ClientUseEdict(actor->getPlayer(), actor, door)) return; /* end this loop here, for the AI this is a) not interesting, * and b) could result in endless loops */ if (G_IsAI(actor)) return; Edict* closeActor = nullptr; while ((closeActor = G_FindRadius(closeActor, door->origin, UNIT_SIZE * 3))) { /* check whether the door is still reachable (this might have * changed due to the rotation) or whether an actor can reach it now */ G_TouchTriggers(closeActor); } }
/** * @brief Function to calculate possible damages for mock pseudoaction. * @param[in,out] mock Pseudo action - only for calculating mock values - nullptr for real action. * @param[in] shooter Pointer to attacker for this mock pseudoaction. * @param[in] struck Pointer to victim of this mock pseudoaction. * @param[in] damage Updates mock value of damage. * @note Called only from G_Damage(). * @sa G_Damage */ static void G_UpdateShotMock (shot_mock_t* mock, const Edict* shooter, const Edict* struck, int damage) { assert(!struck->isSameAs(shooter) || mock->allow_self); if (damage <= 0) return; if (!struck->inuse || G_IsDead(struck)) return; if (!G_IsAI(shooter) && !G_IsVisibleForTeam(struck, shooter->getTeam())) return; if (G_IsCivilian(struck)) mock->civilian += 1; else if (struck->isSameTeamAs(shooter)) mock->friendCount += 1; else if (G_IsActor(struck)) mock->enemyCount += 1; else return; mock->damage += damage; }
/** * @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; } } }
/** * @brief Handles the end of a match * @param[in] team The winning team number * @param[in] nextmap Is there a follow-up map within the same match ? * @sa G_RunFrame * @sa CL_ParseResults */ static void G_MatchSendResults (int team, bool nextmap) { edict_t *ent, *attacker; int i, j = 0; attacker = NULL; ent = NULL; /* Calculate new scores/skills for the soldiers. */ while ((ent = G_EdictsGetNextLivingActor(ent))) { if (!G_IsAI(ent)) G_UpdateCharacterExperience(ent); else if (ent->team == team) attacker = ent; } /* if aliens won, make sure every soldier that is not in the rescue zone dies */ if (team == TEAM_ALIEN) { ent = NULL; while ((ent = G_EdictsGetNextLivingActor(ent))) if (ent->team != team && !G_ActorIsInRescueZone(ent)) { ent->HP = 0; G_ActorDieOrStun(ent, attacker); } } G_VisMakeEverythingVisible(); /* send results */ G_EventAdd(PM_ALL, EV_RESULTS, -1); gi.WriteByte(MAX_TEAMS); gi.WriteByte(team); gi.WriteByte(nextmap); for (i = 0; i < MAX_TEAMS; i++) { gi.WriteByte(level.num_spawned[i]); gi.WriteByte(level.num_alive[i]); } for (i = 0; i <= MAX_TEAMS; i++) for (j = 0; j < MAX_TEAMS; j++) gi.WriteByte(level.num_kills[i][j]); for (i = 0; i <= MAX_TEAMS; i++) for (j = 0; j < MAX_TEAMS; j++) gi.WriteByte(level.num_stuns[i][j]); /* how many actors */ j = 0; ent = NULL; while ((ent = G_EdictsGetNextActor(ent))) if (!G_IsAI(ent)) j++; /* number of soldiers */ gi.WriteByte(j); if (j) { ent = NULL; while ((ent = G_EdictsGetNextActor(ent))) { if (!G_IsAI(ent)) { G_SendCharacterData(ent); } } } G_EventEnd(); }
/** * @brief Handles the end of a match * @param[in] team The winning team number * @param[in] nextmap Is there a follow-up map within the same match ? * @sa G_RunFrame * @sa CL_ParseResults */ static void G_MatchSendResults (int team, bool nextmap) { Edict* attacker = nullptr; Actor* actor = nullptr; /* Calculate new scores/skills for the soldiers. */ while ((actor = G_EdictsGetNextLivingActor(actor))) { if (!G_IsAI(actor)) G_UpdateCharacterExperience(actor); else if (actor->getTeam() == team) attacker = actor; } /* if aliens won, make sure every soldier that is not in the rescue zone dies */ if (team == TEAM_ALIEN) { actor = nullptr; while ((actor = G_EdictsGetNextLivingActor(actor))) if (actor->getTeam() != team && !actor->isInRescueZone()) { actor->HP = 0; G_ActorDieOrStun(actor, attacker); } } G_VisMakeEverythingVisible(); /* send results */ G_EventAdd(PM_ALL, EV_RESULTS, -1); gi.WriteByte(MAX_TEAMS); gi.WriteByte(team); gi.WriteByte(nextmap); for (int i = 0; i < MAX_TEAMS; i++) { gi.WriteByte(level.num_spawned[i]); gi.WriteByte(level.num_alive[i]); } for (int i = 0; i <= MAX_TEAMS; i++) for (int j = 0; j < MAX_TEAMS; j++) gi.WriteByte(level.num_kills[i][j]); for (int i = 0; i <= MAX_TEAMS; i++) for (int j = 0; j < MAX_TEAMS; j++) gi.WriteByte(level.num_stuns[i][j]); /* how many actors */ int n = 0; actor = nullptr; while ((actor = G_EdictsGetNextActor(actor))) if (!G_IsAI(actor)) n++; /* number of soldiers */ gi.WriteByte(n); if (n) { actor = nullptr; while ((actor = G_EdictsGetNextActor(actor))) { if (!G_IsAI(actor)) { G_SendCharacterData(actor); } } } G_EventEnd(); }