/** * @brief Checks whether the actor is allowed to activate reaction fire and will informs the player about * the reason if this would not work. * @param[in] ent The actor to check * @return @c true if the actor is allowed to activate it, @c false otherwise */ static bool G_ReactionFireCanBeEnabled (const edict_t *ent) { /* check ent is a suitable shooter */ if (!ent->inuse || !G_IsLivingActor(ent)) return false; if (G_MatchIsRunning() && ent->team != level.activeTeam) return false; /* actor may not carry weapons at all - so no further checking is needed */ if (!ent->chr.teamDef->weapons) return false; if (!G_ActorHasReactionFireEnabledWeapon(ent)) { G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("No reaction fire enabled weapon.")); return false; } if (!G_ActorHasWorkingFireModeSet(ent)) { G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("No fire mode selected for reaction fire.")); return false; } if (!G_ActorHasEnoughTUsReactionFire(ent)) { G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("Not enough TUs left for activating reaction fire.")); return false; } return true; }
/** * @brief Set the rescue zone data * @param[out] actor The actor to set the rescue zone flag for * @param[in] inRescueZone @c true if the actor is in the rescue zone, @c false otherwise */ void G_ActorSetInRescueZone (Edict* actor, bool inRescueZone) { if (inRescueZone == G_ActorIsInRescueZone(actor)) return; if (inRescueZone) G_ClientPrintf(actor->getPlayer(), PRINT_HUD, _("Soldier entered the rescue zone.")); else G_ClientPrintf(actor->getPlayer(), PRINT_HUD, _("Soldier left the rescue zone.")); actor->inRescueZone = inRescueZone; }
/** * @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); }
static void G_Players_f (const player_t *player) { int count = 0; char smallBuf[64]; char largeBuf[1280]; player_t *p; /* print information */ largeBuf[0] = 0; p = NULL; while ((p = G_PlayerGetNextActiveHuman(p))) { Com_sprintf(smallBuf, sizeof(smallBuf), "(%i) Team %i %s status: %s\n", p->num, p->pers.team, p->pers.netname, (p->roundDone ? "waiting" : "playing")); /* can't print all of them in one packet */ if (strlen(smallBuf) + strlen(largeBuf) > sizeof(largeBuf) - 100) { Q_strcat(largeBuf, "...\n", sizeof(largeBuf)); break; } Q_strcat(largeBuf, smallBuf, sizeof(largeBuf)); count++; } G_ClientPrintf(player, PRINT_CONSOLE, "%s\n%i players\n", largeBuf, count); }
/** * @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); }
/** * @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 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); } }
/** * @brief Changes the state of a player/soldier. * @param[in,out] player The player who controlls the actor * @param[in] ent the edict to perform the state change for * @param[in] reqState The bit-map of the requested state change * @param[in] checkaction only activate the events - network stuff is handled in the calling function * don't even use the G_ActionCheckForCurrentTeam function * @note Use checkaction true only for e.g. spawning values */ void G_ClientStateChange (const player_t* player, edict_t* ent, int reqState, bool checkaction) { /* Check if any action is possible. */ if (checkaction && !G_ActionCheckForCurrentTeam(player, ent, 0)) return; if (!reqState) return; switch (reqState) { case STATE_CROUCHED: /* Toggle between crouch/stand. */ /* Check if player has enough TUs (TU_CROUCH TUs for crouch/uncrouch). */ if (!checkaction || G_ActionCheckForCurrentTeam(player, ent, TU_CROUCH)) { if (G_IsCrouched(ent)) { if (!gi.CanActorStandHere(ent->fieldSize, ent->pos)) break; } G_ToggleCrouched(ent); G_ActorUseTU(ent, TU_CROUCH); G_ActorSetMaxs(ent); } break; case ~STATE_REACTION: /* Request to turn off reaction fire. */ if (G_IsReaction(ent)) { if (G_IsShaken(ent)) { G_ClientPrintf(player, PRINT_HUD, _("Currently shaken, won't let their guard down.")); } else { /* Turn off reaction fire. */ G_RemoveReaction(ent); G_ActorReserveTUs(ent, 0, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch); } } break; /* Request to turn on multi- or single-reaction fire mode. */ case STATE_REACTION: /* Disable reaction fire. */ G_RemoveReaction(ent); if (G_ReactionFireSettingsReserveTUs(ent)) { /* Enable requested reaction fire. */ G_SetState(ent, reqState); } break; default: gi.DPrintf("G_ClientStateChange: unknown request %i, ignoring\n", reqState); return; } /* Only activate the events - network stuff is handled in the calling function */ if (!checkaction) return; G_ClientStateChangeUpdate(ent); }
/** * @brief Check whether the user can talk */ static bool G_CheckFlood (Player& player) { if (flood_msgs->integer) { if (level.time < player.pers.flood_locktill) { G_ClientPrintf(player, PRINT_CHAT, _("You can't talk for %d more seconds\n"), (int)(player.pers.flood_locktill - level.time)); return true; } int i = player.pers.flood_whenhead - flood_msgs->value + 1; if (i < 0) i = (sizeof(player.pers.flood_when)/sizeof(player.pers.flood_when[0])) + i; if (player.pers.flood_when[i] && level.time - player.pers.flood_when[i] < flood_persecond->value) { player.pers.flood_locktill = level.time + flood_waitdelay->value; G_ClientPrintf(player, PRINT_CHAT, _("Flood protection: You can't talk for %d seconds.\n"), flood_waitdelay->integer); return true; } player.pers.flood_whenhead = (player.pers.flood_whenhead + 1) % (sizeof(player.pers.flood_when)/sizeof(player.pers.flood_when[0])); player.pers.flood_when[player.pers.flood_whenhead] = level.time; } return false; }
static void G_Say_f (Player& player, bool arg0, bool team) { if (gi.Cmd_Argc() < 2 && !arg0) return; if (G_CheckFlood(player)) return; char text[256]; if (arg0) { Com_sprintf(text, sizeof(text), "%s %s", gi.Cmd_Argv(0), gi.Cmd_Args()); } else { Com_sprintf(text, sizeof(text), "%s", gi.Cmd_Args()); } /* strip quotes */ char* s = text; if (s[0] == '"' && s[strlen(s) - 1] == '"') { s[strlen(s) - 1] = '\0'; s++; } if (sv_dedicated->integer) { if (!team) gi.DPrintf("%s: %s\n", player.pers.netname, s); else gi.DPrintf("^B%s (team): %s\n", player.pers.netname, s); } Player* p = nullptr; while ((p = G_PlayerGetNextActiveHuman(p))) { if (team && p->getTeam() != player.getTeam()) continue; if (!team) G_ClientPrintf(*p, PRINT_CHAT, "%s: %s\n", player.pers.netname, s); else G_ClientPrintf(*p, PRINT_CHAT, "^B%s (team): %s\n", player.pers.netname, s); } }
/** * @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; }
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 Checks whether the requested action is possible for the current active team * @param[in] player Which player (human player) is trying to do the action * @param[in] ent Which of his units is trying to do the action. * @param[in] TU The time units to check against the ones ent has. * the action with */ bool G_ActionCheckForCurrentTeam (const player_t *player, edict_t *ent, int TU) { /* don't check for a player - but maybe a server action */ if (!player) return true; /* a generic tester if an action could be possible */ if (level.activeTeam != player->pers.team) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - it is not your turn!")); return false; } if (TU > G_ActorUsableTUs(ent)) { return false; } return G_ActionCheck(player, ent); }
static void G_Say_f (player_t *player, bool arg0, bool team) { char text[256]; player_t *p; if (gi.Cmd_Argc() < 2 && !arg0) return; if (G_CheckFlood(player)) return; if (!team) Com_sprintf(text, sizeof(text), "%s: ", player->pers.netname); else Com_sprintf(text, sizeof(text), "^B%s (team): ", player->pers.netname); if (arg0) { Q_strcat(text, gi.Cmd_Argv(0), sizeof(text)); Q_strcat(text, " ", sizeof(text)); Q_strcat(text, gi.Cmd_Args(), sizeof(text)); } else { const char *p = gi.Cmd_Args(); const char *token = Com_Parse(&p); Q_strcat(text, token, sizeof(text)); } Q_strcat(text, "\n", sizeof(text)); if (sv_dedicated->integer) gi.DPrintf("%s", text); p = NULL; while ((p = G_PlayerGetNextActiveHuman(p))) { if (team && p->pers.team != player->pers.team) continue; G_ClientPrintf(p, PRINT_CHAT, "%s", text); } }
/** * @brief Checks whether the requested action is possible * @param[in] player Which player (human player) is trying to do the action * @param[in] ent Which of his units is trying to do the action. */ static bool G_ActionCheck (const player_t *player, edict_t *ent) { /* don't check for a player - but maybe a server action */ if (!player) return true; if (!ent || !ent->inuse) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - object not present!")); return false; } if (ent->type != ET_ACTOR && ent->type != ET_ACTOR2x2) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not an actor!")); return false; } if (G_IsStunned(ent)) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is stunned!")); return false; } if (G_IsDead(ent)) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is dead!")); return false; } if (ent->team != player->pers.team) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not on same team!")); return false; } if (ent->pnum != player->num) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - no control over allied actors!")); return false; } /* could be possible */ return true; }
/** * @brief Moves an item inside an inventory. Floors are handled special. * @param[in] actor The pointer to the selected/used edict/soldier. * @param[in] fromContType The container (-id) the item should be moved from. * @param[in] fItem The item you want to move. * @param[in] toContType The container (-def) the item should be moved to. * @param[in] tx x position where you want the item to go in the destination container * @param[in] ty y position where you want the item to go in the destination container * @param[in] checkaction Set this to true if you want to check for TUs, otherwise false. * @sa event PA_INVMOVE * @sa AI_ActorThink */ bool G_ActorInvMove (Edict* actor, const invDef_t* fromContType, Item* fItem, const invDef_t* toContType, int tx, int ty, bool checkaction) { Edict* floor; bool newFloor; Item* tc; playermask_t mask; inventory_action_t ia; Item fromItemBackup, toItemBackup; int fx, fy; int originalTU, reservedTU = 0; Player& player = actor->getPlayer(); assert(fItem); assert(fItem->def()); /* Store the location/item of 'from' BEFORE actually moving items with moveInInventory. */ fromItemBackup = *fItem; /* Store the location of 'to' BEFORE actually moving items with moveInInventory * so in case we swap ammo the client can be updated correctly */ tc = actor->chr.inv.getItemAtPos(toContType, tx, ty); if (tc) toItemBackup = *tc; else toItemBackup = *fItem; /* Get first used bit in item. */ fItem->getFirstShapePosition(&fx, &fy); fx += fItem->getX(); fy += fItem->getY(); /* Check if action is possible */ /* TUs are 1 here - but this is only a dummy - the real TU check is done in the inventory functions below */ if (checkaction && !G_ActionCheckForCurrentTeam(player, actor, 1)) return false; if (!actor->chr.inv.canHoldItemWeight(fromContType->id, toContType->id, *fItem, actor->chr.score.skills[ABILITY_POWER])) { G_ClientPrintf(player, PRINT_HUD, _("This soldier can not carry anything else.")); return false; } /* "get floor ready" - searching for existing floor-edict */ floor = G_GetFloorItems(actor); if (toContType->isFloorDef() && !floor) { /* We are moving to the floor, but no existing edict for this floor-tile found -> create new one */ floor = G_SpawnFloor(actor->pos); newFloor = true; } else if (fromContType->isFloorDef() && !floor) { /* We are moving from the floor, but no existing edict for this floor-tile found -> this should never be the case. */ gi.DPrintf("G_ClientInvMove: No source-floor found.\n"); return false; } else { /* There already exists an edict for this floor-tile. */ newFloor = false; } /* search for space */ Item* item2; if (tx == NONE) { item2 = actor->chr.inv.getItemAtPos(fromContType, fItem->getX(), fItem->getY()); if (item2) actor->chr.inv.findSpace(toContType, item2, &tx, &ty, fItem); if (tx == NONE) return false; } /** @todo what if we don't have enough TUs after subtracting the reserved ones? */ /* Because moveInInventory don't know anything about character_t and it updates actor->TU, * we need to save original actor->TU for the sake of checking TU reservations. */ originalTU = actor->TU; reservedTU = G_ActorGetReservedTUs(actor); /* Temporary decrease actor->TU to make moveInInventory do what expected. */ G_ActorUseTU(actor, reservedTU); /* Try to actually move the item and check the return value after restoring valid actor->TU. */ ia = game.i.moveInInventory(&actor->chr.inv, fromContType, fItem, toContType, tx, ty, checkaction ? &actor->TU : nullptr, &item2); /* Now restore the original actor->TU and decrease it for TU used for inventory move. */ G_ActorSetTU(actor, originalTU - (originalTU - reservedTU - actor->TU)); switch (ia) { case IA_NONE: /* No action possible - abort */ return false; case IA_NOTIME: G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not enough TUs!")); return false; case IA_NORELOAD: G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - weapon already fully loaded with the same ammunition!")); return false; default: /* Continue below. */ break; } /* successful inventory change; remove the item in clients */ if (fromContType->isFloorDef()) { /* We removed an item from the floor - check how the client * needs to be updated. */ assert(!newFloor); if (actor->getFloor()) { /* There is still something on the floor. */ floor->setFloor(actor); /* Delay this if swapping ammo, otherwise the le will be removed in the client before we can add back * the current ammo because removeNextFrame is set in LE_PlaceItem() if the floor le has no items */ if (ia != IA_RELOAD_SWAP) G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy); } else { /* Floor is empty, remove the edict (from server + client) if we are * not moving to it. */ if (!toContType->isFloorDef()) { G_EventPerish(*floor); G_FreeEdict(floor); } else G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy); } } else { G_EventInventoryDelete(*actor, G_TeamToPM(actor->team), fromContType->id, fx, fy); } /* send tu's */ G_SendStats(*actor); assert(item2); Item item = *item2; if (ia == IA_RELOAD || ia == IA_RELOAD_SWAP) { /* reload */ if (toContType->isFloorDef()) mask = G_VisToPM(floor->visflags); else mask = G_TeamToPM(actor->team); G_EventInventoryReload(toContType->isFloorDef() ? *floor : *actor, mask, &item, toContType, item2); if (ia == IA_RELOAD) { return true; } else { /* ia == IA_RELOAD_SWAP */ item.setAmmoLeft(NONE_AMMO); item.setAmmoDef(nullptr); item.setDef(toItemBackup.ammoDef()); item.rotated = fromItemBackup.rotated; item.setAmount(toItemBackup.getAmount()); toContType = fromContType; if (toContType->isFloorDef()) { /* moveInInventory placed the swapped ammo in an available space, check where it was placed * so we can place it at the same place in the client, otherwise since fItem hasn't been removed * this could end in a different place in the client - will cause an error if trying to use it again */ item2 = actor->chr.inv.findInContainer(toContType->id, &item); assert(item2); fromItemBackup = item; fromItemBackup.setX(item2->getX()); fromItemBackup.setY(item2->getY()); } tx = fromItemBackup.getX(); ty = fromItemBackup.getY(); } } /* We moved an item to the floor - check how the client needs to be updated. */ if (toContType->isFloorDef()) { /* we have to link the temp floor container to the new floor edict or add * the item to an already existing floor edict - the floor container that * is already linked might be from a different entity (this might happen * in case of a throw by another actor) */ floor->setFloor(actor); /* A new container was created for the floor. */ if (newFloor) { /* Send item info to the clients */ G_CheckVis(floor); } else { /* use the backup item to use the old amount values, because the clients have to use the same actions * on the original amount. Otherwise they would end in a different amount of items as the server (+1) */ G_EventInventoryAdd(*floor, G_VisToPM(floor->visflags), 1); G_WriteItem(fromItemBackup, toContType->id, tx, ty); G_EventEnd(); /* Couldn't remove it before because that would remove the le from the client and would cause battlescape to crash * when trying to add back the swapped ammo above */ if (ia == IA_RELOAD_SWAP) G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy); } } else { G_EventInventoryAdd(*actor, G_TeamToPM(actor->team), 1); G_WriteItem(item, toContType->id, tx, ty); G_EventEnd(); } G_ReactionFireSettingsUpdate(actor, actor->chr.RFmode.getFmIdx(), actor->chr.RFmode.getHand(), actor->chr.RFmode.getWeapon()); /* Other players receive weapon info only. */ mask = G_VisToPM(actor->visflags) & ~G_TeamToPM(actor->team); if (mask) { if (fromContType->isRightDef() || fromContType->isLeftDef()) { G_EventInventoryDelete(*actor, mask, fromContType->id, fx, fy); } if (toContType->isRightDef() || toContType->isLeftDef()) { G_EventInventoryAdd(*actor, mask, 1); G_WriteItem(item, toContType->id, tx, ty); G_EventEnd(); } } return true; }
/** * @brief Deals damage of a give type and amount to a target. * @param[in,out] target What we want to damage. * @param[in] fd The fire definition that defines what type of damage is dealt. * @param[in] damage The value of the damage. * @param[in] attacker The attacker. * @param[in] mock pseudo shooting - only for calculating mock values - NULL for real shots * @param[in] impact impact location - @c NULL for splash damage * @sa G_SplashDamage * @sa G_TakeDamage * @sa G_PrintActorStats */ static void G_Damage (edict_t *target, const fireDef_t *fd, int damage, edict_t *attacker, shot_mock_t *mock, const vec3_t impact) { const bool stunEl = (fd->obj->dmgtype == gi.csi->damStunElectro); const bool stunGas = (fd->obj->dmgtype == gi.csi->damStunGas); const bool shock = (fd->obj->dmgtype == gi.csi->damShock); const bool smoke = (fd->obj->dmgtype == gi.csi->damSmoke); bool isRobot; assert(target); /* Breakables */ if (G_IsBrushModel(target) && G_IsBreakable(target)) { /* Breakables are immune to stun & shock damage. */ if (stunEl || stunGas || shock || mock || smoke) return; if (damage >= target->HP) { /* don't reset the HP value here, this value is used to distinguish * between triggered destroy and a shoot */ assert(target->destroy); target->destroy(target); /* maybe the attacker is seeing something new? */ G_CheckVisTeamAll(attacker->team, 0, attacker); /* check if attacker appears/perishes for any other team */ G_CheckVis(attacker); } else { G_TakeDamage(target, damage); } return; } /* Actors don't die again. */ if (!G_IsLivingActor(target)) return; /* only actors after this point - and they must have a teamdef */ assert(target->chr.teamDef); isRobot = CHRSH_IsTeamDefRobot(target->chr.teamDef); /* Apply armour effects. */ if (damage > 0) { damage = G_ApplyProtection(target, fd->dmgweight, damage); } else if (damage < 0) { /* Robots can't be healed. */ if (isRobot) return; } Com_DPrintf(DEBUG_GAME, " Total damage: %d\n", damage); /* Apply difficulty settings. */ if (sv_maxclients->integer == 1) { if (G_IsAlien(attacker) && !G_IsAlien(target)) damage *= pow(1.18, g_difficulty->value); else if (!G_IsAlien(attacker) && G_IsAlien(target)) damage *= pow(1.18, -g_difficulty->value); } assert(attacker->team >= 0 && attacker->team < MAX_TEAMS); assert(target->team >= 0 && target->team < MAX_TEAMS); if (g_nodamage != NULL && !g_nodamage->integer) { /* hit */ if (mock) { G_UpdateShotMock(mock, attacker, target, damage); } else if (stunEl) { target->STUN += damage; } else if (stunGas) { if (!isRobot) /* Can't stun robots with gas */ target->STUN += damage; } else if (shock) { /* Only do this if it's not one from our own team ... they should have known that there was a flashbang coming. */ if (!isRobot && target->team != attacker->team) { /** @todo there should be a possible protection, too */ /* dazed entity wont reaction fire */ G_RemoveReaction(target); G_ActorReserveTUs(target, 0, target->chr.reservedTus.shot, target->chr.reservedTus.crouch); /* flashbangs kill TUs */ G_ActorSetTU(target, 0); G_SendStats(target); /* entity is dazed */ G_SetDazed(target); G_ClientPrintf(G_PLAYER_FROM_ENT(target), PRINT_HUD, _("Soldier is dazed!\nEnemy used flashbang!")); return; } } else { if (damage < 0) { /* The 'attacker' is healing the target. */ G_TreatActor(target, fd, damage, attacker->team); } else { /* Real damage was dealt. */ G_DamageActor(target, damage, impact); /* Update overall splash damage for stats/score. */ if (!mock && damage > 0 && fd->splrad) /**< Check for >0 and splrad to not count this as direct hit. */ G_UpdateHitScore(attacker, target, fd, damage); } } } if (mock) return; G_CheckDeathOrKnockout(target, attacker, fd, damage); }