Beispiel #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);
	}
}
Beispiel #2
0
/**
 * @brief Let an actor fall down if e.g. the func_breakable the actor was standing on was destroyed.
 * @param[in,out] ent The actor that should fall down
 * @todo Handle cases where the grid position the actor would fall to is occupied by another actor already.
 */
void G_ActorFall (edict_t *ent)
{
    edict_t* entAtPos;
    const int oldZ = ent->pos[2];

    ent->pos[2] = gi.GridFall(gi.routingMap, ent->fieldSize, ent->pos);

    if (oldZ == ent->pos[2])
        return;

    entAtPos = G_GetEdictFromPos(ent->pos, ET_NULL);
    if (entAtPos != NULL && (G_IsBreakable(entAtPos) || G_IsBlockingMovementActor(entAtPos))) {
        const int diff = oldZ - ent->pos[2];
        G_TakeDamage(entAtPos, (int)(FALLING_DAMAGE_FACTOR * (float)diff));
    }

    G_EdictCalcOrigin(ent);
    gi.LinkEdict(ent);

    G_CheckVis(ent, true);

    G_EventActorFall(ent);

    gi.EndEvents();
}
Beispiel #3
0
static void func_object_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	// only squash thing we fall on top of
	if( !plane )
		return;
	if( plane->normal[2] < 1.0 )
		return;
	if( other->takedamage == DAMAGE_NO )
		return;

	G_TakeDamage( other, self, self, vec3_origin, vec3_origin, self->s.origin, self->dmg, 1, 0, 0, MOD_CRUSH );
}
Beispiel #4
0
/**
 * @brief Heals a target and treats wounds.
 * @param[in,out] target Pointer to the actor who we want to treat.
 * @param[in] fd Pointer to the firedef used to heal the target.
 * @param[in] heal The value of the damage to heal.
 * @param[in] healerTeam The index of the team of the healer.
 */
void G_TreatActor (Actor* target, const fireDef_t* const fd, const int heal, const int healerTeam)
{
	assert(target->chr.teamDef);

	/* Treat wounds */
	if (fd->dmgweight == gi.csi->damNormal) {
		int mostWounded = 0;
		woundInfo_t* wounds = &target->chr.wounds;

		/* Find the worst not treated wound */
		for (int bodyPart = 0; bodyPart < target->chr.teamDef->bodyTemplate->numBodyParts(); ++bodyPart)
			if (wounds->woundLevel[bodyPart] > wounds->woundLevel[mostWounded])
				mostWounded = bodyPart;

		if (wounds->woundLevel[mostWounded] > 0) {
			const int woundsHealed = std::min(static_cast<int>(abs(heal) / target->chr.teamDef->bodyTemplate->bleedingFactor(mostWounded)),
					wounds->woundLevel[mostWounded]);
			G_TakeDamage(target, heal);
			wounds->woundLevel[mostWounded] -= woundsHealed;
			wounds->treatmentLevel[mostWounded] += woundsHealed;

			/* Update stats here to get info on how many HP the target received. */
			if (target->chr.scoreMission)
				target->chr.scoreMission->heal += abs(heal);
		}
	}

	/* Treat stunned actors */
	if (fd->dmgweight == gi.csi->damStunElectro && target->isStunned()) {
		if (CHRSH_IsTeamDefAlien(target->chr.teamDef) && target->getTeam() != healerTeam)
			/** @todo According to specs it should only be possible to use the medikit to keep an alien sedated when
			 * 'live alien' is researched, is it possible to find if a tech is researched here? */
			target->setStun(std::min(255, target->getStun() - heal));
		else
			target->setStun(std::max(0, target->getStun() + heal));
		G_ActorCheckRevitalise(target);
	}

	/* Increase morale */
	if (fd->dmgweight == gi.csi->damShock)
		target->setMorale(std::min(GET_MORALE(target->chr.score.skills[ABILITY_MIND]), target->morale - heal));

	G_SendWoundStats(target);
}
Beispiel #5
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();
}
Beispiel #6
0
/**
 * @brief Hurt trigger
 * @sa SP_trigger_hurt
 */
bool Touch_HurtTrigger (Edict* self, Edict* activator)
{
	const int damage = G_ApplyProtection(activator, self->dmgtype, self->dmg);
	const bool stunEl = (self->dmgtype == gi.csi->damStunElectro);
	const bool stunGas = (self->dmgtype == gi.csi->damStunGas);
	const bool shock = (self->dmgtype == gi.csi->damShock);
	const bool isRobot = activator->chr.teamDef->robot;

	/* these actors should really not be able to trigger this - they don't move anymore */
	if (G_IsDead(activator))
		return false;

	if (stunEl || (stunGas && !isRobot)) {
		activator->STUN += damage;
	} else if (shock) {
		/** @todo Handle dazed via trigger_hurt */
	} else {
		G_TakeDamage(activator, damage);
	}

	return true;
}
Beispiel #7
0
/**
 * @brief Hurt trigger
 * @sa SP_trigger_hurt
 */
bool Touch_HurtTrigger (Edict* self, Edict* activator)
{
	/* Dead actors should really not be able to trigger this - they can't be hurt anymore anyway */
	if (!G_IsLivingActor(activator))
		return false;

	/* If no damage is dealt don't count it as triggered */
	const int damage = G_ApplyProtection(activator, self->dmgtype, self->dmg);
	if (damage == 0)
		return false;

	const bool stunEl = (self->dmgtype == gi.csi->damStunElectro);
	const bool stunGas = (self->dmgtype == gi.csi->damStunGas);
	const bool shock = (self->dmgtype == gi.csi->damShock);
	const bool isRobot = activator->chr.teamDef->robot;
	Actor* actor = makeActor(activator);

	if (stunEl || (stunGas && !isRobot)) {
		actor->addStun(damage);
	} else if (shock) {
		/** @todo Handle dazed via trigger_hurt */
	} else {
		G_TakeDamage(actor, damage);
	}
	/* Play hurt sound unless this is shock damage -- it doesn't do anything
	 * because we don't actually handle it above yet */
	if (!shock) {
		const teamDef_t* teamDef = activator->chr.teamDef;
		const int gender = activator->chr.gender;
		const char* sound = teamDef->getActorSound(gender, SND_HURT);
		G_EventSpawnSound(G_PlayerToPM(activator->getPlayer()), *activator, nullptr, sound);
	}

	G_CheckDeathOrKnockout(actor, nullptr, nullptr, damage);

	return true;
}
Beispiel #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);
}
Beispiel #9
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;
}