Пример #1
0
/**
 * @brief Deals damage and causes wounds.
 * @param[in,out] target Pointer to the actor we want to damage.
 * @param[in] damage The value of the damage.
 * @param[in] impact Impact location @c nullptr for splash damage.
 */
void G_DamageActor (Edict* target, const int damage, const vec3_t impact)
{
	assert(target->chr.teamDef);

	G_TakeDamage(target, damage);
	if (damage > 0 && target->HP > 0) {
		const teamDef_t* const teamDef = target->chr.teamDef;
		if (impact) {
			/* Direct hit */
			const byte impactDirection = G_GetImpactDirection(target, impact);
			const float impactHeight = impact[2] / (target->absBox.mins[2] + target->absBox.maxs[2]);
			const int bodyPart = teamDef->bodyTemplate->getHitBodyPart(impactDirection, impactHeight);
			target->chr.wounds.woundLevel[bodyPart] += damage;
		} else {
			/* No direct hit (splash damage) */
			for (int bodyPart = 0; bodyPart < teamDef->bodyTemplate->numBodyParts(); ++bodyPart)
				target->chr.wounds.woundLevel[bodyPart] += teamDef->bodyTemplate->getArea(bodyPart) * damage;
		}
#if 0
		if (!CHRSH_IsTeamDefRobot(target->chr.teamDef))
			/* Knockback -- currently disabled, not planned in the specs, also there's no way to tell stunned and dead actors apart */
			target->STUN = std::min(255.0f, target->STUN + std::max(0.0f, damage * crand() * 0.25f));
#endif
		G_SendWoundStats(target);
	}
}
Пример #2
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);
	}
}
Пример #3
0
/**
 * @brief Sets correct bounding box for actor (state dependent).
 * @param[in] ent Pointer to entity for which bounding box is being set.
 * @note Also re-links the actor edict - because the server must know about the
 * changed bounding box for tracing to work.
 */
void G_ActorSetMaxs (Edict* ent)
{
	if (G_IsCrouched(ent))
		VectorSet(ent->entBox.maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_CROUCH);
	else if (G_IsDead(ent) && !CHRSH_IsTeamDefRobot(ent->chr.teamDef))
		VectorSet(ent->entBox.maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_DEAD);
	else
		VectorSet(ent->entBox.maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_STAND);

	/* Link it. */
	gi.LinkEdict(ent);
}
static void HOS_EntryWoundData (const character_t *const chr, const int entry)
{
	const BodyData *bodyData = chr->teamDef->bodyTemplate;
	int bodyPart;
	const woundInfo_t *const wounds = &chr->wounds;

	for (bodyPart = 0; bodyPart < bodyData->numBodyParts(); ++bodyPart) {
		if (wounds->treatmentLevel[bodyPart] != 0) {
			const float severity = static_cast<float>(wounds->treatmentLevel[bodyPart]) / chr->maxHP;
			char text[MAX_VAR];
			Com_sprintf(text, lengthof(text), CHRSH_IsTeamDefRobot(chr->teamDef) ? _("Damaged %s (damage: %i)") :
					_("Wounded %s (damage: %i)"), _(bodyData->name(bodyPart)), wounds->treatmentLevel[bodyPart]);
			cgi->UI_ExecuteConfunc("hospital_wounds %i %s %f \"%s\"", entry, bodyData->id(bodyPart), severity, text);
		}
	}
}
Пример #5
0
/**
 * @brief Returns the body model for the soldiers for armoured and non armoured soldiers
 * @param[in] chr Pointer to character struct
 * @sa CHRSH_CharGetBody
 * @return the character body model (from a static buffer)
 */
const char *CHRSH_CharGetBody (const character_t * const chr)
{
	static char returnModel[MAX_VAR];

	/* models of UGVs don't change - because they are already armoured */
	if (INVSH_HasArmour(&chr->i) && !CHRSH_IsTeamDefRobot(chr->teamDef)) {
		const objDef_t *od = INVSH_HasArmour(&chr->i)->item.t;
		const char *id = od->armourPath;
		if (!INV_IsArmour(od))
			Sys_Error("CHRSH_CharGetBody: Item is no armour");

		Com_sprintf(returnModel, sizeof(returnModel), "%s%s/%s", chr->path, id, chr->body);
	} else
		Com_sprintf(returnModel, sizeof(returnModel), "%s/%s", chr->path, chr->body);
	return returnModel;
}
Пример #6
0
/**
 * @brief Deal damage to each wounded team member.
 * @param[in] team The index of the team to deal damage to.
 */
void G_BleedWounds (const int team)
{
	Actor* actor = nullptr;

	while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, team))) {
		if (CHRSH_IsTeamDefRobot(actor->chr.teamDef))
			continue;
		const teamDef_t* const teamDef = actor->chr.teamDef;
		const woundInfo_t& wounds = actor->chr.wounds;
		int damage = 0;
		for (int bodyPart = 0; bodyPart < teamDef->bodyTemplate->numBodyParts(); ++bodyPart)
			if (wounds.woundLevel[bodyPart] > actor->chr.maxHP * teamDef->bodyTemplate->woundThreshold(bodyPart))
				damage += wounds.woundLevel[bodyPart] * teamDef->bodyTemplate->bleedingFactor(bodyPart);
		if (damage > 0) {
			G_PrintStats("%s is bleeding (damage: %i)", actor->chr.name, damage);
			G_TakeDamage(actor, damage);
			G_CheckDeathOrKnockout(actor, nullptr, nullptr, damage);
		}
	}
	/* Maybe the last team member bled to death */
	G_MatchEndCheck();
}
Пример #7
0
/**
 * @brief Returns if storing a specific life form is supported by the containment
 * @param[in] team Pointer to the alien Team Definition
 */
bool AlienContainment::isLifeSupported(const teamDef_t* team)
{
	/* No team - not supported */
	if (!team)
		return false;

	/* humans supported */
	if (!CHRSH_IsTeamDefAlien(team))
		return true;

	/* Robots are supported */
	if (CHRSH_IsTeamDefRobot(team))
		return true;

	/* Organic aliens need breathing apparatus known */
	/** @todo find a way that doesn't need a tech ID hardcoded */
	const technology_t* tech = RS_GetTechByID(BREATHINGAPPARATUS_TECH);
	if (!tech)
		return false;

	return RS_IsResearched_ptr(tech);
}
Пример #8
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);
}
Пример #9
0
/**
 * @brief Puts alien cargo into Alien Containment.
 * @param[in] aircraft Aircraft transporting cargo to homebase.
 * @sa B_AircraftReturnedToHomeBase
 * @sa AL_FillInContainment
 * @note an event mail about missing breathing tech will be triggered if necessary.
 */
void AL_AddAliens (aircraft_t *aircraft)
{
	base_t *toBase;
	const aliensTmp_t *cargo;
	int alienCargoTypes;
	int i;
	int j;
	qboolean limit = qfalse;
	qboolean messageAlreadySet = qfalse;
	technology_t *breathingTech;
	qboolean alienBreathing = qfalse;
	const objDef_t *alienBreathingObjDef;

	assert(aircraft);
	toBase = aircraft->homebase;
	assert(toBase);

	cargo = AL_GetAircraftAlienCargo(aircraft);
	alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft);

	if (alienCargoTypes == 0)
		return;

	if (!B_GetBuildingStatus(toBase, B_ALIEN_CONTAINMENT)) {
		MS_AddNewMessage(_("Notice"), _("You cannot process aliens yet. Alien Containment not ready in this base."), qfalse, MSG_STANDARD, NULL);
		return;
	}

	breathingTech = RS_GetTechByID(BREATHINGAPPARATUS_TECH);
	if (!breathingTech)
		Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus tech definition");
	alienBreathing = RS_IsResearched_ptr(breathingTech);
	alienBreathingObjDef = INVSH_GetItemByID(breathingTech->provides);
	if (!alienBreathingObjDef)
		Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus item definition");

	for (i = 0; i < alienCargoTypes; i++) {
		for (j = 0; j < ccs.numAliensTD; j++) {
			assert(toBase->alienscont[j].teamDef);
			assert(cargo[i].teamDef);

			if (toBase->alienscont[j].teamDef == cargo[i].teamDef) {
				toBase->alienscont[j].amountDead += cargo[i].amountDead;
				/* Add breathing apparatuses to aircraft cargo so that they are processed with other collected items */
				AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountDead);
				if (cargo[i].amountAlive <= 0)
					continue;
				if (!alienBreathing && !CHRSH_IsTeamDefRobot(cargo[i].teamDef)) {
					/* We can not store living (i.e. no robots or dead bodies) aliens without rs_alien_breathing tech */
					toBase->alienscont[j].amountDead += cargo[i].amountAlive;
					/* Add breathing apparatuses as well */
					AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountAlive);
					/* only once */
					if (!messageAlreadySet) {
						MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), qfalse, MSG_DEATH, NULL);
						messageAlreadySet = qtrue;
					}
					if (!ccs.breathingMailSent) {
						Cmd_ExecuteString("addeventmail alienbreathing");
						ccs.breathingMailSent = qtrue;
					}
				} else {
					int k;

					for (k = 0; k < cargo[i].amountAlive; k++) {
						/* Check base capacity. */
						if (AL_CheckAliveFreeSpace(toBase, NULL, 1)) {
							AL_ChangeAliveAlienNumber(toBase, &(toBase->alienscont[j]), 1);
						} else {
							/* Every exceeding alien is killed
							 * Display a message only when first one is killed */
							if (!limit) {
								toBase->capacities[CAP_ALIENS].cur = toBase->capacities[CAP_ALIENS].max;
								MS_AddNewMessage(_("Notice"), _("You don't have enough space in Alien Containment. Some aliens got killed."), qfalse, MSG_STANDARD, NULL);
								limit = qtrue;
							}
							/* Just kill aliens which don't fit the limit. */
							toBase->alienscont[j].amountDead++;
							AII_CollectItem(aircraft, alienBreathingObjDef, 1);
						}
					}
					/* only once */
					if (!messageAlreadySet) {
						MS_AddNewMessage(_("Notice"), _("You've captured new aliens."), qfalse, MSG_STANDARD, NULL);
						messageAlreadySet = qtrue;
					}
				}
				break;
			}
		}
	}

	for (i = 0; i < ccs.numAliensTD; i++) {
		aliensCont_t *ac = &toBase->alienscont[i];
		technology_t *tech = ac->tech;
#ifdef DEBUG
		if (!tech)
			Sys_Error("AL_AddAliens: Failed to initialize the tech for '%s'\n", ac->teamDef->name);
#endif
		/* we need this to let RS_Collected_ return true */
		if (ac->amountAlive + ac->amountDead > 0)
			RS_MarkCollected(tech);
#ifdef DEBUG
		/* print all of them */
		if (ac->amountAlive > 0)
			Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens alive: %s amount: %i\n", ac->teamDef->name, ac->amountAlive);
		if (ac->amountDead > 0)
			Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens bodies: %s amount: %i\n", ac->teamDef->name, ac->amountDead);
#endif
	}

	/* we shouldn't have any more aliens on the aircraft after this */
	AL_SetAircraftAlienCargoTypes(aircraft, 0);
}
Пример #10
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;
}