<commit_before>/*
 * CombatManager.cpp
 *
 *  Created on: 24/02/2010
 *      Author: victor
 */

#include "CombatManager.h"
#include "CreatureAttackData.h"
#include "server/zone/objects/scene/variables/DeltaVector.h"
#include "server/zone/objects/creature/CreatureObject.h"
#include "server/zone/objects/player/PlayerObject.h"
#include "server/zone/objects/player/events/LogoutTask.h"
#include "server/zone/objects/creature/CreatureState.h"
#include "server/zone/objects/creature/commands/CombatQueueCommand.h"
#include "server/zone/objects/creature/CreatureAttribute.h"
#include "server/zone/packets/object/CombatSpam.h"
#include "server/zone/packets/object/CombatAction.h"
#include "server/zone/packets/chat/ChatSystemMessage.h"
#include "server/zone/packets/tangible/UpdatePVPStatusMessage.h"
#include "server/zone/Zone.h"
#include "server/zone/managers/collision/CollisionManager.h"
#include "server/zone/objects/creature/buffs/StateBuff.h"
#include "server/zone/managers/visibility/VisibilityManager.h"
#include "server/zone/managers/creature/CreatureManager.h"
#include "server/zone/managers/creature/LairObserver.h"
#include "server/zone/managers/reaction/ReactionManager.h"
#include "server/zone/objects/installation/components/TurretDataComponent.h"
#include "server/zone/objects/creature/AiAgent.h"

const uint32 CombatManager::defaultAttacks[9] = {
		0x99476628, 0xF5547B91, 0x3CE273EC, 0x734C00C,
		0x43C4FFD0, 0x56D7CC78, 0x4B41CAFB, 0x2257D06B,
		0x306887EB
};


bool CombatManager::startCombat(CreatureObject* attacker, TangibleObject* defender, bool lockDefender) {
	if (attacker == defender)
		return false;

	if (attacker->getZone() == NULL || defender->getZone() == NULL)
		return false;

	if (attacker->isRidingMount()) {
		ManagedReference<CreatureObject*> parent = attacker->getParent().get().castTo<CreatureObject*>();

		if (parent == NULL || !parent->isMount())
			return false;

		if (attacker->hasBuff(String("gallop").hashCode()))
			return false;
	}

	if (attacker->hasRidingCreature())
		return false;

	if (!defender->isAttackableBy(attacker))
		return false;

	if (defender->isCreatureObject() && (cast<CreatureObject*>(defender))->isIncapacitated())
		return false;

	if (attacker->isPlayerCreature() && attacker->getPlayerObject()->isAFK())
		return false;

	attacker->clearState(CreatureState::PEACE);

	Locker clocker(defender, attacker);

	attacker->setDefender(defender);
	defender->addDefender(attacker);

	return true;
}

bool CombatManager::attemptPeace(CreatureObject* attacker) {
	DeltaVector<ManagedReference<SceneObject*> >* defenderList = attacker->getDefenderList();

	for (int i = defenderList->size() - 1; i >= 0; --i) {
		ManagedReference<SceneObject*> object = defenderList->get(i);

		TangibleObject* defender = cast<TangibleObject*>( object.get());

		if (defender == NULL)
			continue;

		try {
			Locker clocker(defender, attacker);

			if (defender->hasDefender(attacker)) {

				if (defender->isCreatureObject()) {
					CreatureObject* creature = cast<CreatureObject*>(defender);

					if (creature->getMainDefender() != attacker || creature->hasState(CreatureState::PEACE) || creature->isDead() || attacker->isDead() || !creature->isInRange(attacker, 128.f)) {
						attacker->removeDefender(defender);
						defender->removeDefender(attacker);
					}
				} else {
					attacker->removeDefender(defender);
					defender->removeDefender(attacker);
				}
			} else {
				attacker->removeDefender(defender);
			}

			clocker.release();

		} catch (Exception& e) {
			error(e.getMessage());
			e.printStackTrace();
		}
	}

	if (defenderList->size() != 0) {
		//info("defenderList not empty, trying to set Peace State");

		attacker->setState(CreatureState::PEACE);

		return false;
	} else {
		attacker->clearCombatState(false);

		// clearCombatState() (rightfully) does not automatically set peace, so set it
		attacker->setState(CreatureState::PEACE);

		return true;
	}
}

void CombatManager::forcePeace(CreatureObject* attacker) {
	DeltaVector<ManagedReference<SceneObject*> >* defenderList = attacker->getDefenderList();

	for (int i = 0; i < defenderList->size(); ++i) {
		ManagedReference<SceneObject*> object = defenderList->get(i);

		if (!object->isTangibleObject())
			continue;

		TangibleObject* defender = cast<TangibleObject*>( object.get());

		Locker clocker(defender, attacker);

		if (defender->hasDefender(attacker)) {
			attacker->removeDefender(defender);
			defender->removeDefender(attacker);
		} else {
			attacker->removeDefender(defender);
		}

		--i;

		clocker.release();
	}

	attacker->clearCombatState(false);
	attacker->setState(CreatureState::PEACE);
}



int CombatManager::doCombatAction(CreatureObject* attacker, WeaponObject* weapon, TangibleObject* defenderObject, CombatQueueCommand* command) {
	return doCombatAction(attacker, weapon, defenderObject, CreatureAttackData("",command));
}

int CombatManager::doCombatAction(CreatureObject* attacker, WeaponObject* weapon, TangibleObject* defenderObject, const CreatureAttackData& data) {
	//info("entering doCombat action with data ", true);

	if (!startCombat(attacker, defenderObject))
		return -1;

	//info("past start combat", true);

	if (attacker->hasAttackDelay() || !attacker->checkKnockdownRecovery())
		return -3;

	//info("past delay", true);

	if (!applySpecialAttackCost(attacker, weapon, data))
		return -2;

	//info("past special attack cost", true);

	int damage = 0;

	if (data.getCommand()->isAreaAction() || data.getCommand()->isConeAction())
		damage = doAreaCombatAction(attacker, weapon, defenderObject, data);
	else
		damage = doTargetCombatAction(attacker, weapon, defenderObject, data);

	if (damage > 0) {
		attacker->updateLastSuccessfulCombatAction();
	}

	return damage;
}

int CombatManager::doCombatAction(TangibleObject* attacker, WeaponObject* weapon, TangibleObject* defender, CombatQueueCommand* command){

	if(weapon != NULL){
		if(!command->isAreaAction()){
			return doTargetCombatAction(attacker, weapon, defender, CreatureAttackData("",command));
		} else {
			return doAreaCombatAction(attacker, weapon, defender, CreatureAttackData("",command));
		}
	}

	return 0;
}

int CombatManager::doTargetCombatAction(CreatureObject* attacker, WeaponObject* weapon, TangibleObject* tano, const CreatureAttackData& data) {
	int damage = 0;

	Locker clocker(tano, attacker);

	if (!tano->isAttackableBy(attacker))
		return 0;

	attacker->addDefender(tano);
	tano->addDefender(attacker);

	if (tano->isCreatureObject()) {
		CreatureObject* defender = cast<CreatureObject*>( tano);

		if (defender->getWeapon() == NULL)
			return 0;

		damage = doTargetCombatAction(attacker, weapon, defender, data);
	} else {
		int poolsToDamage = calculatePoolsToDamage(data.getPoolsToDamage());

		damage = applyDamage(attacker, weapon, tano, poolsToDamage, data);

		broadcastCombatAction(attacker, tano, weapon, data, 0x01);

		data.getCommand()->sendAttackCombatSpam(attacker, tano, HIT, damage, data);
	}

	tano->notifyObservers(ObserverEventType::DAMAGERECEIVED, attacker, damage);

	if (damage > 0 && tano->isAiAgent()) {
		AiAgent* aiAgent = cast<AiAgent*>(tano);
		aiAgent->sendReactionChat(ReactionManager::HIT);
	}

	if (damage > 0 && attacker->isAiAgent()) {
		AiAgent* aiAgent = cast<AiAgent*>(attacker);
		aiAgent->sendReactionChat(ReactionManager::HITTARGET);
	}

	return damage;
}

int CombatManager::doTargetCombatAction(CreatureObject* attacker, WeaponObject* weapon, CreatureObject* defender, const CreatureAttackData& data) {
	if (defender->isEntertaining())
		defender->stopEntertaining();

	int hitVal = 0;
	float damageMultiplier = data.getDamageMultiplier();

	// need to calculate damage here to get proper client spam
	int damage = 0;
	Vector<int> foodMitigation; //used to save the food mitigation value for combat spam sent later.

	if (damageMultiplier != 0)
		damage = calculateDamage(attacker, weapon, defender, data, foodMitigation) * damageMultiplier;

	damageMultiplier = 1.0f;
	hitVal = getHitChance(attacker, defender, weapon, damage, data.getAccuracyBonus() + attacker->getSkillMod(data.getCommand()->getAccuracySkillMod()));

	int poolsToDamage = calculatePoolsToDamage(data.getPoolsToDamage());
	// TODO: animations are probably determined by which pools are damaged (high, mid, low, combos, etc)

	broadcastCombatAction(attacker, defender, weapon, data, hitVal);

	//Send Attack Combat Spam. For state-only attacks, this is sent in applyStates().
	if (!data.isStateOnlyAttack())
		data.getCommand()->sendAttackCombatSpam(attacker, defender, hitVal, damage, data);

	switch (hitVal) {
	case MISS:
		doMiss(attacker, weapon, defender, damage);
		if (data.isStateOnlyAttack()) //Send combat spam for failed state attack.
			data.getCommand()->sendAttackCombatSpam(attacker, defender, hitVal, damage, data);
		return 0;
		break;
	case HIT:
		break;
	case BLOCK:
		doBlock(attacker, weapon, defender, damage);
		damageMultiplier = 0.5f;
		break;
	case DODGE:
		doDodge(attacker, weapon, defender, damage);
		damageMultiplier = 0.0f;
		break;
	case COUNTER:
		doCounterAttack(attacker, weapon, defender, damage);
		//defender->enqueueCommand(String("counterattack").hashCode(), 0, attacker->getObjectID(), "");
		damageMultiplier = 0.0f;
		break;
	case RICOCHET:
		doLightsaberBlock(attacker, weapon, defender, damage);
		damageMultiplier = 0.0f;
		break;
	default:
		break;
	}

	// Apply states first
	applyStates(attacker, defender, data);

	// Return if it's a state only attack (intimidate, warcry, wookiee roar) so they don't apply dots or break combat delays
	if (poolsToDamage == 0)
		return 0;

	if (defender->hasAttackDelay())
		defender->removeAttackDelay();

	if (damageMultiplier != 0 && damage != 0) {
		// Apply dots before damage so that target's armor doesn't reduce
		applyDots(attacker, defender, data, damage);
		applyWeaponDots(attacker, defender, weapon);

		damage = applyDamage(attacker, weapon, defender, damage, damageMultiplier, poolsToDamage, data);
	}

	//Send defensive buff combat spam last.
	if (!foodMitigation.isEmpty())
		sendMitigationCombatSpam(defender, weapon, foodMitigation.get(0), FOOD);

	return damage;
}

int CombatManager::doTargetCombatAction(TangibleObject* attacker, WeaponObject* weapon, TangibleObject* tano, const CreatureAttackData& data) {

	int damage = 0;

	Locker clocker(tano, attacker);

	if (tano->isCreatureObject()) {
		CreatureObject* defenderObject = cast<CreatureObject*>( tano);

		if (defenderObject->getWeapon() != NULL)
			damage = doTargetCombatAction(attacker, weapon, defenderObject, data);
	} else {
		// TODO: implement, tano->tano damage
	}

	return damage;
}

int CombatManager::doTargetCombatAction(TangibleObject* attacker, WeaponObject* weapon, CreatureObject* defenderObject, const CreatureAttackData& data) {
	if(defenderObject == NULL || !defenderObject->isAttackableBy(attacker))
		return 0;

	if (defenderObject->isEntertaining())
		defenderObject->stopEntertaining();

	attacker->addDefender(defenderObject);
	defenderObject->addDefender(attacker);

	float damageMultiplier = data.getDamageMultiplier();

	// need to calculate damage here to get proper client spam
	int damage = 0;
	Vector<int> foodMitigation; //used to save the food mitigation value for combat spam sent later.

	if (damageMultiplier != 0)
		damage = calculateDamage(attacker, weapon, defenderObject, data, foodMitigation) * damageMultiplier;

	damageMultiplier = 1.0f;
	int hitVal = getHitChance(attacker, defenderObject, weapon, damage, data.getAccuracyBonus());

	//Send Attack Combat Spam
	data.getCommand()->sendAttackCombatSpam(attacker, defenderObject, hitVal, damage, data);

	CombatAction* combatAction = NULL;

	uint32 animationCRC = data.getAnimationCRC();
	combatAction = new CombatAction(attacker, defenderObject, animationCRC, hitVal, CombatManager::DEFAULTTRAIL);
	attacker->broadcastMessage(combatAction,true);

	switch (hitVal) {
	case MISS:
		doMiss(attacker, weapon, defenderObject, damage);
		return 0;
		break;
	case HIT:
		break;
	case BLOCK:
		doBlock(attacker, weapon, defenderObject, damage);
		damageMultiplier = 0.5f;
		break;
	case DODGE:
		doDodge(attacker, weapon, defenderObject, damage);
		damageMultiplier = 0.0f;
		break;
	case COUNTER:
		doCounterAttack(attacker, weapon, defenderObject, damage);
		//defenderObject->enqueueCommand(String("counterattack").hashCode(), 0, attacker->getObjectID(), "");
		damageMultiplier = 0.0f;
		break;
	case RICOCHET:
		doLightsaberBlock(attacker, weapon, defenderObject, damage);
		damageMultiplier = 0.0f;
		break;
	default:
		break;
	}

	int poolsToDamage = calculatePoolsToDamage(data.getPoolsToDamage());

	if (poolsToDamage == 0)
		return 0;

	if (defenderObject->hasAttackDelay())
		defenderObject->removeAttackDelay();

	if (damageMultiplier != 0 && damage != 0)
		damage = applyDamage(attacker, weapon, defenderObject, damage, damageMultiplier, poolsToDamage, data);

	//Send defensive buff combat spam last.
	if (!foodMitigation.isEmpty())
		sendMitigationCombatSpam(defenderObject, weapon, foodMitigation.get(0), FOOD);

	return damage;
}

void CombatManager::applyDots(CreatureObject* attacker, CreatureObject* defender, const CreatureAttackData& data, int appliedDamage) {
	VectorMap<uint64, DotEffect>* dotEffects = data.getDotEffects();

	if (defender->isPlayerCreature() && defender->getPvpStatusBitmask() == CreatureFlag::NONE)
		return;

	for (int i = 0; i < dotEffects->size(); i++) {
		DotEffect effect = dotEffects->get(i);

		if (defender->hasDotImmunity(effect.getDotType()) || effect.getDotDuration() == 0 || System::random(100) > effect.getDotChance())
			continue;

		Vector<String> defenseMods = effect.getDefenderStateDefenseModifers();
		int resist = 0;

		for (int j = 0; j < defenseMods.size(); j++)
			resist += defender->getSkillMod(defenseMods.get(j));

		//info("entering addDotState", true);
		defender->addDotState(attacker, effect.getDotType(), data.getCommand()->getNameCRC(), effect.isDotDamageofHit() ? appliedDamage * effect.getPrimaryPercent() / 100.0f : effect.getDotStrength(), effect.getDotPool(), effect.getDotDuration(), effect.getDotPotency(), resist,
				effect.isDotDamageofHit() ? appliedDamage * effect.getSecondaryPercent() / 100.0f : effect.getDotStrength());
	}
}

void CombatManager::applyWeaponDots(CreatureObject* attacker, CreatureObject* defender, WeaponObject* weapon) {
	// Get attacker's weapon they have.
	ManagedReference<WeaponObject*> attackerWeapon = cast<WeaponObject*>(weapon);

	if (defender->isPlayerCreature() && defender->getPvpStatusBitmask() == CreatureFlag::NONE)
		return;

	if (!attackerWeapon->isCertifiedFor(attacker))
		return;

	for (int i = 0; i < attackerWeapon->getNumberOfDots(); i++) {
		if (attackerWeapon->getDotUses(i) <= 0)
			continue;

		int resist = 0;
		int power = 0;

		switch (attackerWeapon->getDotType(i)) {
		case 1: //POISON
			if (defender->hasDotImmunity(CreatureState::POISONED))
				break;
			resist = defender->getSkillMod("resistance_poison") + defender->getSkillMod("poison_disease_resist");
			power = defender->addDotState(attacker, CreatureState::POISONED, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i));

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		case 2: //DISEASE
			if (defender->hasDotImmunity(CreatureState::DISEASED))
				break;
			resist = defender->getSkillMod("resistance_disease") + defender->getSkillMod("poison_disease_resist");
			power = defender->addDotState(attacker, CreatureState::DISEASED, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i));

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		case 3: //FIRE
			if (defender->hasDotImmunity(CreatureState::ONFIRE))
				break;
			resist = defender->getSkillMod("resistance_fire") + defender->getSkillMod("fire_resist");
			power = defender->addDotState(attacker, CreatureState::ONFIRE, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i) * .5f);

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		case 4: //BLEED
			if (defender->hasDotImmunity(CreatureState::BLEEDING))
				break;
			resist = defender->getSkillMod("resistance_bleeding") + defender->getSkillMod("combat_bleeding_defense") + defender->getSkillMod("bleed_resist");
			power = defender->addDotState(attacker, CreatureState::BLEEDING, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i));

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		default:
			break;
		}
	}
}

float CombatManager::getWeaponRangeModifier(float currentRange, WeaponObject* weapon) {
	float minRange = 0;
	float idealRange = 2;
	float maxRange = 5;

	float smallMod = 7;
	float bigMod = 7;

	if (weapon != NULL) {
		minRange = (float) weapon->getPointBlankRange();
		idealRange = (float) weapon->getIdealRange();
		maxRange = (float) weapon->getMaxRange();

		smallMod = (float) weapon->getPointBlankAccuracy();
		bigMod = (float) weapon->getIdealAccuracy();
	}

	// this assumes that we are attacking somewhere between point blank and ideal range
	float smallRange = minRange;
	float bigRange = idealRange;

	// check that assumption and correct if it's not true
	if (currentRange > idealRange) {
		if (weapon != NULL) {
			smallMod = (float) weapon->getIdealAccuracy();
			bigMod = (float) weapon->getMaxRangeAccuracy();
		} // else retain mod values = 7

		smallRange = idealRange;
		bigRange = maxRange;
	} else if (currentRange <= minRange)
		return smallMod;

	return smallMod + ((currentRange - smallRange) / (bigRange - smallRange) * (bigMod - smallMod));
}

int CombatManager::calculatePostureModifier(CreatureObject* creature, WeaponObject* weapon) {<commit_msg>[fixed] division by 0 causing mobs outside ideal range to not miss<commit_after>/*
 * CombatManager.cpp
 *
 *  Created on: 24/02/2010
 *      Author: victor
 */

#include "CombatManager.h"
#include "CreatureAttackData.h"
#include "server/zone/objects/scene/variables/DeltaVector.h"
#include "server/zone/objects/creature/CreatureObject.h"
#include "server/zone/objects/player/PlayerObject.h"
#include "server/zone/objects/player/events/LogoutTask.h"
#include "server/zone/objects/creature/CreatureState.h"
#include "server/zone/objects/creature/commands/CombatQueueCommand.h"
#include "server/zone/objects/creature/CreatureAttribute.h"
#include "server/zone/packets/object/CombatSpam.h"
#include "server/zone/packets/object/CombatAction.h"
#include "server/zone/packets/chat/ChatSystemMessage.h"
#include "server/zone/packets/tangible/UpdatePVPStatusMessage.h"
#include "server/zone/Zone.h"
#include "server/zone/managers/collision/CollisionManager.h"
#include "server/zone/objects/creature/buffs/StateBuff.h"
#include "server/zone/managers/visibility/VisibilityManager.h"
#include "server/zone/managers/creature/CreatureManager.h"
#include "server/zone/managers/creature/LairObserver.h"
#include "server/zone/managers/reaction/ReactionManager.h"
#include "server/zone/objects/installation/components/TurretDataComponent.h"
#include "server/zone/objects/creature/AiAgent.h"

const uint32 CombatManager::defaultAttacks[9] = {
		0x99476628, 0xF5547B91, 0x3CE273EC, 0x734C00C,
		0x43C4FFD0, 0x56D7CC78, 0x4B41CAFB, 0x2257D06B,
		0x306887EB
};


bool CombatManager::startCombat(CreatureObject* attacker, TangibleObject* defender, bool lockDefender) {
	if (attacker == defender)
		return false;

	if (attacker->getZone() == NULL || defender->getZone() == NULL)
		return false;

	if (attacker->isRidingMount()) {
		ManagedReference<CreatureObject*> parent = attacker->getParent().get().castTo<CreatureObject*>();

		if (parent == NULL || !parent->isMount())
			return false;

		if (attacker->hasBuff(String("gallop").hashCode()))
			return false;
	}

	if (attacker->hasRidingCreature())
		return false;

	if (!defender->isAttackableBy(attacker))
		return false;

	if (defender->isCreatureObject() && (cast<CreatureObject*>(defender))->isIncapacitated())
		return false;

	if (attacker->isPlayerCreature() && attacker->getPlayerObject()->isAFK())
		return false;

	attacker->clearState(CreatureState::PEACE);

	Locker clocker(defender, attacker);

	attacker->setDefender(defender);
	defender->addDefender(attacker);

	return true;
}

bool CombatManager::attemptPeace(CreatureObject* attacker) {
	DeltaVector<ManagedReference<SceneObject*> >* defenderList = attacker->getDefenderList();

	for (int i = defenderList->size() - 1; i >= 0; --i) {
		ManagedReference<SceneObject*> object = defenderList->get(i);

		TangibleObject* defender = cast<TangibleObject*>( object.get());

		if (defender == NULL)
			continue;

		try {
			Locker clocker(defender, attacker);

			if (defender->hasDefender(attacker)) {

				if (defender->isCreatureObject()) {
					CreatureObject* creature = cast<CreatureObject*>(defender);

					if (creature->getMainDefender() != attacker || creature->hasState(CreatureState::PEACE) || creature->isDead() || attacker->isDead() || !creature->isInRange(attacker, 128.f)) {
						attacker->removeDefender(defender);
						defender->removeDefender(attacker);
					}
				} else {
					attacker->removeDefender(defender);
					defender->removeDefender(attacker);
				}
			} else {
				attacker->removeDefender(defender);
			}

			clocker.release();

		} catch (Exception& e) {
			error(e.getMessage());
			e.printStackTrace();
		}
	}

	if (defenderList->size() != 0) {
		//info("defenderList not empty, trying to set Peace State");

		attacker->setState(CreatureState::PEACE);

		return false;
	} else {
		attacker->clearCombatState(false);

		// clearCombatState() (rightfully) does not automatically set peace, so set it
		attacker->setState(CreatureState::PEACE);

		return true;
	}
}

void CombatManager::forcePeace(CreatureObject* attacker) {
	DeltaVector<ManagedReference<SceneObject*> >* defenderList = attacker->getDefenderList();

	for (int i = 0; i < defenderList->size(); ++i) {
		ManagedReference<SceneObject*> object = defenderList->get(i);

		if (!object->isTangibleObject())
			continue;

		TangibleObject* defender = cast<TangibleObject*>( object.get());

		Locker clocker(defender, attacker);

		if (defender->hasDefender(attacker)) {
			attacker->removeDefender(defender);
			defender->removeDefender(attacker);
		} else {
			attacker->removeDefender(defender);
		}

		--i;

		clocker.release();
	}

	attacker->clearCombatState(false);
	attacker->setState(CreatureState::PEACE);
}



int CombatManager::doCombatAction(CreatureObject* attacker, WeaponObject* weapon, TangibleObject* defenderObject, CombatQueueCommand* command) {
	return doCombatAction(attacker, weapon, defenderObject, CreatureAttackData("",command));
}

int CombatManager::doCombatAction(CreatureObject* attacker, WeaponObject* weapon, TangibleObject* defenderObject, const CreatureAttackData& data) {
	//info("entering doCombat action with data ", true);

	if (!startCombat(attacker, defenderObject))
		return -1;

	//info("past start combat", true);

	if (attacker->hasAttackDelay() || !attacker->checkKnockdownRecovery())
		return -3;

	//info("past delay", true);

	if (!applySpecialAttackCost(attacker, weapon, data))
		return -2;

	//info("past special attack cost", true);

	int damage = 0;

	if (data.getCommand()->isAreaAction() || data.getCommand()->isConeAction())
		damage = doAreaCombatAction(attacker, weapon, defenderObject, data);
	else
		damage = doTargetCombatAction(attacker, weapon, defenderObject, data);

	if (damage > 0) {
		attacker->updateLastSuccessfulCombatAction();
	}

	return damage;
}

int CombatManager::doCombatAction(TangibleObject* attacker, WeaponObject* weapon, TangibleObject* defender, CombatQueueCommand* command){

	if(weapon != NULL){
		if(!command->isAreaAction()){
			return doTargetCombatAction(attacker, weapon, defender, CreatureAttackData("",command));
		} else {
			return doAreaCombatAction(attacker, weapon, defender, CreatureAttackData("",command));
		}
	}

	return 0;
}

int CombatManager::doTargetCombatAction(CreatureObject* attacker, WeaponObject* weapon, TangibleObject* tano, const CreatureAttackData& data) {
	int damage = 0;

	Locker clocker(tano, attacker);

	if (!tano->isAttackableBy(attacker))
		return 0;

	attacker->addDefender(tano);
	tano->addDefender(attacker);

	if (tano->isCreatureObject()) {
		CreatureObject* defender = cast<CreatureObject*>( tano);

		if (defender->getWeapon() == NULL)
			return 0;

		damage = doTargetCombatAction(attacker, weapon, defender, data);
	} else {
		int poolsToDamage = calculatePoolsToDamage(data.getPoolsToDamage());

		damage = applyDamage(attacker, weapon, tano, poolsToDamage, data);

		broadcastCombatAction(attacker, tano, weapon, data, 0x01);

		data.getCommand()->sendAttackCombatSpam(attacker, tano, HIT, damage, data);
	}

	tano->notifyObservers(ObserverEventType::DAMAGERECEIVED, attacker, damage);

	if (damage > 0 && tano->isAiAgent()) {
		AiAgent* aiAgent = cast<AiAgent*>(tano);
		aiAgent->sendReactionChat(ReactionManager::HIT);
	}

	if (damage > 0 && attacker->isAiAgent()) {
		AiAgent* aiAgent = cast<AiAgent*>(attacker);
		aiAgent->sendReactionChat(ReactionManager::HITTARGET);
	}

	return damage;
}

int CombatManager::doTargetCombatAction(CreatureObject* attacker, WeaponObject* weapon, CreatureObject* defender, const CreatureAttackData& data) {
	if (defender->isEntertaining())
		defender->stopEntertaining();

	int hitVal = 0;
	float damageMultiplier = data.getDamageMultiplier();

	// need to calculate damage here to get proper client spam
	int damage = 0;
	Vector<int> foodMitigation; //used to save the food mitigation value for combat spam sent later.

	if (damageMultiplier != 0)
		damage = calculateDamage(attacker, weapon, defender, data, foodMitigation) * damageMultiplier;

	damageMultiplier = 1.0f;
	hitVal = getHitChance(attacker, defender, weapon, damage, data.getAccuracyBonus() + attacker->getSkillMod(data.getCommand()->getAccuracySkillMod()));

	int poolsToDamage = calculatePoolsToDamage(data.getPoolsToDamage());
	// TODO: animations are probably determined by which pools are damaged (high, mid, low, combos, etc)

	broadcastCombatAction(attacker, defender, weapon, data, hitVal);

	//Send Attack Combat Spam. For state-only attacks, this is sent in applyStates().
	if (!data.isStateOnlyAttack())
		data.getCommand()->sendAttackCombatSpam(attacker, defender, hitVal, damage, data);

	switch (hitVal) {
	case MISS:
		doMiss(attacker, weapon, defender, damage);
		if (data.isStateOnlyAttack()) //Send combat spam for failed state attack.
			data.getCommand()->sendAttackCombatSpam(attacker, defender, hitVal, damage, data);
		return 0;
		break;
	case HIT:
		break;
	case BLOCK:
		doBlock(attacker, weapon, defender, damage);
		damageMultiplier = 0.5f;
		break;
	case DODGE:
		doDodge(attacker, weapon, defender, damage);
		damageMultiplier = 0.0f;
		break;
	case COUNTER:
		doCounterAttack(attacker, weapon, defender, damage);
		//defender->enqueueCommand(String("counterattack").hashCode(), 0, attacker->getObjectID(), "");
		damageMultiplier = 0.0f;
		break;
	case RICOCHET:
		doLightsaberBlock(attacker, weapon, defender, damage);
		damageMultiplier = 0.0f;
		break;
	default:
		break;
	}

	// Apply states first
	applyStates(attacker, defender, data);

	// Return if it's a state only attack (intimidate, warcry, wookiee roar) so they don't apply dots or break combat delays
	if (poolsToDamage == 0)
		return 0;

	if (defender->hasAttackDelay())
		defender->removeAttackDelay();

	if (damageMultiplier != 0 && damage != 0) {
		// Apply dots before damage so that target's armor doesn't reduce
		applyDots(attacker, defender, data, damage);
		applyWeaponDots(attacker, defender, weapon);

		damage = applyDamage(attacker, weapon, defender, damage, damageMultiplier, poolsToDamage, data);
	}

	//Send defensive buff combat spam last.
	if (!foodMitigation.isEmpty())
		sendMitigationCombatSpam(defender, weapon, foodMitigation.get(0), FOOD);

	return damage;
}

int CombatManager::doTargetCombatAction(TangibleObject* attacker, WeaponObject* weapon, TangibleObject* tano, const CreatureAttackData& data) {

	int damage = 0;

	Locker clocker(tano, attacker);

	if (tano->isCreatureObject()) {
		CreatureObject* defenderObject = cast<CreatureObject*>( tano);

		if (defenderObject->getWeapon() != NULL)
			damage = doTargetCombatAction(attacker, weapon, defenderObject, data);
	} else {
		// TODO: implement, tano->tano damage
	}

	return damage;
}

int CombatManager::doTargetCombatAction(TangibleObject* attacker, WeaponObject* weapon, CreatureObject* defenderObject, const CreatureAttackData& data) {
	if(defenderObject == NULL || !defenderObject->isAttackableBy(attacker))
		return 0;

	if (defenderObject->isEntertaining())
		defenderObject->stopEntertaining();

	attacker->addDefender(defenderObject);
	defenderObject->addDefender(attacker);

	float damageMultiplier = data.getDamageMultiplier();

	// need to calculate damage here to get proper client spam
	int damage = 0;
	Vector<int> foodMitigation; //used to save the food mitigation value for combat spam sent later.

	if (damageMultiplier != 0)
		damage = calculateDamage(attacker, weapon, defenderObject, data, foodMitigation) * damageMultiplier;

	damageMultiplier = 1.0f;
	int hitVal = getHitChance(attacker, defenderObject, weapon, damage, data.getAccuracyBonus());

	//Send Attack Combat Spam
	data.getCommand()->sendAttackCombatSpam(attacker, defenderObject, hitVal, damage, data);

	CombatAction* combatAction = NULL;

	uint32 animationCRC = data.getAnimationCRC();
	combatAction = new CombatAction(attacker, defenderObject, animationCRC, hitVal, CombatManager::DEFAULTTRAIL);
	attacker->broadcastMessage(combatAction,true);

	switch (hitVal) {
	case MISS:
		doMiss(attacker, weapon, defenderObject, damage);
		return 0;
		break;
	case HIT:
		break;
	case BLOCK:
		doBlock(attacker, weapon, defenderObject, damage);
		damageMultiplier = 0.5f;
		break;
	case DODGE:
		doDodge(attacker, weapon, defenderObject, damage);
		damageMultiplier = 0.0f;
		break;
	case COUNTER:
		doCounterAttack(attacker, weapon, defenderObject, damage);
		//defenderObject->enqueueCommand(String("counterattack").hashCode(), 0, attacker->getObjectID(), "");
		damageMultiplier = 0.0f;
		break;
	case RICOCHET:
		doLightsaberBlock(attacker, weapon, defenderObject, damage);
		damageMultiplier = 0.0f;
		break;
	default:
		break;
	}

	int poolsToDamage = calculatePoolsToDamage(data.getPoolsToDamage());

	if (poolsToDamage == 0)
		return 0;

	if (defenderObject->hasAttackDelay())
		defenderObject->removeAttackDelay();

	if (damageMultiplier != 0 && damage != 0)
		damage = applyDamage(attacker, weapon, defenderObject, damage, damageMultiplier, poolsToDamage, data);

	//Send defensive buff combat spam last.
	if (!foodMitigation.isEmpty())
		sendMitigationCombatSpam(defenderObject, weapon, foodMitigation.get(0), FOOD);

	return damage;
}

void CombatManager::applyDots(CreatureObject* attacker, CreatureObject* defender, const CreatureAttackData& data, int appliedDamage) {
	VectorMap<uint64, DotEffect>* dotEffects = data.getDotEffects();

	if (defender->isPlayerCreature() && defender->getPvpStatusBitmask() == CreatureFlag::NONE)
		return;

	for (int i = 0; i < dotEffects->size(); i++) {
		DotEffect effect = dotEffects->get(i);

		if (defender->hasDotImmunity(effect.getDotType()) || effect.getDotDuration() == 0 || System::random(100) > effect.getDotChance())
			continue;

		Vector<String> defenseMods = effect.getDefenderStateDefenseModifers();
		int resist = 0;

		for (int j = 0; j < defenseMods.size(); j++)
			resist += defender->getSkillMod(defenseMods.get(j));

		//info("entering addDotState", true);
		defender->addDotState(attacker, effect.getDotType(), data.getCommand()->getNameCRC(), effect.isDotDamageofHit() ? appliedDamage * effect.getPrimaryPercent() / 100.0f : effect.getDotStrength(), effect.getDotPool(), effect.getDotDuration(), effect.getDotPotency(), resist,
				effect.isDotDamageofHit() ? appliedDamage * effect.getSecondaryPercent() / 100.0f : effect.getDotStrength());
	}
}

void CombatManager::applyWeaponDots(CreatureObject* attacker, CreatureObject* defender, WeaponObject* weapon) {
	// Get attacker's weapon they have.
	ManagedReference<WeaponObject*> attackerWeapon = cast<WeaponObject*>(weapon);

	if (defender->isPlayerCreature() && defender->getPvpStatusBitmask() == CreatureFlag::NONE)
		return;

	if (!attackerWeapon->isCertifiedFor(attacker))
		return;

	for (int i = 0; i < attackerWeapon->getNumberOfDots(); i++) {
		if (attackerWeapon->getDotUses(i) <= 0)
			continue;

		int resist = 0;
		int power = 0;

		switch (attackerWeapon->getDotType(i)) {
		case 1: //POISON
			if (defender->hasDotImmunity(CreatureState::POISONED))
				break;
			resist = defender->getSkillMod("resistance_poison") + defender->getSkillMod("poison_disease_resist");
			power = defender->addDotState(attacker, CreatureState::POISONED, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i));

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		case 2: //DISEASE
			if (defender->hasDotImmunity(CreatureState::DISEASED))
				break;
			resist = defender->getSkillMod("resistance_disease") + defender->getSkillMod("poison_disease_resist");
			power = defender->addDotState(attacker, CreatureState::DISEASED, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i));

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		case 3: //FIRE
			if (defender->hasDotImmunity(CreatureState::ONFIRE))
				break;
			resist = defender->getSkillMod("resistance_fire") + defender->getSkillMod("fire_resist");
			power = defender->addDotState(attacker, CreatureState::ONFIRE, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i) * .5f);

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		case 4: //BLEED
			if (defender->hasDotImmunity(CreatureState::BLEEDING))
				break;
			resist = defender->getSkillMod("resistance_bleeding") + defender->getSkillMod("combat_bleeding_defense") + defender->getSkillMod("bleed_resist");
			power = defender->addDotState(attacker, CreatureState::BLEEDING, attackerWeapon->getObjectID(), attackerWeapon->getDotStrength(i), attackerWeapon->getDotAttribute(i), attackerWeapon->getDotDuration(i), attackerWeapon->getDotPotency(i), resist, attackerWeapon->getDotStrength(i));

			if (power > 0) { // Unresisted, reduce use count.
				if (attackerWeapon->getDotUses(i) > 0) {
					attackerWeapon->setDotUses(attackerWeapon->getDotUses(i) - 1, i);
				}
			}
			break;
		default:
			break;
		}
	}
}

float CombatManager::getWeaponRangeModifier(float currentRange, WeaponObject* weapon) {
	float minRange = 0;
	float idealRange = 2;
	float maxRange = 5;

	float smallMod = 7;
	float bigMod = 7;

	if (weapon != NULL) {
		minRange = (float) weapon->getPointBlankRange();
		idealRange = (float) weapon->getIdealRange();
		maxRange = (float) weapon->getMaxRange();

		smallMod = (float) weapon->getPointBlankAccuracy();
		bigMod = (float) weapon->getIdealAccuracy();
	}

	// this assumes that we are attacking somewhere between point blank and ideal range
	float smallRange = minRange;
	float bigRange = idealRange;

	// check that assumption and correct if it's not true
	if (currentRange > idealRange) {
		if (weapon != NULL) {
			smallMod = (float) weapon->getIdealAccuracy();
			bigMod = (float) weapon->getMaxRangeAccuracy();
		} // else retain mod values = 7

		smallRange = idealRange;
		bigRange = maxRange;
	} else if (currentRange <= minRange)
		return smallMod;

	return smallMod + ((currentRange - smallRange) / (bigRange == smallRange ? 1 : (bigRange - smallRange)) * (bigMod - smallMod));
}

int CombatManager::calculatePostureModifier(CreatureObject* creature, WeaponObject* weapon) {
{'fraction_non_alphanumeric': 0.13391573003782029, 'fraction_numerical': 0.0093751664624727, 'mean_word_length': 5.543569187870338, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 395, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '675989', 'n_tokens_mistral': 13870, 'n_tokens_neox': 12254, 'n_words': 2536}