Esempio n. 1
0
/**
 * @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;
}
Esempio n. 2
0
/**
 * @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;
}
Esempio n. 3
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);
}
Esempio n. 4
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);
}
Esempio n. 5
0
/**
 * @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);
}
Esempio n. 6
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();
		}
	}
}
Esempio n. 7
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)
{
	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);
	}
}
Esempio n. 8
0
/**
 * @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);
}
Esempio n. 9
0
/**
 * @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;
}
Esempio n. 10
0
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);
	}
}
Esempio n. 11
0
/**
 * @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;
}
Esempio n. 12
0
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;
	}
}
Esempio n. 13
0
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;
	}
}
Esempio n. 14
0
/**
 * @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);
}
Esempio n. 15
0
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);
    }
}
Esempio n. 16
0
/**
 * @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;
}
Esempio n. 17
0
/**
 * @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;
}
Esempio n. 18
0
/**
 * @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);
}