/** * @brief Checks whether the actor should stop movement * @param ent The actors edict * @param visState The visibility check state @c VIS_PERISH, @c VIS_APPEAR * @return @c true if the actor should stop movement, @c false otherwise */ static bool G_ActorShouldStopInMidMove (const edict_t *ent, int visState, dvec_t* dvtab, int max) { if (visState & VIS_STOP) return true; /* check that the appearing unit is not on a grid position the actor wanted to walk to. * this might be the case if the edict got visible in mid mode */ if (visState & VIS_APPEAR) { pos3_t pos; VectorCopy(ent->pos, pos); while (max >= 0) { int tmp = 0; const edict_t *blockEdict; PosAddDV(pos, tmp, dvtab[max]); max--; blockEdict = G_GetLivingActorFromPos(pos); if (blockEdict && G_IsBlockingMovementActor(blockEdict)) { const bool visible = G_IsVisibleForTeam(blockEdict, ent->team); if (visible) return true; } } } return false; }
/** * @brief Check whether ent can reaction fire at target, i.e. that it can see it and neither is dead etc. * @param[in] ent The entity that might be firing * @param[in] target The entity that might be fired at * @return @c true if 'ent' can actually fire at 'target', @c false otherwise */ static bool G_ReactionFireIsPossible (const edict_t *ent, const edict_t *target) { float actorVis; bool frustum; /* an entity can't reaction fire at itself */ if (ent == target) return false; /* Don't react in your own turn */ if (ent->team == level.activeTeam) return false; /* ent can't use RF if is in STATE_DAZED (flashbang impact) */ if (G_IsDazed(ent)) return false; if (G_IsDead(target)) return false; /* check ent has reaction fire enabled */ if (!G_IsShaken(ent) && !G_IsReaction(ent)) return false; /* check ent has weapon in RF hand */ /* @todo Should this situation even happen when G_IsReaction(ent) is true? */ if (!ACTOR_GET_INV(ent, ent->chr.RFmode.hand)) { /* print character info if this happens, for now */ gi.DPrintf("Reaction fire enabled but no weapon for hand (name=%s,hand=%i,fmIdx=%i)\n", ent->chr.name, ent->chr.RFmode.hand, ent->chr.RFmode.fmIdx); return false; } if (!G_IsVisibleForTeam(target, ent->team)) return false; /* If reaction fire is triggered by a friendly unit * and the shooter is still sane, don't shoot; * well, if the shooter isn't sane anymore... */ if (G_IsCivilian(target) || target->team == ent->team) if (!G_IsShaken(ent) || (float) ent->morale / mor_shaken->value > frand()) return false; /* check in range and visible */ if (VectorDistSqr(ent->origin, target->origin) > MAX_SPOT_DIST * MAX_SPOT_DIST) return false; frustum = G_FrustumVis(ent, target->origin); if (!frustum) return false; actorVis = G_ActorVis(ent->origin, ent, target, true); if (actorVis <= 0.2) return false; /* okay do it then */ return true; }
/** * @brief Check whether ent can reaction fire at target, i.e. that it can see it and neither is dead etc. * @param[in] ent The entity that might be firing * @param[in] target The entity that might be fired at * @return @c true if 'ent' can actually fire at 'target', @c false otherwise */ static bool G_ReactionFireIsPossible (Edict *ent, const Edict *target) { /* an entity can't reaction fire at itself */ if (ent == target) return false; /* Don't react in your own turn */ if (ent->team == level.activeTeam) return false; /* ent can't use RF if is in STATE_DAZED (flashbang impact) */ if (G_IsDazed(ent)) return false; if (G_IsDead(target)) return false; /* check ent has reaction fire enabled */ if (!G_IsReaction(ent)) return false; /* check ent has weapon in RF hand */ if (!ent->getHandItem(ent->chr.RFmode.getHand())) { /* print character info if this happens, for now */ gi.DPrintf("Reaction fire enabled but no weapon for hand (name=%s,entnum=%i,hand=%i,fmIdx=%i)\n", ent->chr.name, ent->number, ent->chr.RFmode.getHand(), ent->chr.RFmode.getFmIdx()); G_RemoveReaction(ent); return false; } if (!G_IsVisibleForTeam(target, ent->team)) return false; /* If reaction fire is triggered by a friendly unit * and the shooter is still sane, don't shoot; * well, if the shooter isn't sane anymore... */ if (G_IsCivilian(target) || target->team == ent->team) if (!G_IsShaken(ent) || (float) ent->morale / mor_shaken->value > frand()) return false; /* check in range and visible */ const int spotDist = G_VisCheckDist(ent); if (VectorDistSqr(ent->origin, target->origin) > spotDist * spotDist) return false; const bool frustum = G_FrustumVis(ent, target->origin); if (!frustum) return false; const float actorVis = G_ActorVis(ent->origin, ent, target, true); if (actorVis <= 0.2) return false; /* okay do it then */ return true; }
/** * @brief Check whether ent can reaction fire at target, i.e. that it can see it and neither is dead etc. * @param[in] ent The entity that might be firing * @param[in] target The entity that might be fired at * @return @c true if 'ent' can actually fire at 'target', @c false otherwise */ static qboolean G_ReactionFireIsPossible (const edict_t *ent, const edict_t *target) { float actorVis; qboolean frustum; /* an entity can't reaction fire at itself */ if (ent == target) return qfalse; /* Don't react in your own turn */ if (ent->team == level.activeTeam) return qfalse; /* ent can't use RF if is in STATE_DAZED (flashbang impact) */ if (G_IsDazed(ent)) return qfalse; if (G_IsDead(target)) return qfalse; /* check ent has reaction fire enabled */ if (!G_IsShaken(ent) && !G_IsReaction(ent)) return qfalse; if (!G_IsVisibleForTeam(target, ent->team)) return qfalse; /* If reaction fire is triggered by a friendly unit * and the shooter is still sane, don't shoot; * well, if the shooter isn't sane anymore... */ if (G_IsCivilian(target) || target->team == ent->team) if (!G_IsShaken(ent) || (float) ent->morale / mor_shaken->value > frand()) return qfalse; /* check in range and visible */ if (VectorDistSqr(ent->origin, target->origin) > MAX_SPOT_DIST * MAX_SPOT_DIST) return qfalse; frustum = G_FrustumVis(ent, target->origin); if (!frustum) return qfalse; actorVis = G_ActorVis(ent->origin, target, qtrue); if (actorVis <= 0.2) return qfalse; /* okay do it then */ return qtrue; }
/** * @brief This function sends all the actors to the client that are not visible * initially - this is needed because an actor can e.g. produce sounds that are * send over the net. And the client can only handle them if he knows the * @c le_t (local entity) already * @note Call this for the first @c G_CheckVis call for every new * actor or player * @sa G_CheckVis * @sa CL_ActorAdd */ void G_SendInvisible (const player_t* player) { const int team = player->pers.team; assert(team != TEAM_NO_ACTIVE); if (level.num_alive[team]) { edict_t* ent = NULL; /* check visibility */ while ((ent = G_EdictsGetNextActor(ent))) { if (ent->team != team) { /* not visible for this team - so add the le only */ if (!G_IsVisibleForTeam(ent, team)) { G_EventActorAdd(G_PlayerToPM(player), ent); } } } } }
/** * @brief Check whether shooter can see his target well enough * @param[in] shooter The entity that might be firing * @param[in] target The entity that might be fired at */ bool ReactionFire::canSee (const Actor* shooter, const Edict* target) const { if (!G_IsVisibleForTeam(target, shooter->getTeam())) return false; /* check in range and visible */ const int spotDist = G_VisCheckDist(shooter); if (VectorDistSqr(shooter->origin, target->origin) > spotDist * spotDist) return false; const bool frustum = G_FrustumVis(shooter, target->origin); if (!frustum) return false; const float actorVis = G_ActorVis(shooter->origin, shooter, target, true); if (actorVis < 0.1) return false; return true; }
/** * @brief Function to calculate possible damages for mock pseudoaction. * @param[in,out] mock Pseudo action - only for calculating mock values - NULL 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_t *shooter, const edict_t *struck, int damage) { assert(struck->number != shooter->number || mock->allow_self); if (damage > 0) { if (!struck->inuse || G_IsDead(struck)) return; else if (!G_IsVisibleForTeam(struck, shooter->team)) return; else if (G_IsCivilian(struck)) mock->civilian += 1; else if (struck->team == shooter->team) mock->friendCount += 1; else if (G_IsActor(struck)) mock->enemyCount += 1; else return; mock->damage += damage; } }
/** * @brief test if @c check is visible by team (or if visibility changed?) * @sa G_CheckVisTeam * @param[in] team the team the edict may become visible for or perish from * their view * @param[in] check the edict we are talking about - which may become visible * or perish * @param[in] flags if you want to check whether the edict may also perish from * other players view, you should use the @c VT_PERISH bits * @note If the edict is already visible and flags doesn't contain the * bits of @c VT_PERISH, no further checks are performed - only the * @c VIS_YES bits are returned */ int G_TestVis (const int team, edict_t * check, int flags) { edict_t *from = NULL; /* store old flag */ const int old = G_IsVisibleForTeam(check, team) ? VIS_CHANGE : 0; if (g_aidebug->integer) return VIS_YES | !old; if (!(flags & VT_PERISH) && old) return VIS_YES; /* test if check is visible */ while ((from = G_EdictsGetNextInUse(from))) if (G_Vis(team, from, check, flags)) /* visible */ return VIS_YES | !old; /* just return the old state */ return old; }
/** * @brief test if @c check is visible by team (or if visibility changed?) * @sa G_CheckVisTeam * @param[in] team the team the edict may become visible for or perish from * their view * @param[in] check the edict we are talking about - which may become visible * or perish * @param[in] flags if you want to check whether the edict may also perish from * other players view, you should use the @c VT_PERISHCHK bits * @note If the edict is already visible and flags doesn't contain the * bits of @c VT_PERISHCHK, no further checks are performed - only the * @c VS_YES bits are returned * @return VS_CHANGE is added to the bit mask if the edict flipped its visibility * (invisible to visible or vice versa) VS_YES means the edict is visible for the * given team */ int G_TestVis (const int team, Edict* check, const vischeckflags_t flags) { /* store old flag */ const int old = G_IsVisibleForTeam(check, team) ? VS_CHANGE : 0; if (g_aidebug->integer) return VS_YES | !old; if (!(flags & VT_PERISHCHK) && old) return VS_YES; /* test if check is visible */ Edict* from = nullptr; while ((from = G_EdictsGetNextInUse(from))) if (G_Vis(team, from, check, flags)) /* visible */ return VS_YES | !old; /* just return the old state */ return old; }
/** * @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; } } }