/** * @sa G_MoraleStopPanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoralePanic (edict_t * ent, qboolean sanity) { G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("%s panics!\n"), ent->chr.name); /* drop items in hands */ if (!sanity && ent->chr.teamDef->weapons) { if (RIGHT(ent)) G_ActorInvMove(ent, INVDEF(gi.csi->idRight), RIGHT(ent), INVDEF(gi.csi->idFloor), NONE, NONE, qtrue); if (LEFT(ent)) G_ActorInvMove(ent, INVDEF(gi.csi->idLeft), LEFT(ent), INVDEF(gi.csi->idFloor), NONE, NONE, qtrue); } /* get up */ G_RemoveCrouched(ent); G_ActorSetMaxs(ent); /* send panic */ G_SetPanic(ent); G_EventSendState(G_VisToPM(ent->visflags), ent); /* center view */ G_EventCenterView(ent); /* move around a bit, try to avoid opponents */ AI_ActorThink(G_PLAYER_FROM_ENT(ent), ent); /* kill TUs */ G_ActorSetTU(ent, 0); }
/** * @sa G_MoraleStopPanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoralePanic (Edict* ent) { G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("%s panics!"), ent->chr.name); G_PrintStats("%s panics (entnum %i).", ent->chr.name, ent->getIdNum()); /* drop items in hands */ if (G_IsInsane(ent) && ent->chr.teamDef->weapons) { if (ent->getRightHandItem()) G_ActorInvMove(ent, INVDEF(CID_RIGHT), ent->getRightHandItem(), INVDEF(CID_FLOOR), NONE, NONE, true); if (ent->getLeftHandItem()) G_ActorInvMove(ent, INVDEF(CID_LEFT), ent->getLeftHandItem(), INVDEF(CID_FLOOR), NONE, NONE, true); G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, false); } /* get up */ G_RemoveCrouched(ent); G_ActorSetMaxs(ent); /* send panic */ G_SetPanic(ent); G_EventSendState(G_VisToPM(ent->visflags), *ent); /* center view */ G_EventCenterView(*ent); /* move around a bit, try to avoid opponents */ AI_ActorThink(ent->getPlayer(), ent); /* kill TUs */ G_ActorSetTU(ent, 0); }
/** * @brief Announce the actor die event for the clients that are seeing the actor * @param[in] ent The actor that is dying */ void G_EventActorDie (const edict_t* ent) { G_EventAdd(G_VisToPM(ent->visflags), EV_ACTOR_DIE, ent->number); gi.WriteShort(ent->state); gi.WriteByte(ent->pnum); G_EventEnd(); }
/** * @brief Stops the rage state of an actor * @note This is only called when cvar mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopPanic * @sa G_MoraleBehaviour */ static void G_MoraleStopRage (edict_t * ent) { if (ent->morale / mor_panic->value > m_rage_stop->value * frand()) { G_RemoveInsane(ent); G_EventSendState(G_VisToPM(ent->visflags), ent); } else G_MoralePanic(ent, qtrue); /* regains sanity */ }
/** * @brief Start the shooting event * @param ent The entity that starts the shooting * @param visMask the vis mask of the teams to determine the clients from this event is send to * @param shootType The type of the shoot * @param at The grid position to target to */ void G_EventStartShoot (const edict_t* ent, vismask_t visMask, shoot_types_t shootType, const pos3_t at) { G_EventAdd(G_VisToPM(visMask), EV_ACTOR_START_SHOOT, ent->number); gi.WriteByte(shootType); gi.WriteGPos(ent->pos); gi.WriteGPos(at); G_EventEnd(); }
/** * @brief Announce the actor die event for the clients that are seeing the actor * @param[in] ent The actor that is dying */ void G_EventActorDie (const Edict& ent, bool attacker) { G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_DIE, ent.number); gi.WriteShort(ent.state); gi.WriteByte(ent.pnum); gi.WriteByte(attacker); G_EventEnd(); }
/** * @brief Start the shooting event * @param ent The entity that starts the shooting * @param teamMask the vis mask of the teams to determine the clients from this event is send to * @param shootType The type of the shoot * @param at The grid position to target to */ void G_EventStartShoot (const Edict& ent, teammask_t teamMask, shoot_types_t shootType, const pos3_t at) { G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_START_SHOOT, ent.number); gi.WriteByte(shootType); gi.WriteGPos(ent.pos); gi.WriteGPos(at); G_EventEnd(); }
/** * @brief Applies morale behaviour on actors * @note only called when mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleStopPanic */ void G_MoraleBehaviour (int team) { edict_t *ent = NULL; int newMorale; while ((ent = G_EdictsGetNextInUse(ent))) { /* this only applies to ET_ACTOR but not to ET_ACTOR2x2 */ if (ent->type == ET_ACTOR && ent->team == team && !G_IsDead(ent)) { /* civilians have a 1:1 chance to randomly run away in multiplayer */ if (sv_maxclients->integer >= 2 && level.activeTeam == TEAM_CIVILIAN && 0.5 > frand()) G_MoralePanic(ent, qfalse); /* multiplayer needs enabled sv_enablemorale */ /* singleplayer has this in every case */ if (G_IsMoraleEnabled()) { /* if panic, determine what kind of panic happens: */ if (ent->morale <= mor_panic->value && !G_IsPaniced(ent) && !G_IsRaged(ent)) { qboolean sanity; if ((float) ent->morale / mor_panic->value > (m_sanity->value * frand())) sanity = qtrue; else sanity = qfalse; if ((float) ent->morale / mor_panic->value > (m_rage->value * frand())) G_MoralePanic(ent, sanity); else G_MoraleRage(ent, sanity); /* if shaken, well .. be shaken; */ } else if (ent->morale <= mor_shaken->value && !G_IsPaniced(ent) && !G_IsRaged(ent)) { /* shaken is later reset along with reaction fire */ G_SetShaken(ent); G_SetState(ent, STATE_REACTION); G_EventSendState(G_VisToPM(ent->visflags), ent); G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("%s is currently shaken.\n"), ent->chr.name); } else { if (G_IsPaniced(ent)) G_MoraleStopPanic(ent); else if (G_IsRaged(ent)) G_MoraleStopRage(ent); } } G_ActorSetMaxs(ent); /* morale-regeneration, capped at max: */ newMorale = ent->morale + MORALE_RANDOM(mor_regeneration->value); if (newMorale > GET_MORALE(ent->chr.score.skills[ABILITY_MIND])) ent->morale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]); else ent->morale = newMorale; /* send phys data and state: */ G_SendStats(ent); gi.EndEvents(); } } }
/** * @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 Stops the rage state of an actor * @note This is only called when cvar mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopPanic * @sa G_MoraleBehaviour */ static void G_MoraleStopRage (Edict *ent) { if (ent->morale / mor_panic->value > m_rage_stop->value * frand()) { G_RemoveInsane(ent); G_EventSendState(G_VisToPM(ent->visflags), *ent); G_PrintStats("%s is no longer insane (entnum %i).", ent->chr.name, ent->number); } else { G_MoralePanic(ent, true); /* regains sanity */ } }
/** * @brief Stops the panic state of an actor * @note This is only called when cvar mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleStopPanic (Edict *ent) { if (ent->morale / mor_panic->value > m_panic_stop->value * frand()) { G_RemovePanic(ent); G_PrintStats("%s is no longer panicked (entnum %i).", ent->chr.name, ent->number); G_EventSendState(G_VisToPM(ent->visflags), *ent); } else { G_MoralePanic(ent, true); } }
/** * @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); } }
static void Think_SmokeAndFire (edict_t *self) { if (self->time + self->count <= level.actualRound) { bool checkVis = self->type == ET_SMOKE; G_EventEdictPerish(G_VisToPM(self->particleLink->visflags), self->particleLink); G_FreeEdict(self->particleLink); G_FreeEdict(self); if (checkVis) G_CheckVis(NULL); } }
/** * @brief Applies morale behaviour on actors * @note only called when mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleStopPanic */ void G_MoraleBehaviour (int team) { bool enabled = G_IsMoraleEnabled(team); if (!enabled) return; Edict* ent = nullptr; while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team)) != nullptr) { /* this only applies to ET_ACTOR but not to ET_ACTOR2x2 */ if (ent->type != ET_ACTOR || CHRSH_IsTeamDefRobot(ent->chr.teamDef)) continue; /* if panic, determine what kind of panic happens: */ if (!G_IsPanicked(ent) && !G_IsRaged(ent)) { if (ent->morale <= mor_panic->integer) { const float ratio = (float) ent->morale / mor_panic->value; const bool sanity = ratio > (m_sanity->value * frand()); if (!sanity) G_SetInsane(ent); if (ratio > (m_rage->value * frand())) G_MoralePanic(ent); else G_MoraleRage(ent); /* if shaken, well .. be shaken; */ } else if (ent->morale <= mor_shaken->integer) { /* shaken is later reset along with reaction fire */ G_SetShaken(ent); G_ClientStateChange(ent->getPlayer(), ent, STATE_REACTION, false); G_EventSendState(G_VisToPM(ent->visflags), *ent); G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("%s is currently shaken."), ent->chr.name); G_PrintStats("%s is shaken (entnum %i).", ent->chr.name, ent->getIdNum()); } } else { if (G_IsPanicked(ent)) G_MoraleStopPanic(ent); else if (G_IsRaged(ent)) G_MoraleStopRage(ent); } G_ActorSetMaxs(ent); /* morale-regeneration, capped at max: */ int newMorale = ent->morale + MORALE_RANDOM(mor_regeneration->value); const int maxMorale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]); if (newMorale > maxMorale) ent->morale = maxMorale; else ent->morale = newMorale; /* send phys data and state: */ G_SendStats(*ent); } }
/** * @param[in] teamMask the vis mask to determine the clients from this event is send to * @param[in] fd The firedefinition to use * @param[in] dt Delta time * @param[in] flags bitmask of the following values: @c SF_BODY, @c SF_IMPACT, @c SF_BOUNCING and @c SF_BOUNCED * @param[in] position The current position * @param[in] velocity The velocity of the throw */ void G_EventThrow (teammask_t teamMask, const fireDef_t* fd, float dt, byte flags, const vec3_t position, const vec3_t velocity) { G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_THROW, -1); gi.WriteShort(dt * 1000); gi.WriteShort(fd->obj->idx); gi.WriteByte(fd->weapFdsIdx); gi.WriteByte(fd->fdIdx); gi.WriteByte(flags); gi.WritePos(position); gi.WritePos(velocity); G_EventEnd(); }
void G_EventActorFall (const Edict& ent) { G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_MOVE, ent.number); gi.WriteByte(1); gi.WriteByte(ent.pos[0]); gi.WriteByte(ent.pos[1]); gi.WriteByte(ent.pos[2]); gi.WriteByte(makeDV(DIRECTION_FALL, ent.pos[2])); gi.WriteShort(GRAVITY); gi.WriteShort(0); G_EventEnd(); }
static void Think_SmokeAndFire (Edict* self) { const int endRound = self->time + self->count; const int spawnIndex = (self->team + level.teamOfs) % MAX_TEAMS; const int currentIndex = (level.activeTeam + level.teamOfs) % MAX_TEAMS; if (endRound < level.actualRound || (endRound == level.actualRound && spawnIndex <= currentIndex)) { const bool checkVis = self->type == ET_SMOKE; G_EventEdictPerish(G_VisToPM(self->particleLink->visflags), *self->particleLink); G_FreeEdict(self->particleLink); G_FreeEdict(self); if (checkVis) G_CheckVis(nullptr); } }
/** * @brief Change the amount of available ammo for the given entity * @param ent The entity to change the amount of ammo for * @param ammo The ammo to change * @param amount The new amount of the left ammo * @param shootType The shooting type to determine which container to use */ void G_EventInventoryAmmo (const Edict& ent, const objDef_t* ammo, int amount, shoot_types_t shootType) { G_EventAdd(G_VisToPM(ent.visflags), EV_INV_AMMO, ent.number); gi.WriteByte(amount); gi.WriteByte(ammo->idx); if (IS_SHOT_RIGHT(shootType)) gi.WriteByte(CID_RIGHT); else gi.WriteByte(CID_LEFT); /* x and y value */ gi.WriteByte(0); gi.WriteByte(0); G_EventEnd(); }
/** * @sa G_MoralePanic * @sa G_MoraleStopPanic * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleRage (edict_t * ent, qboolean sanity) { if (sanity) G_SetRage(ent); else G_SetInsane(ent); G_EventSendState(G_VisToPM(ent->visflags), ent); if (sanity) gi.BroadcastPrintf(PRINT_HUD, _("%s is on a rampage.\n"), ent->chr.name); else gi.BroadcastPrintf(PRINT_HUD, _("%s is consumed by mad rage!\n"), ent->chr.name); AI_ActorThink(G_PLAYER_FROM_ENT(ent), ent); }
/** * @brief Change the amount of available ammo for the given entity * @param ent The entity to change the amount of ammo for * @param ammo The ammo to change * @param amount The new amount of the left ammo * @param shootType The shooting type to determine which container to use */ void G_EventInventoryAmmo (const edict_t* ent, const objDef_t* ammo, int amount, shoot_types_t shootType) { G_EventAdd(G_VisToPM(ent->visflags), EV_INV_AMMO, ent->number); gi.WriteByte(amount); gi.WriteByte(ammo->idx); if (IS_SHOT_RIGHT(shootType)) gi.WriteByte(gi.csi->idRight); else gi.WriteByte(gi.csi->idLeft); /* x and y value */ gi.WriteByte(0); gi.WriteByte(0); G_EventEnd(); }
/** * @sa G_MoralePanic * @sa G_MoraleStopPanic * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleRage (Edict* ent) { G_SetRage(ent); if (!G_IsInsane(ent)) { gi.BroadcastPrintf(PRINT_HUD, _("%s is on a rampage!"), ent->chr.name); G_PrintStats("%s is on a rampage (entnum %i).", ent->chr.name, ent->getIdNum()); } else { gi.BroadcastPrintf(PRINT_HUD, _("%s is consumed by mad rage!"), ent->chr.name); G_PrintStats("%s is consumed by mad rage (entnum %i).", ent->chr.name, ent->getIdNum()); } G_EventSendState(G_VisToPM(ent->visflags), *ent); G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, false); AI_ActorThink(ent->getPlayer(), ent); }
/** * @sa G_MoralePanic * @sa G_MoraleStopPanic * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleRage (edict_t *ent, bool sanity) { if (sanity) { G_SetRage(ent); gi.BroadcastPrintf(PRINT_HUD, _("%s is on a rampage!"), ent->chr.name); G_PrintStats("%s is on a rampage (entnum %i).", ent->chr.name, ent->number); } else { G_SetInsane(ent); gi.BroadcastPrintf(PRINT_HUD, _("%s is consumed by mad rage!"), ent->chr.name); G_PrintStats("%s is consumed by mad rage (entnum %i).", ent->chr.name, ent->number); } G_EventSendState(G_VisToPM(ent->visflags), ent); AI_ActorThink(G_PLAYER_FROM_ENT(ent), ent); }
/** * @brief Writes a step of the move event to the net * @param[in] ent Edict to move * @param[in] stepAmount Pointer to the amount of steps in this move-event * @param[in] dvec The direction vector for the step to be added * @param[in] contentFlags The material we are walking over */ static void G_WriteStep (edict_t* ent, byte** stepAmount, const int dvec, const int contentFlags) { /* write move header if not yet done */ if (gi.GetEvent() != EV_ACTOR_MOVE) { gi.AddEvent(G_VisToPM(ent->visflags), EV_ACTOR_MOVE); gi.WriteShort(ent->number); /* stepAmount is a pointer to a location in the netchannel * the value of this pointer depends on how far the actor walks * and this might be influenced at a later stage inside this * loop. That's why we can modify the value of this byte * if e.g. a VIS_STOP occurred and no more steps should be made. * But keep in mind, that no other events might be between * this event and its successful end - otherwise the * stepAmount pointer would no longer be valid and you would * modify data in the new event. */ *stepAmount = gi.WriteDummyByte(0); /* Add three more dummy bytes. These will be the final actor position. */ gi.WriteDummyByte(0); /* x */ gi.WriteDummyByte(0); /* y */ gi.WriteDummyByte(0); /* z */ } else if (!*stepAmount) { gi.DPrintf("Event %i activate and no stepAmount pointer set\n", gi.GetEvent()); return; } /* the moveinfo stuff is used inside the G_PhysicsStep think function */ if (ent->moveinfo.steps >= MAX_DVTAB) { ent->moveinfo.steps = 0; ent->moveinfo.currentStep = 0; } ent->moveinfo.contentFlags[ent->moveinfo.steps] = contentFlags; ent->moveinfo.visflags[ent->moveinfo.steps] = ent->visflags; ent->moveinfo.steps++; /* store steps in netchannel */ byte *pStep = *stepAmount; (*pStep)++; /* store the position too */ *(pStep + 1) = ent->pos[0]; *(pStep + 2) = ent->pos[1]; *(pStep + 3) = ent->pos[2]; /* write move header and always one step after another - because the next step * might already be the last one due to some stop event */ gi.WriteShort(dvec); gi.WriteShort(ent->speed); gi.WriteShort(contentFlags); }
/** * @brief After an actor changed his state, he might get visible for other * players. Check the vis here and send the state change to the clients that * are seeing him already. * @param ent The actor edict */ static void G_ClientStateChangeUpdate (edict_t *ent) { /* Send the state change. */ G_EventSendState(G_VisToPM(ent->visflags), ent); /* Check if the player appears/perishes, seen from other teams. */ G_CheckVis(ent); /* Calc new vis for this player. */ G_CheckVisTeamAll(ent->team, 0, ent); /* Send the new TUs. */ G_SendStats(ent); /* End the event. */ G_EventEnd(); }
/** * @brief Removes one particular item from a given container * @param itemID The id of the item to remove * @param ent The edict that holds the inventory to remove the item from * @param container The container in the inventory of the edict to remove the searched item from. * @return @c true if the removal was successful, @c false otherwise. */ bool G_InventoryRemoveItemByID (const char *itemID, edict_t *ent, containerIndex_t container) { invList_t *ic = CONTAINER(ent, container); while (ic) { const objDef_t *item = ic->item.item; if (item != NULL && Q_streq(item->id, itemID)) { /* remove the virtual item to update the inventory lists */ if (!game.i.RemoveFromInventory(&game.i, &ent->chr.i, INVDEF(container), ic)) gi.Error("Could not remove item '%s' from inventory %i", ic->item.item->id, container); G_EventInventoryDelete(ent, G_VisToPM(ent->visflags), INVDEF(container), ic->x, ic->y); return true; } ic = ic->next; } return false; }
/** * @brief Updates the reaction fire settings in case something was moved into a hand or from a hand * that would make the current settings invalid * @param[in,out] actor The actor edict to check the settings for * @param[in] fmIdx The fire mode index that should be used for reaction fire * @param[in] hand The hand that should be used for reaction fire * @param[in] od The object/weapon for the reaction fire */ void G_ReactionFireSettingsUpdate (Actor* actor, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t* od) { actor->chr.RFmode.set(hand, fmIdx, od); /* FiremodeSettings */ if (!G_ActorHasWorkingFireModeSet(actor)) { /* Disable reaction fire if no valid firemode was found. */ G_ClientStateChange(actor->getPlayer(), actor, ~STATE_REACTION, false); G_EventReactionFireChange(*actor); G_EventSendState(G_VisToPM(actor->visflags), *actor); return; } G_EventReactionFireChange(*actor); /* If reaction fire is active, update the reserved TUs */ if (actor->isReaction()) { G_ReactionFireSettingsReserveTUs(actor); } }
/** * @brief Do the shooting * @param ent The entity that is doing the shooting * @param visMask the vis mask to determine the clients from this event is send to * @param fd The firedefinition to use for the shoot * @param firstShoot Is this the first shoot * @param shootType The type of the shoot * @param flags Define some flags in a bitmask: @c SF_BODY, @c SF_IMPACT, @c SF_BOUNCING and @c SF_BOUNCING * @param trace The trace what was used to determine whether this shot has hit something * @param from The position the entity shoots from * @param impact The impact world vector for the shot */ void G_EventShoot (const edict_t* ent, vismask_t visMask, const fireDef_t* fd, bool firstShoot, shoot_types_t shootType, int flags, const trace_t* trace, const vec3_t from, const vec3_t impact) { const edict_t *targetEdict = trace->ent; G_EventAdd(G_VisToPM(visMask), EV_ACTOR_SHOOT, ent->number); if (targetEdict && G_IsBreakable(targetEdict)) gi.WriteShort(targetEdict->number); else gi.WriteShort(SKIP_LOCAL_ENTITY); gi.WriteByte(firstShoot ? 1 : 0); gi.WriteShort(fd->obj->idx); gi.WriteByte(fd->weapFdsIdx); gi.WriteByte(fd->fdIdx); gi.WriteByte(shootType); gi.WriteByte(flags); gi.WriteByte(trace->contentFlags); gi.WritePos(from); gi.WritePos(impact); gi.WriteDir(trace->plane.normal); G_EventEnd(); }
/** * @brief Do the shooting * @param ent The entity that is doing the shooting * @param teamMask the vis mask to determine the clients from this event is send to * @param fd The firedefinition to use for the shoot * @param firstShoot Is this the first shoot * @param shootType The type of the shoot * @param flags Define some flags in a bitmask: @c SF_BODY, @c SF_IMPACT, @c SF_BOUNCING and @c SF_BOUNCING * @param trace The trace what was used to determine whether this shot has hit something * @param from The position the entity shoots from * @param impact The impact world vector for the shot */ void G_EventShoot (const Edict& ent, teammask_t teamMask, const fireDef_t* fd, bool firstShoot, shoot_types_t shootType, int flags, const trace_t* trace, const vec3_t from, const vec3_t impact) { const Edict* targetEdict = G_EdictsGetByNum(trace->entNum); /* the ent possibly hit by the trace */ G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_SHOOT, ent.number); if (targetEdict && G_IsBreakable(targetEdict)) gi.WriteShort(targetEdict->number); else gi.WriteShort(SKIP_LOCAL_ENTITY); gi.WriteByte(firstShoot ? 1 : 0); gi.WriteShort(fd->obj->idx); gi.WriteByte(fd->weapFdsIdx); gi.WriteByte(fd->fdIdx); gi.WriteByte(shootType); gi.WriteByte(flags); gi.WriteByte(trace->contentFlags); gi.WritePos(from); gi.WritePos(impact); gi.WriteDir(trace->plane.normal); G_EventEnd(); }
/** * @brief Writes a step of the move event to the net * @param[in] ent Edict to move * @param[in] stepAmount Pointer to the amount of steps in this move-event * @param[in] dvec The direction vector for the step to be added * @param[in] contentFlags The material we are walking over */ static void G_WriteStep (edict_t* ent, byte** stepAmount, const int dvec, const int contentFlags) { /* write move header if not yet done */ if (gi.GetEvent() != EV_ACTOR_MOVE) { G_EventAdd(G_VisToPM(ent->visflags), EV_ACTOR_MOVE, ent->number); } /* the moveinfo stuff is used inside the G_PhysicsStep think function */ if (ent->moveinfo.steps >= MAX_ROUTE) { ent->moveinfo.steps = 0; ent->moveinfo.currentStep = 0; } ent->moveinfo.contentFlags[ent->moveinfo.steps] = contentFlags; ent->moveinfo.visflags[ent->moveinfo.steps] = ent->visflags; ent->moveinfo.steps++; /* write move header and always one step after another - because the next step * might already be the last one due to some stop event */ gi.WriteShort(dvec); gi.WriteShort(ent->speed); gi.WriteShort(contentFlags); }
/** * @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); }