Пример #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;
}
Пример #2
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);
}
Пример #3
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_t *ent = NULL;
	while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team)) != NULL) {
		/* 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 (ratio > (m_rage->value * frand()))
					G_MoralePanic(ent, sanity);
				else
					G_MoraleRage(ent, sanity);
				/* 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(G_PLAYER_FROM_ENT(ent), ent, STATE_REACTION, false);
				G_EventSendState(G_VisToPM(ent->visflags), ent);
				G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("%s is currently shaken."),
						ent->chr.name);
				G_PrintStats("%s is shaken (entnum %i).", ent->chr.name, ent->number);
			}
		} 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);
	}
}
Пример #4
0
/**
 * @brief Get the weapon firing TUs of the item in the right hand of the edict.
 * @return -1 if no firedef was found for the item or the reaction fire mode is not activated for the right hand.
 * @todo why only right hand?
 * @param[in] ent The reaction firing actor
 * @param[in] target The target to check reaction fire for (e.g. check whether the weapon that was marked for
 * using in reaction fire situations can handle the distance between the shooter and the target)
 * @param[in] invList The items that are checked for reaction fire
 * @note This does 'not' return the weapon (lowest TU costs, highest damage, highest accuracy) but the first weapon that
 * would fit for reaction fire.
 */
static int G_ReactionFireGetTUsForItem (const edict_t *ent, const edict_t *target, const invList_t *invList)
{
	if (invList && invList->item.m && invList->item.t->weapon
	 && (!invList->item.t->reload || invList->item.a > 0)) {
		const fireDef_t *fdArray = FIRESH_FiredefForWeapon(&invList->item);
		const chrFiremodeSettings_t *fmSetting;
		if (fdArray == NULL)
			return -1;

		fmSetting = &ent->chr.RFmode;
		if (fmSetting->hand == ACTOR_HAND_RIGHT && fmSetting->fmIdx >= 0
		 && fmSetting->fmIdx < MAX_FIREDEFS_PER_WEAPON) { /* If a RIGHT-hand firemode is selected and sane. */
			const fireDefIndex_t fmIdx = fmSetting->fmIdx;
			const int reactionFire = G_PLAYER_FROM_ENT(ent)->reactionLeftover;
			const fireDef_t *fd = &fdArray[fmIdx];
			const int tus = fd->time + reactionFire;

			if (tus <= ent->TU && fd->range > VectorDist(ent->origin, target->origin)) {
				return tus;
			}
		}
	}

	return -1;
}
Пример #5
0
/**
 * @brief Perform the reaction fire shot
 * @param[in] shooter The actor that is trying to shoot
 * @param[in] at Position to fire on.
 * @param[in] type What type of shot this is (left, right reaction-left etc...).
 * @param[in] firemode The firemode index of the ammo for the used weapon (objDef.fd[][x])  .
 * @return true if everything went ok (i.e. the shot(s) where fired ok), otherwise false.
 * @sa G_ClientShoot
 */
static bool G_ReactionFireShoot (edict_t *shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode)
{
	const int minhit = 30;
	shot_mock_t mock;
	int i;
	const player_t *player = G_PLAYER_FROM_ENT(shooter);
	/* this is the max amount of friendly units that were hit during the mock calculation */
	int maxff;

	if (G_IsInsane(shooter))
		maxff = 100;
	else if (G_IsRaged(shooter))
		maxff = 60;
	else if (G_IsPaniced(shooter))
		maxff = 30;
	else if (G_IsShaken(shooter))
		maxff = 15;
	else
		maxff = 5;

	/* calculate the mock values - e.g. how many friendly units we would hit
	 * when opening the reaction fire */
	OBJZERO(mock);
	for (i = 0; i < 100; i++)
		if (!G_ClientShoot(player, shooter, at, type, firemode, &mock, false, 0))
			break;

	const int ff = mock.friendCount + (G_IsAlien(shooter) ? 0 : mock.civilian);
	if (ff <= maxff && mock.enemyCount >= minhit)
		return G_ClientShoot(player, shooter, at, type, firemode, NULL, false, 0);

	return false;
}
Пример #6
0
/**
 * @brief Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot
 * @param[in] ent The entity to resolve reaction fire for
 * @return true if the entity fired (or would have fired if mock), false otherwise
 */
static qboolean G_ReactionFireTryToShoot (edict_t *ent)
{
	qboolean tookShot;

	/* check whether this ent has a reaction fire queued */
	assert(ent->reactionTarget);

	/* ent can't take a reaction shot if it's not possible - and check that
	 * the target is still alive */
	if (!G_ReactionFireIsPossible(ent, ent->reactionTarget)) {
		ent->reactionTarget = NULL;
		return qfalse;
	}

	/* take the shot */
	tookShot = G_ReactionFireShoot(G_PLAYER_FROM_ENT(ent), ent, ent->reactionTarget->pos, ST_RIGHT_REACTION, ent->chr.RFmode.fmIdx);

	if (tookShot) {
		/* clear any shakenness */
		G_RemoveShaken(ent);

		/* check whether further reaction fire is possible */
		if (G_ReactionFireIsPossible(ent, ent->reactionTarget)){
			/* see how quickly ent can fire (if it can fire at all) */
			const int tus = G_ReactionFireGetTUsForItem(ent, ent->reactionTarget, RIGHT(ent));
			if (tus >= 0) {
				/* An enemy getting reaction shot gets more time before
				 * reaction fire is repeated. */
				ent->reactionTUs = max(0, ent->reactionTarget->TU - tus);
			}
		}
	}

	return tookShot;
}
Пример #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)
{
	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();
		}
	}
}
Пример #8
0
/**
 * @brief Will inform the player about the real TU reservation
 * @param ent The actors edict.
 */
void G_EventActorSendReservations (const edict_t *ent)
{
	G_EventAdd(G_PlayerToPM(G_PLAYER_FROM_ENT(ent)), EV_ACTOR_RESERVATIONCHANGE, ent->number);
	gi.WriteShort(ent->chr.reservedTus.reaction);
	gi.WriteShort(ent->chr.reservedTus.shot);
	gi.WriteShort(ent->chr.reservedTus.crouch);

	G_EventEnd();
}
Пример #9
0
/**
 * @brief Actor has been wounded/treated
 * @param[in] ent The actor whose wound status has changed
 * @note This event is sent to the player this actor belongs to
 */
void G_EventActorWound (const edict_t *ent, const int bodyPart)
{
	const int mask = G_PlayerToPM(G_PLAYER_FROM_ENT(ent));
	G_EventAdd(mask, EV_ACTOR_WOUND, ent->number);
	gi.WriteByte(bodyPart);
	gi.WriteByte(ent->chr.wounds.woundLevel[bodyPart]);
	gi.WriteByte(ent->chr.wounds.treatmentLevel[bodyPart]);
	G_EventEnd();
}
Пример #10
0
void G_EventReactionFireChange (const edict_t* ent)
{
	const objDef_t *od = ent->chr.RFmode.weapon;

	G_EventAdd(G_PlayerToPM(G_PLAYER_FROM_ENT(ent)), EV_ACTOR_REACTIONFIRECHANGE, ent->number);
	gi.WriteByte(ent->chr.RFmode.fmIdx);
	gi.WriteByte(ent->chr.RFmode.getHand());
	gi.WriteShort(od ? od->idx : NONE);

	G_EventEnd();
}
Пример #11
0
/**
 * @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);
}
Пример #12
0
/**
 * @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);
}
Пример #13
0
/**
 * @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] ent 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_ReactionFireUpdate (edict_t *ent, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t *od)
{
	ent->chr.RFmode.set(hand, fmIdx, od);	/* FiremodeSettings */

	if (!G_ActorHasWorkingFireModeSet(ent)) {
		/* Disable reaction fire if no valid firemode was found. */
		G_ClientStateChange(G_PLAYER_FROM_ENT(ent), ent, ~STATE_REACTION, true);
		return;
	}

	G_EventReactionFireChange(ent);

	/* If reaction fire is active, update the reserved TUs */
	if (G_IsReaction(ent)) {
		G_ReactionFireSettingsReserveTUs(ent);
	}
}
Пример #14
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;
	}
}
Пример #15
0
/**
 * @brief Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot
 * @param[in] shooter The entity to resolve reaction fire for
 * @param[in] target The victim of the reaction fire
 * @return true if the entity fired (or would have fired if mock), false otherwise
 */
static bool G_ReactionFireTryToShoot (edict_t *shooter, const edict_t *target)
{
	bool tookShot;

	/* check for valid target */
	assert(target);

	/* shooter can't take a reaction shot if it's not possible - and check that
	 * the target is still alive */
	if (!G_ReactionFireIsPossible(shooter, target)) {
		G_ReactionFireTargetsRemove(shooter, target);
		return false;
	}

	/* take the shot */
	tookShot = G_ReactionFireShoot(G_PLAYER_FROM_ENT(shooter), shooter, target->pos, ST_RIGHT_REACTION, shooter->chr.RFmode.fmIdx);

	if (tookShot) {
		/* clear any shakenness */
		G_RemoveShaken(shooter);
	}

	return tookShot;
}
Пример #16
0
/**
 * @brief Reset the client actions for the given entity
 * @param[in] ent The entity to reset the client action for
 * @note This event is send to the player this edict belongs to
 */
void G_EventResetClientAction (const edict_t* ent)
{
	const int playerMask = G_PlayerToPM(G_PLAYER_FROM_ENT(ent));
	G_EventAdd(playerMask, EV_RESET_CLIENT_ACTION, ent->number);
	G_EventEnd();
}
Пример #17
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);
}
Пример #18
0
/**
 * @brief Think function for actors that spawn crouched
 * @param ent The actor
 */
static void G_ThinkActorGoCrouch (edict_t *ent)
{
	G_ClientStateChange(G_PLAYER_FROM_ENT(ent), ent, STATE_CROUCHED, true);
	ent->think = NULL;
}