Browse Source

Wards support is now fully extended to allow divine aura / stoneskin / intercept as possible solutions

Addresses issue #115 - minus fervor needs its own work implemented.

AddWard(dmg,keepward,wardtype,damagetype,dmgabsorptionpct,damageabsorptionmaxhealthpct,redirectdmgpct,maxhitcount)
Image 3 years ago
parent
commit
07c14f1d92

+ 12 - 1
EQ2/source/WorldServer/Combat.cpp

@@ -908,6 +908,8 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
 
 	LogWrite(MISC__TODO, 3, "TODO", "Take players armor into account\nfile: %s, func: %s, line: %i)", __FILE__, __FUNCTION__, __LINE__);
 
+	bool useWards = false;
+
 	if(damage <= 0){
 		hit_result = DAMAGE_PACKET_RESULT_NO_DAMAGE;
 		damage = 0;
@@ -915,7 +917,12 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
 	else{
 		hit_result = DAMAGE_PACKET_RESULT_SUCCESSFUL;
 		GetZone()->CallSpawnScript(victim, SPAWN_SCRIPT_HEALTHCHANGED, this);
-		damage = victim->CheckWards(damage, damage_type);
+		int32 prevDmg = damage;
+		damage = victim->CheckWards(this, damage, damage_type);
+
+		if (damage < prevDmg)
+			useWards = true;
+
 		victim->TakeDamage(damage);
 		victim->CheckProcs(PROC_TYPE_DAMAGED, this);
 
@@ -953,6 +960,10 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
 		if (victim->IsEntity())
 			((Entity*)victim)->CheckInterruptSpell(this);
 	}
+	else if (useWards)
+	{
+		GetZone()->SendDamagePacket(this, victim, DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, DAMAGE_PACKET_RESULT_NO_DAMAGE, damage_type, 0, spell_name);
+	}
 
 	if (victim->GetHP() <= 0)
 		KillSpawn(victim, damage_type, blow_type);

+ 45 - 3
EQ2/source/WorldServer/Entity.cpp

@@ -1263,7 +1263,7 @@ void Entity::RemoveWard(int32 spellID) {
 	}
 }
 
-int32 Entity::CheckWards(int32 damage, int8 damage_type) {
+int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) {
 	map<int32, WardInfo*>::iterator itr;
 	WardInfo* ward = 0;
 	LuaSpell* spell = 0;
@@ -1272,7 +1272,7 @@ int32 Entity::CheckWards(int32 damage, int8 damage_type) {
 		// Get the ward with the lowest base damage
 		for (itr = m_wardList.begin(); itr != m_wardList.end(); itr++) {
 			if (!ward || itr->second->BaseDamage < ward->BaseDamage) {
-				if (itr->second->DamageLeft > 0 &&
+				if ((itr->second->AbsorbAllDamage || itr->second->DamageLeft > 0) &&
 					(itr->second->WardType == WARD_TYPE_ALL ||
 					(itr->second->WardType == WARD_TYPE_PHYSICAL && damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_SLASH && damage_type <= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE) ||
 					(itr->second->WardType == WARD_TYPE_MAGICAL && ((itr->second->DamageType == 0 && damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE) || (damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE && itr->second->DamageType == damage_type)))))
@@ -1285,6 +1285,12 @@ int32 Entity::CheckWards(int32 damage, int8 damage_type) {
 
 		spell = ward->Spell;
 
+		// damage to redirect at the source (like intercept)
+		int32 redirectDamage = 0;
+		if (ward->RedirectDamagePercent)
+			redirectDamage = (int32)(double)damage * ((double)ward->RedirectDamagePercent / 100.0);
+
+		// percentage the spell absorbs of all possible damage
 		int32 damageToAbsorb = 0;
 		if (ward->DamageAbsorptionPercentage > 0)
 			damageToAbsorb = (int32)(double)damage * ((double)ward->DamageAbsorptionPercentage/100.0);
@@ -1302,9 +1308,17 @@ int32 Entity::CheckWards(int32 damage, int8 damage_type) {
 
 		int32 baseDamageRemaining = damage - damageToAbsorb;
 
-		if (damageToAbsorb >= ward->DamageLeft) {
+		bool hasSpellBeenRemoved = false;
+		if (ward->AbsorbAllDamage)
+		{
+			ward->LastAbsorbedDamage = ward->DamageLeft;
+			GetZone()->SendHealPacket(ward->Spell->caster, this, HEAL_PACKET_TYPE_ABSORB, damage, spell->spell->GetName());
+			damage = 0;
+		}
+		else if (damageToAbsorb >= ward->DamageLeft) {
 			// Damage is greater than or equal to the amount left on the ward
 
+			ward->LastAbsorbedDamage = ward->DamageLeft;
 			// remove what damage we can absorb 
 			damageToAbsorb -= ward->DamageLeft;
 
@@ -1315,13 +1329,16 @@ int32 Entity::CheckWards(int32 damage, int8 damage_type) {
 			spell->damage_remaining = 0;
 			GetZone()->SendHealPacket(spell->caster, this, HEAL_PACKET_TYPE_ABSORB, ward->DamageLeft, spell->spell->GetName());
 			if (!ward->keepWard) {
+				hasSpellBeenRemoved = true;
 				RemoveWard(spell->spell->GetSpellID());
 				GetZone()->GetSpellProcess()->DeleteCasterSpell(spell);
 			}
 		}
 		else {
+			ward->LastAbsorbedDamage = damageToAbsorb;
 			// Damage is less then the amount left on the ward
 			ward->DamageLeft -= damageToAbsorb;
+
 			spell->damage_remaining = ward->DamageLeft;
 			if (spell->caster->IsPlayer())
 				ClientPacketFunctions::SendMaintainedExamineUpdate(GetZone()->GetClientBySpawn(spell->caster), spell->slot_pos, ward->DamageLeft, 1);
@@ -1331,6 +1348,31 @@ int32 Entity::CheckWards(int32 damage, int8 damage_type) {
 			damage = baseDamageRemaining;
 		}
 
+		if (redirectDamage)
+		{
+			ward->LastRedirectDamage = redirectDamage;
+			if (attacker && spell->caster)
+				attacker->DamageSpawn(spell->caster, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, redirectDamage, redirectDamage, 0);
+		}
+
+		bool shouldRemoveSpell = false;
+		ward->HitCount++; // increment hit count
+
+		if (ward->MaxHitCount && spell->num_triggers)
+		{
+			spell->num_triggers--;
+			ClientPacketFunctions::SendMaintainedExamineUpdate(spell->caster->GetZone()->GetClientBySpawn(spell->caster), spell->slot_pos, spell->num_triggers, 0);
+		}
+		
+		if (ward->HitCount >= ward->MaxHitCount) // there isn't a max hit requirement with the hit count, so just go based on hit count
+			shouldRemoveSpell = true;
+
+		if (shouldRemoveSpell && !hasSpellBeenRemoved)
+		{
+			RemoveWard(spell->spell->GetSpellID());
+			GetZone()->GetSpellProcess()->DeleteCasterSpell(spell);
+		}
+
 		// Reset ward pointer
 		ward = 0;
 	}

+ 12 - 3
EQ2/source/WorldServer/Entity.h

@@ -255,8 +255,17 @@ struct WardInfo {
 	int8		WardType;
 	int8		DamageType;
 	bool		keepWard;
-	int8		DamageAbsorptionPercentage;
-	int8		DamageAbsorptionMaxHealthPercent;
+	int32		DamageAbsorptionPercentage;
+	int32		DamageAbsorptionMaxHealthPercent;
+	int32		RedirectDamagePercent;
+	
+	int32		LastRedirectDamage;
+	int32		LastAbsorbedDamage;
+
+	int32		HitCount;
+	int32		MaxHitCount;
+
+	bool		AbsorbAllDamage; // damage is always absorbed, usually spells based on hits, when we pass damage in AddWard as 0 this will be set to true
 };
 
 #define WARD_TYPE_ALL 0
@@ -720,7 +729,7 @@ public:
 	/// <summary>Subtracts the given damage from the wards</summary>
 	/// <param name='damage'>The damage to subtract from the wards</param>
 	/// <returns>The amount of damage left after wards</returns>
-	int32 CheckWards(int32 damage, int8 damage_type);
+	int32 CheckWards(Entity* attacker, int32 damage, int8 damage_type);
 
 	map<int16, float> stats;
 

+ 27 - 2
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -4934,8 +4934,10 @@ int EQ2Emu_lua_AddWard(lua_State* state) {
 	bool keepWard = (lua_interface->GetInt8Value(state, 2) == 1);
 	int8 wardType = lua_interface->GetInt8Value(state, 3);
 	int8 damageTypes = lua_interface->GetInt8Value(state, 4);
-	int8 damageAbsorptionPercent = lua_interface->GetInt8Value(state, 5);
-	int8 damageAbsorptionMaxHealthPercent = lua_interface->GetInt8Value(state, 6);
+	int32 damageAbsorptionPercent = lua_interface->GetInt32Value(state, 5);
+	int32 damageAbsorptionMaxHealthPercent = lua_interface->GetInt32Value(state, 6);
+	int32 redirectDamagePercent = lua_interface->GetInt32Value(state, 7);
+	int32 maxHitCount = lua_interface->GetInt32Value(state, 8);
 
 	LuaSpell* spell = lua_interface->GetCurrentSpell(state);
 
@@ -4957,6 +4959,8 @@ int EQ2Emu_lua_AddWard(lua_State* state) {
 			ward->Spell = spell;
 			ward->BaseDamage = damage;
 			ward->DamageLeft = damage;
+			ward->AbsorbAllDamage = (damage == 0) ? true : false;
+
 			ward->keepWard = keepWard;
 			ward->WardType = wardType;
 			if (damageAbsorptionPercent > 100)
@@ -4969,6 +4973,17 @@ int EQ2Emu_lua_AddWard(lua_State* state) {
 
 			ward->DamageAbsorptionMaxHealthPercent = damageAbsorptionMaxHealthPercent;
 
+			ward->RedirectDamagePercent = redirectDamagePercent;
+
+			ward->LastRedirectDamage = 0;
+			ward->LastAbsorbedDamage = 0;
+			ward->HitCount = 0;
+
+			spell->num_triggers = maxHitCount;
+			spell->had_triggers = true;
+			spell->cancel_after_all_triggers = false;
+			ward->MaxHitCount = maxHitCount;
+
 			if (wardType == WARD_TYPE_MAGICAL)
 				ward->DamageType = damageTypes;
 
@@ -5069,6 +5084,16 @@ int EQ2Emu_lua_GetWardValue(lua_State* state) {
 				lua_interface->SetInt32Value(state, ward->DamageAbsorptionPercentage);
 			else if (boost::iequals(type, "dmgabsorptionmaxhealthpct"))
 				lua_interface->SetInt32Value(state, ward->DamageAbsorptionMaxHealthPercent);
+			else if (boost::iequals(type, "redirectdamagepercent"))
+				lua_interface->SetInt32Value(state, ward->RedirectDamagePercent);
+			else if (boost::iequals(type, "lastredirectdamage"))
+				lua_interface->SetInt32Value(state, ward->LastRedirectDamage);
+			else if (boost::iequals(type, "lastabsorbeddamage"))
+				lua_interface->SetInt32Value(state, ward->LastAbsorbedDamage);
+			else if (boost::iequals(type, "hitcount"))
+				lua_interface->SetInt32Value(state, ward->HitCount);
+			else if (boost::iequals(type, "maxhitcount"))
+				lua_interface->SetInt32Value(state, ward->MaxHitCount);
 			else
 				lua_interface->LogError("%s: LUA GetWardValue command argument type '%s' did not match any options", lua_interface->GetScriptName(state), type);
 			return 1;