Ejemplo n.º 1
0
/**
 * @brief Set the reaction fire TU reservation for an actor
 * @param[in,out] ent The actor edict to set the TUs for
 * @return @c true if TUs for reaction fire were reserved, @c false if the reservation was set
 * back to @c 0
 */
bool G_ReactionFireSettingsReserveTUs (edict_t *ent)
{
	if (G_ReactionFireSetDefault(ent) && G_ReactionFireCanBeEnabled(ent)) {
		const int TUs = G_ActorGetTUForReactionFire(ent);
		/* Enable requested reaction fire. */
		G_ActorReserveTUs(ent, TUs, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
		return true;
	}

	G_ActorReserveTUs(ent, 0, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
	return false;
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
0
/**
 * @brief The client sent us a message that he did something. We now execute the related function(s) and notify him if necessary.
 * @param[in] player The player to execute the action for (the actor belongs to this player)
 * @note a client action will also send the server side edict number to determine the actor
 */
int G_ClientAction (player_t * player)
{
	player_action_t action;
	int num;
	pos3_t pos;
	int i;
	fireDefIndex_t firemode;
	int from, fx, fy, to, tx, ty;
	actorHands_t hand;
	int fmIdx, objIdx;
	int resCrouch, resShot;
	edict_t *ent;
	const char *format;

	/* read the header */
	action = (player_action_t)gi.ReadByte();
	num = gi.ReadShort();

	ent = G_EdictsGetByNum(num);
	if (ent == NULL)
		return action;

	format = pa_format[action];

	switch (action) {
	case PA_NULL:
		/* do nothing on a null action */
		break;

	case PA_TURN:
		gi.ReadFormat(format, &i);
		G_ClientTurn(player, ent, (dvec_t) i);
		break;

	case PA_MOVE:
		gi.ReadFormat(format, &pos);
		G_ClientMove(player, player->pers.team, ent, pos);
		break;

	case PA_STATE:
		gi.ReadFormat(format, &i);
		G_ClientStateChange(player, ent, i, true);
		break;

	case PA_SHOOT:
		gi.ReadFormat(format, &pos, &i, &firemode, &from);
		G_ClientShoot(player, ent, pos, i, firemode, NULL, true, from);
		break;

	case PA_INVMOVE:
		gi.ReadFormat(format, &from, &fx, &fy, &to, &tx, &ty);

		if (from < 0 || from >= gi.csi->numIDs || to < 0 || to >= gi.csi->numIDs) {
			gi.DPrintf("G_ClientAction: PA_INVMOVE Container index out of range. (from: %i, to: %i)\n", from, to);
		} else {
			const invDef_t *fromPtr = INVDEF(from);
			const invDef_t *toPtr = INVDEF(to);
			invList_t *fromItem = INVSH_SearchInInventory(&ent->chr.i, fromPtr, fx, fy);
			if (fromItem)
				G_ActorInvMove(ent, fromPtr, fromItem, toPtr, tx, ty, true);
		}
		break;

	case PA_USE:
		if (ent->clientAction) {
			edict_t *actionEnt;

			/* read the door the client wants to open */
			gi.ReadFormat(format, &i);

			/* get the door edict */
			actionEnt = G_EdictsGetByNum(i);

			/* maybe the door is no longer 'alive' because it was destroyed */
			if (actionEnt && ent->clientAction == actionEnt) {
				if (G_IsDoor(actionEnt)) {
					G_ActorUseDoor(ent, actionEnt);
				}
			}
		}
		break;

	case PA_REACT_SELECT:
		gi.ReadFormat(format, &hand, &fmIdx, &objIdx);
		G_ReactionFireSettingsUpdate(ent, fmIdx, hand, INVSH_GetItemByIDX(objIdx));
		break;

	case PA_RESERVE_STATE:
		gi.ReadFormat(format, &resShot, &resCrouch);

		G_ActorReserveTUs(ent, ent->chr.reservedTus.reaction, resShot, resCrouch);
		break;

	default:
		gi.Error("G_ClientAction: Unknown action!\n");
	}
	return action;
}
Ejemplo n.º 4
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);
}
Ejemplo n.º 5
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 - nullptr for real shots
 * @param[in] impact impact location - @c nullptr for splash damage
 * @return @c true if damage could be dealt (even if it was 0) @c false otherwise
 * @sa G_SplashDamage
 * @sa G_TakeDamage
 * @sa G_PrintActorStats
 */
static bool G_Damage (Edict* target, const fireDef_t* fd, int damage, Actor* attacker, shot_mock_t* mock, const vec3_t impact)
{
	assert(target);

	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);

	/* Breakables */
	if (G_IsBrushModel(target) && G_IsBreakable(target)) {
		/* Breakables are immune to stun & shock damage. */
		if (stunEl || stunGas || shock || mock || smoke)
			return false;

		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->getTeam(), 0, attacker);

			/* check if attacker appears/perishes for any other team */
			G_CheckVis(attacker);
		} else {
			G_TakeDamage(target, damage);
		}
		return true;
	}

	/* Actors don't die again. */
	if (!G_IsLivingActor(target))
		return false;
	/* Now we know that the target is an actor */
	Actor* victim = makeActor(target);

	/* only actors after this point - and they must have a teamdef */
	assert(victim->chr.teamDef);
	const bool isRobot = CHRSH_IsTeamDefRobot(victim->chr.teamDef);

	/* Apply armour effects. */
	if (damage > 0) {
		damage = G_ApplyProtection(victim, fd->dmgweight, damage);
	} else if (damage < 0) {
		/* Robots can't be healed. */
		if (isRobot)
			return false;
	}
	Com_DPrintf(DEBUG_GAME, " Total damage: %d\n", damage);

	/* Apply difficulty settings. */
	if (G_IsSinglePlayer()) {
		if (G_IsAlien(attacker) && !G_IsAlien(victim))
			damage *= pow(1.18f, g_difficulty->value);
		else if (!G_IsAlien(attacker) && G_IsAlien(victim))
			damage *= pow(1.18f, -g_difficulty->value);
	}

	assert(attacker->getTeam() >= 0 && attacker->getTeam() < MAX_TEAMS);
	assert(victim->getTeam() >= 0 && victim->getTeam() < MAX_TEAMS);

	if ((g_nodamage != nullptr && !g_nodamage->integer) || mock) {
		/* hit */
		if (mock) {
			G_UpdateShotMock(mock, attacker, victim, damage);
		} else if (stunEl) {
			victim->addStun(damage);
		} else if (stunGas) {
			if (!isRobot) /* Can't stun robots with gas */
				victim->addStun(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 && !victim->isSameTeamAs(attacker)) {
				/** @todo there should be a possible protection, too */
				/* dazed entity wont reaction fire */
				victim->removeReaction();
				G_ActorReserveTUs(victim, 0, victim->chr.reservedTus.shot, victim->chr.reservedTus.crouch);
				/* flashbangs kill TUs */
				G_ActorSetTU(victim, 0);
				G_SendStats(*victim);
				/* entity is dazed */
				victim->setDazed();
				G_EventSendState(G_VisToPM(victim->visflags), *victim);
				return !mock;
			} else {
				return false;
			}
		} else {
			if (damage < 0) {
				/* The 'attacker' is healing the victim. */
				G_TreatActor(victim, fd, damage, attacker->getTeam());
			} else {
				/* Real damage was dealt. */
				G_DamageActor(victim, 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, victim, fd, damage);
			}
		}
	}

	if (mock)
		return false;

	G_CheckDeathOrKnockout(victim, attacker, fd, damage);
	return true;
}