/** * @param[in] team The team looking at the edict (or not) * @param[in] check The edict to check the visibility for * @param[in] visFlags The flags for the vis check * @param[in] playerMask The mask for the players to send the appear/perish events to. If this is @c 0 the events * are not sent - we only update the visflags of the edict * @param[in] ent The edict that was responsible for letting the check edict appear and is looking */ static int G_DoTestVis (const int team, Edict* check, const vischeckflags_t visFlags, playermask_t playerMask, const Edict* ent) { int status = 0; const int vis = G_TestVis(team, check, visFlags); /* visibility has changed ... */ if (vis & VS_CHANGE) { /* swap the vis mask for the given team */ const bool appear = (vis & VS_YES) == VS_YES; if (playerMask == 0) { G_VisFlagsSwap(*check, G_TeamToVisMask(team)); } else { G_AppearPerishEvent(playerMask, appear, *check, ent); } /* ... to visible */ if (vis & VS_YES) { status |= VIS_APPEAR; if (G_VisShouldStop(check)) status |= VIS_STOP; } else { status |= VIS_PERISH; } } else if (vis == 0 && (visFlags & VT_NEW)) { if (G_IsActor(check)) { G_EventActorAdd(playerMask, *check); } } return status; }
static void Reset_RescueTrigger (Edict* self, Edict* activator) { if (G_IsActor(activator)) { Actor* actor = makeActor(activator); if (self->isSameTeamAs(actor)) actor->setInRescueZone(false); } }
int G_VisCheckDist (const Edict* const ent) { if (G_IsActiveCamera(ent)) return MAX_SPOT_DIST_CAMERA; if (G_IsActor(ent)) return MAX_SPOT_DIST * G_ActorGetInjuryPenalty(ent, MODIFIER_SIGHT); return MAX_SPOT_DIST; }
static bool G_CameraUse (Edict* self, Edict* activator) { if (!activator || !G_IsActor(activator)) { return false; } self->toggleActive(); return false; }
/** * @brief Make everything visible to anyone who can't already see it */ void G_VisMakeEverythingVisible (void) { edict_t *ent = NULL; while ((ent = G_EdictsGetNextInUse(ent))) { const int playerMask = G_VisToPM(ent->visflags); G_AppearPerishEvent(~playerMask, true, ent, NULL); if (G_IsActor(ent)) G_SendInventory(~G_TeamToPM(ent->team), ent); } }
/** * @brief Make everything visible to anyone who can't already see it */ void G_VisMakeEverythingVisible (void) { Edict* ent = nullptr; while ((ent = G_EdictsGetNextInUse(ent))) { const int playerMask = G_VisToPM(ent->visflags); G_AppearPerishEvent(~playerMask, true, *ent, nullptr); if (G_IsActor(ent)) G_SendInventory(~G_TeamToPM(ent->getTeam()), *ent); } }
/** * @brief Convert an Edict pointer into an Actor pointer */ Actor* makeActor (Edict* ent) { if (G_IsActor(ent)) { /* should be a dynamic_cast one fine day */ Actor* actor = static_cast<Actor*>(ent); if (actor) return actor; } Sys_Error("Unexpected non-Actor Edict found."); return nullptr; }
/** * @brief Iterate through the actor entities (even the dead! - but skips the invisible) * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning */ Edict* G_EdictsGetNextActor (Edict* lastEnt) { Edict* ent = lastEnt; assert(lastEnt < &g_edicts[globals.num_edicts]); while ((ent = G_EdictsGetNextInUse(ent))) { if (G_IsActor(ent)) break; } return ent; }
/** * @brief If an actor was standing on the breakable that is going to get destroyed, we have to let him fall to the ground * @param self The breakable edict * @param activator The touching edict * @note This touch function is only executed if the func_breakable edict has a HP level of 0 (e.g. it is already destroyed) * @return false because this is no client action */ static bool Touch_Breakable (edict_t *self, edict_t *activator) { /* not yet broken */ if (self->HP != 0) return false; /** @todo check that the actor is standing upon the breakable */ if (G_IsActor(activator)) G_ActorFall(activator); return false; }
/** * @brief Rescue trigger * @sa SP_trigger_resuce */ static bool Touch_RescueTrigger (Edict* self, Edict* activator) { /* these actors should really not be able to trigger this - they don't move anymore */ assert(!G_IsDead(activator)); if (G_IsActor(activator)) { Actor* actor = makeActor(activator); if (self->isSameTeamAs(actor)) actor->setInRescueZone(true); } return false; }
/** * @brief Iterate through the actor entities (even the dead!) * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning */ Actor* G_EdictsGetNextActor (Actor* lastEnt) { Edict* ent = lastEnt; assert(lastEnt < &g_edicts[globals.num_edicts]); while ((ent = G_EdictsGetNextInUse(ent))) { if (G_IsActor(ent)) { Actor* actor = static_cast<Actor*>(ent); if (actor) return actor; Sys_Error("dynamic_cast to Actor failed."); } } return nullptr; }
static bool Message_Use (edict_t *self, edict_t *activator) { if (!activator || !G_IsActor(activator)) { return false; } else { player_t *player = G_PLAYER_FROM_ENT(activator); const char *msg = self->message; /* remove gettext marker */ if (msg[0] == '_') msg++; G_ClientPrintf(player, PRINT_HUD, "%s", msg); if (self->spawnflags & 1) G_FreeEdict(self); return false; } }
static bool Message_Use (Edict* self, Edict* activator) { if (!activator || !G_IsActor(activator)) { return false; } else { Player& player = activator->getPlayer(); const char* msg = self->message; /* remove gettext marker */ if (msg[0] == '_') msg++; G_ClientPrintf(player, PRINT_HUD, "%s", msg); if (self->spawnflags & 1) G_FreeEdict(self); return false; } }
/** * @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 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; }
/** * @brief Applies the given damage value to an edict that is either an actor or has * the @c FL_DESTROYABLE flag set. * @param ent The edict to apply the damage to. * @param damage The damage value. * @note This function assures, that the health points of the edict are never * getting negative. * @sa G_Damage */ void G_TakeDamage (edict_t *ent, int damage) { if (G_IsBreakable(ent) || G_IsActor(ent)) ent->HP = max(ent->HP - damage, 0); }
/** * @brief Deals splash damage to a target and its surroundings. * @param[in] ent The shooting actor * @param[in] fd The fire definition that defines what type of damage is dealt and how big the splash radius is. * @param[in] impact The impact vector where the grenade is exploding * @param[in,out] mock pseudo shooting - only for calculating mock values - NULL for real shots * @param[in] tr The trace where the grenade hits something (or not) */ static void G_SplashDamage (edict_t *ent, const fireDef_t *fd, vec3_t impact, shot_mock_t *mock, const trace_t* tr) { edict_t *check = NULL; vec3_t center; float dist; int damage; const bool shock = (fd->obj->dmgtype == gi.csi->damShock); assert(fd->splrad > 0.0); while ((check = G_EdictsGetNextInUse(check))) { /* If we use a blinding weapon we skip the target if it's looking * away from the impact location. */ if (shock && !G_FrustumVis(check, impact)) continue; if (G_IsBrushModel(check) && G_IsBreakable(check)) VectorCenterFromMinsMaxs(check->absmin, check->absmax, center); else if (G_IsLivingActor(check) || G_IsBreakable(check)) VectorCopy(check->origin, center); else continue; /* check for distance */ dist = VectorDist(impact, center); dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0; if (dist > fd->splrad) continue; if (fd->irgoggles) { if (G_IsActor(check)) { /* check whether this actor (check) is in the field of view of the 'shooter' (ent) */ if (G_FrustumVis(ent, check->origin)) { if (!mock) { const unsigned int playerMask = G_TeamToPM(ent->team) ^ G_VisToPM(check->visflags); G_AppearPerishEvent(playerMask, true, check, ent); G_VisFlagsAdd(check, G_PMToVis(playerMask)); } } } continue; } /* check for walls */ if (G_IsLivingActor(check) && !G_ActorVis(impact, ent, check, false)) continue; /* do damage */ if (shock) damage = 0; else damage = fd->spldmg[0] * (1.0 - dist / fd->splrad); if (mock) mock->allow_self = true; G_Damage(check, fd, damage, ent, mock, NULL); if (mock) mock->allow_self = false; } /** @todo splash might also hit other surfaces and the trace doesn't handle that */ if (tr && G_FireAffectedSurface(tr->surface, fd)) { /* move a little away from the impact vector */ VectorMA(impact, 1, tr->plane.normal, impact); G_SpawnParticle(impact, tr->contentFlags >> 8, "burning"); }
/** * @brief Checks whether the given edict is a living actor * @param[in] ent The edict to perform the check for * @sa LE_IsLivingActor */ bool G_IsLivingActor (const Edict* ent) { return G_IsActor(ent) && (G_IsStunned(ent) || !G_IsDead(ent)); }