diff --git a/EQ2/source/WorldServer/Bots/Bot.cpp b/EQ2/source/WorldServer/Bots/Bot.cpp index 9465f6c72..516fd3da2 100644 --- a/EQ2/source/WorldServer/Bots/Bot.cpp +++ b/EQ2/source/WorldServer/Bots/Bot.cpp @@ -687,9 +687,6 @@ void Bot::ChangeLevel(int16 old_level, int16 new_level) { GetInfoStruct()->set_magic_base((int16)(new_level*1.5 + 10)); GetInfoStruct()->set_divine_base((int16)(new_level*1.5 + 10)); GetInfoStruct()->set_poison_base((int16)(new_level*1.5 + 10)); - - SetHPRegen((int)(new_level*.75) + (int)(new_level / 10) + 3); - SetPowerRegen(new_level + (int)(new_level / 10) + 4); /*UpdateTimeStampFlag(LEVEL_UPDATE_FLAG); GetPlayer()->SetCharSheetChanged(true); diff --git a/EQ2/source/WorldServer/Combat.cpp b/EQ2/source/WorldServer/Combat.cpp index e845145b4..401fdbedd 100644 --- a/EQ2/source/WorldServer/Combat.cpp +++ b/EQ2/source/WorldServer/Combat.cpp @@ -582,7 +582,9 @@ bool Entity::SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string if (heal_amt > 0){ //int32 base_roll = heal_amt; //potency mod + MStats.lock(); heal_amt *= (stats[ITEM_STAT_POTENCY] / 100 + 1); + MStats.unlock(); //primary stat mod, insert forula here when done //heal_amt += base_roll * (GetPrimaryStat() @@ -682,12 +684,14 @@ bool Entity::SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string if (target->IsEntity()) { int32 hate_amt = heal_amt / 2; set<int32>::iterator itr; + ((Entity*)target)->MHatedBy.lock(); for (itr = ((Entity*)target)->HatedBy.begin(); itr != ((Entity*)target)->HatedBy.end(); itr++) { Spawn* spawn = GetZone()->GetSpawnByID(*itr); if (spawn && spawn->IsEntity()) { ((Entity*)spawn)->AddHate(this, hate_amt); } } + ((Entity*)target)->MHatedBy.unlock(); } return true; @@ -702,14 +706,28 @@ int8 Entity::DetermineHit(Spawn* victim, int8 damage_type, float ToHitBonus, boo return DAMAGE_PACKET_RESULT_INVULNERABLE; } - if(!victim->IsEntity() || (!spell && BehindTarget(victim))) { + bool behind = false; + if(!victim->IsEntity() || (!spell && victim->GetAdventureClass() != BRAWLER && (behind = BehindTarget(victim)))) { return DAMAGE_PACKET_RESULT_SUCCESSFUL; } float bonus = ToHitBonus; Skill* skill = GetSkillByWeaponType(damage_type, true); + + float skillAddedByWeapon = 0.0f; + if(skill) + { + int16 skillID = master_item_list.GetItemStatIDByName(skill->name.data); + if(skillID != 0xFFFFFFFF) + { + MStats.lock(); + skillAddedByWeapon = stats[skillID]; + MStats.unlock(); + } + } + if (skill) - bonus += skill->current_val / 25; + bonus += (skill->current_val+skillAddedByWeapon) / 25; if (victim->IsEntity()) bonus -= ((Entity*)victim)->GetDamageTypeResistPercentage(damage_type); @@ -725,38 +743,76 @@ int8 Entity::DetermineHit(Spawn* victim, int8 damage_type, float ToHitBonus, boo if(skill) roll_chance -= skill->current_val / 25; - skill = entity_victim->GetSkillByName("Defense", true); - if (skill) - chance -= skill->current_val / 25; - - if(rand()%roll_chance >= (chance - entity_victim->GetAgi()/50)){ - entity_victim->CheckProcs(PROC_TYPE_EVADE, this); - return DAMAGE_PACKET_RESULT_DODGE;//successfully dodged - } - if(rand() % roll_chance >= chance) - return DAMAGE_PACKET_RESULT_MISS; //successfully avoided - if(entity_victim->IsImmune(IMMUNITY_TYPE_RIPOSTE)) return DAMAGE_PACKET_RESULT_RIPOSTE; + // Avoidance Instructions: https://forums.daybreakgames.com/eq2/index.php?threads/avoidance-faq.482979/ + + /*Parry: reads as parry in the avoidance tooltip, increased by items with +parry on them + Caps at 70%. For plate tanks, works in the front quadrant only, for brawlers this is 360 degrees. + A small % of parries will be ripostes, which not only avoid the attack but also damage your attacker + */ + skill = entity_victim->GetSkillByName("Parry", true); if(skill){ - if(rand()%roll_chance >= (chance - 5 - skill->current_val/25)){ //successful parry - if(rand()%100 <= 20) { - entity_victim->CheckProcs(PROC_TYPE_RIPOSTE, this); - return DAMAGE_PACKET_RESULT_RIPOSTE; + float parryChance = entity_victim->GetInfoStruct()->get_parry(); + float chanceValue = (100.0f - parryChance); + + if(rand()%roll_chance >= chanceValue){ //successful parry + /* You may only riposte things in the front quadrant. + Riposte is based off of parry: a certain % of parries turn into ripostes. + */ + if(!behind && victim->InFrontSpawn((Spawn*)this, victim->GetX(), victim->GetZ())) { // if the attacker is not behind the victim, and the victim is facing the attacker (in front of spawn) then we can riposte + float riposteChanceValue = parryChance / 7.0f; // Riposte is based off of parry: a certain % of parries turn into ripostes. Unknown what the value is divided by, 7 to make it 10% even. + if(rand()%100 <= riposteChanceValue) { + entity_victim->CheckProcs(PROC_TYPE_RIPOSTE, this); + return DAMAGE_PACKET_RESULT_RIPOSTE; + } } entity_victim->CheckProcs(PROC_TYPE_PARRY, this); return DAMAGE_PACKET_RESULT_PARRY; } } - skill = entity_victim->GetSkillByName("Deflection", true); - if(skill){ - if(rand()%100 >= (chance - skill->current_val/25)) { //successfully deflected - return DAMAGE_PACKET_RESULT_DEFLECT; + skill = nullptr; + + + float blockChance = 0.0f; + if(victim->GetAdventureClass() == BRAWLER) + skill = entity_victim->GetSkillByName("Deflection", true); + + blockChance = entity_victim->GetInfoStruct()->get_block(); + + if(blockChance > 0.0f) + { + blockChance += (blockChance*(entity_victim->GetInfoStruct()->get_block_chance()/100.0f)); + float chanceValue = (100.0f - blockChance); + /* Non-brawlers may only block things in the front quadrant. + Riposte is based off of parry: a certain % of parries turn into ripostes. + */ + float rnd = rand()%roll_chance; + if(rnd >= chanceValue){ //successful block + if(victim->GetAdventureClass() == BRAWLER || (!behind && victim->InFrontSpawn((Spawn*)this, victim->GetX(), victim->GetZ()))) { // if the attacker is not behind the victim, and the victim is facing the attacker (in front of spawn) then we can block + entity_victim->CheckProcs(PROC_TYPE_BLOCK, this); + return (victim->GetAdventureClass() == BRAWLER) ? DAMAGE_PACKET_RESULT_DEFLECT : DAMAGE_PACKET_RESULT_BLOCK; + } } } + + skill = entity_victim->GetSkillByName("Defense", true); + + float dodgeChance = entity_victim->GetInfoStruct()->get_avoidance_base(); + if(dodgeChance > 0.0f) + { + float chanceValue = (100.0f - dodgeChance); + float rnd = rand()%roll_chance; + if(rnd >= chanceValue){ //successful dodge + entity_victim->CheckProcs(PROC_TYPE_EVADE, this); + return DAMAGE_PACKET_RESULT_DODGE;//successfully dodged + } + } + if(rand() % roll_chance >= chance) + return DAMAGE_PACKET_RESULT_MISS; //successfully avoided } else{ skill = entity_victim->GetSkillByName("Spell Avoidance", true); @@ -873,7 +929,9 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_ //Potency and ability mod is only applied to spells/CAs else { // Potency mod + MStats.lock(); damage *= ((stats[ITEM_STAT_POTENCY] / 100) + 1); + MStats.unlock(); // Ability mod can only give up to half of damage after potency int32 mod = (int32)min(info_struct.get_ability_modifier(), (float)(damage / 2)); @@ -888,7 +946,9 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_ // Crit Roll else { + victim->MStats.lock(); float chance = max((float)0, (info_struct.get_crit_chance() - victim->stats[ITEM_STAT_CRITAVOIDANCE])); + victim->MStats.unlock(); if (MakeRandomFloat(0, 100) <= chance) crit = true; } @@ -1052,8 +1112,14 @@ bool Entity::CheckInterruptSpell(Entity* attacker) { //modified to 50% and added global rule, 30% was too small at starting levels int8 percent = rule_manager.GetGlobalRule(R_Spells, NoInterruptBaseChance)->GetInt32(); Skill* skill = GetSkillByName("Focus", true); + + float focusSkillPts = 0.0f; + MStats.lock(); + focusSkillPts = stats[ITEM_STAT_FOCUS]; + MStats.unlock(); + if(skill) - percent += ((skill->current_val + 1)/6); + percent += ((skill->current_val + 1 + focusSkillPts)/6); if(MakeRandomInt(1, 100) > percent) { LogWrite(COMBAT__DEBUG, 0, "Combat", "'%s' interrupted spell for '%s': %i%%", attacker->GetName(), GetName(), percent); GetZone()->Interrupted(this, attacker, SPELL_ERROR_INTERRUPTED); diff --git a/EQ2/source/WorldServer/Commands/Commands.cpp b/EQ2/source/WorldServer/Commands/Commands.cpp index 9c62dba13..b94187f0e 100644 --- a/EQ2/source/WorldServer/Commands/Commands.cpp +++ b/EQ2/source/WorldServer/Commands/Commands.cpp @@ -318,7 +318,7 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c } case SPAWN_SET_VALUE_FACIAL_HAIR_TYPE:{ if(target->IsEntity()){ - sprintf(tmp, "%i", ((Entity*)target)->GetHairType()); + sprintf(tmp, "%i", ((Entity*)target)->GetFacialHairType()); ((Entity*)target)->SetFacialHairType(val, send_update); } break; @@ -1638,6 +1638,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_WAYPOINT: { bool success = false; + client->ClearWaypoint(); if (sep && sep->IsNumber(0) && sep->IsNumber(1) && sep->IsNumber(2)) { if (!client->ShowPathToTarget(atof(sep->arg[0]), atof(sep->arg[1]), atof(sep->arg[2]), 0)) client->Message(CHANNEL_COLOR_RED, "Invalid coordinates given"); @@ -1647,7 +1648,6 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie client->Message(CHANNEL_COLOR_RED, "Invalid coordinates given"); } else { - client->ClearWaypoint(); client->Message(CHANNEL_COLOR_YELLOW, "Usage: /waypoint x y z"); } break; @@ -3123,16 +3123,13 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie if(type == 0){ if(incombat) client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "You stop fighting."); - player->InCombat(false); - player->InCombat(false, true); - player->SetRangeAttack(false); + player->StopCombat(type); } else { if(type == 2){ player->InCombat(false); if(incombat && player->GetRangeAttack()){ - player->SetRangeAttack(false); - player->InCombat(false, true); + player->StopCombat(type); client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "You stop fighting."); } else{ @@ -3705,12 +3702,22 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } break; } - else if (ToLower(string(sep->arg[0])) == "pathto") + else if (ToLower(string(sep->arg[0])) == "behind") { + bool isBehind = client->GetPlayer()->BehindTarget(cmdTarget); + client->Message(CHANNEL_COLOR_YELLOW, "%s %s.", isBehind ? "YOU are behind" : "YOU are NOT behind", cmdTarget->GetName()); break; } - else if (ToLower(string(sep->arg[0])) == "pathfrom") + else if (ToLower(string(sep->arg[0])) == "infront") { + bool isBehind = client->GetPlayer()->InFrontSpawn(cmdTarget, client->GetPlayer()->GetX(), client->GetPlayer()->GetZ()); + client->Message(CHANNEL_COLOR_YELLOW, "%s %s.", isBehind ? "YOU are infront of" : "YOU are NOT infront of", cmdTarget->GetName()); + break; + } + else if (ToLower(string(sep->arg[0])) == "flank") + { + bool isFlanking = client->GetPlayer()->FlankingTarget(cmdTarget); + client->Message(CHANNEL_COLOR_YELLOW, "%s is %s.", isFlanking ? "YOU are flanking" : "YOU are NOT flanking", cmdTarget->GetName()); break; } } @@ -4571,6 +4578,39 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie case COMMAND_TARGETITEM : { Command_TargetItem(client, sep); break; } case COMMAND_FINDSPAWN: { Command_FindSpawn(client, sep); break; } case COMMAND_MOVECHARACTER: { Command_MoveCharacter(client, sep); break; } + case COMMAND_CRAFTITEM: { + Item* item = 0; + if (sep && sep->IsNumber(0)) { + int32 item_id = atol(sep->arg[0]); + int32 quantity = 1; + + if (sep->arg[1] && sep->IsNumber(1)) + quantity = atoi(sep->arg[1]); + item = new Item(master_item_list.GetItem(item_id)); + if (!item) { + LogWrite(TRADESKILL__ERROR, 0, "CraftItem", "Item (%u) not found.", item_id); + } + else { + item->details.count = quantity; + // use CHANNEL_COLOR_CHAT_RELATIONSHIP as that is the same value (4) as it is in a log for this message + client->Message(CHANNEL_COLOR_CHAT_RELATIONSHIP, "You created %s.", item->CreateItemLink(client->GetVersion()).c_str()); + client->AddItem(item); + //Check for crafting quest updates + int8 update_amt = 0; + if (item->stack_count > 1) + update_amt = 1; + else + update_amt = quantity; + client->GetPlayer()->CheckQuestsCraftUpdate(item, update_amt); + } + + } + + else { + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /craftitem {item_id} [quantity] "); + } + break; + } default: @@ -10380,6 +10420,10 @@ void Commands::Add_AA(Client* client, Seperator* sep) { spell_tier = client->GetPlayer()->GetSpellTier(spell_id); AltAdvanceData* data = master_aa_list.GetAltAdvancement(spell_id); // addspellbookentry here + if(!data) { + LogWrite(COMMAND__ERROR, 0, "Command", "Error in Add_AA no data for spell_id %u spell_tier %u", spell_id, spell_tier); + return; + } if (spell_tier >= data->maxRank) { LogWrite(COMMAND__ERROR, 0, "Command", "Error in Add_AA spell_tier %u >= maxRank %u", spell_tier, data->maxRank); return; diff --git a/EQ2/source/WorldServer/Commands/Commands.h b/EQ2/source/WorldServer/Commands/Commands.h index f174fe28b..9ec7292c3 100644 --- a/EQ2/source/WorldServer/Commands/Commands.h +++ b/EQ2/source/WorldServer/Commands/Commands.h @@ -872,6 +872,9 @@ private: #define COMMAND_MOVECHARACTER 525 +#define COMMAND_CRAFTITEM 526 + + #define GET_AA_XML 750 #define ADD_AA 751 #define COMMIT_AA_PROFILE 752 diff --git a/EQ2/source/WorldServer/Entity.cpp b/EQ2/source/WorldServer/Entity.cpp index efaf534db..d32e862b9 100644 --- a/EQ2/source/WorldServer/Entity.cpp +++ b/EQ2/source/WorldServer/Entity.cpp @@ -28,10 +28,14 @@ #include "classes.h" #include "LuaInterface.h" #include "ClientPacketFunctions.h" +#include "Skills.h" +#include "Rules/Rules.h" extern World world; extern MasterItemList master_item_list; extern MasterSpellList master_spell_list; +extern MasterSkillList master_skill_list; +extern RuleManager rule_manager; extern Classes classes; Entity::Entity(){ @@ -77,7 +81,6 @@ Entity::Entity(){ MCommandMutex.SetName("Entity::MCommandMutex"); hasSeeInvisSpell = false; hasSeeHideSpell = false; - } Entity::~Entity(){ @@ -118,15 +121,15 @@ void Entity::MapInfoStruct() get_int16_funcs["max_mitigation"] = l::bind(&InfoStruct::get_max_mitigation, &info_struct); get_int16_funcs["mitigation_base"] = l::bind(&InfoStruct::get_mitigation_base, &info_struct); get_int16_funcs["avoidance_display"] = l::bind(&InfoStruct::get_avoidance_display, &info_struct); - get_int16_funcs["cur_avoidance"] = l::bind(&InfoStruct::get_cur_avoidance, &info_struct); + get_float_funcs["cur_avoidance"] = l::bind(&InfoStruct::get_cur_avoidance, &info_struct); get_int16_funcs["base_avoidance_pct"] = l::bind(&InfoStruct::get_base_avoidance_pct, &info_struct); get_int16_funcs["avoidance_base"] = l::bind(&InfoStruct::get_avoidance_base, &info_struct); get_int16_funcs["max_avoidance"] = l::bind(&InfoStruct::get_max_avoidance, &info_struct); - get_int16_funcs["parry"] = l::bind(&InfoStruct::get_parry, &info_struct); - get_int16_funcs["parry_base"] = l::bind(&InfoStruct::get_parry_base, &info_struct); - get_int16_funcs["deflection"] = l::bind(&InfoStruct::get_deflection, &info_struct); + get_float_funcs["parry"] = l::bind(&InfoStruct::get_parry, &info_struct); + get_float_funcs["parry_base"] = l::bind(&InfoStruct::get_parry_base, &info_struct); + get_float_funcs["deflection"] = l::bind(&InfoStruct::get_deflection, &info_struct); get_int16_funcs["deflection_base"] = l::bind(&InfoStruct::get_deflection_base, &info_struct); - get_int16_funcs["block"] = l::bind(&InfoStruct::get_block, &info_struct); + get_float_funcs["block"] = l::bind(&InfoStruct::get_block, &info_struct); get_int16_funcs["block_base"] = l::bind(&InfoStruct::get_block_base, &info_struct); get_float_funcs["str"] = l::bind(&InfoStruct::get_str, &info_struct); get_float_funcs["sta"] = l::bind(&InfoStruct::get_sta, &info_struct); @@ -231,6 +234,12 @@ void Entity::MapInfoStruct() get_string_funcs["biography"] = l::bind(&InfoStruct::get_biography, &info_struct); get_float_funcs["drunk"] = l::bind(&InfoStruct::get_drunk, &info_struct); + get_sint16_funcs["power_regen"] = l::bind(&InfoStruct::get_power_regen, &info_struct); + get_sint16_funcs["hp_regen"] = l::bind(&InfoStruct::get_hp_regen, &info_struct); + + get_int8_funcs["power_regen_override"] = l::bind(&InfoStruct::get_power_regen_override, &info_struct); + get_int8_funcs["hp_regen_override"] = l::bind(&InfoStruct::get_hp_regen_override, &info_struct); + /** SETS **/ set_string_funcs["name"] = l::bind(&InfoStruct::set_name, &info_struct, l::_1); @@ -252,15 +261,15 @@ void Entity::MapInfoStruct() set_int16_funcs["max_mitigation"] = l::bind(&InfoStruct::set_max_mitigation, &info_struct, l::_1); set_int16_funcs["mitigation_base"] = l::bind(&InfoStruct::set_mitigation_base, &info_struct, l::_1); set_int16_funcs["avoidance_display"] = l::bind(&InfoStruct::set_avoidance_display, &info_struct, l::_1); - set_int16_funcs["cur_avoidance"] = l::bind(&InfoStruct::set_cur_avoidance, &info_struct, l::_1); + set_float_funcs["cur_avoidance"] = l::bind(&InfoStruct::set_cur_avoidance, &info_struct, l::_1); set_int16_funcs["base_avoidance_pct"] = l::bind(&InfoStruct::set_base_avoidance_pct, &info_struct, l::_1); set_int16_funcs["avoidance_base"] = l::bind(&InfoStruct::set_avoidance_base, &info_struct, l::_1); set_int16_funcs["max_avoidance"] = l::bind(&InfoStruct::set_max_avoidance, &info_struct, l::_1); - set_int16_funcs["parry"] = l::bind(&InfoStruct::set_parry, &info_struct, l::_1); - set_int16_funcs["parry_base"] = l::bind(&InfoStruct::set_parry_base, &info_struct, l::_1); - set_int16_funcs["deflection"] = l::bind(&InfoStruct::set_deflection, &info_struct, l::_1); + set_float_funcs["parry"] = l::bind(&InfoStruct::set_parry, &info_struct, l::_1); + set_float_funcs["parry_base"] = l::bind(&InfoStruct::set_parry_base, &info_struct, l::_1); + set_float_funcs["deflection"] = l::bind(&InfoStruct::set_deflection, &info_struct, l::_1); set_int16_funcs["deflection_base"] = l::bind(&InfoStruct::set_deflection_base, &info_struct, l::_1); - set_int16_funcs["block"] = l::bind(&InfoStruct::set_block, &info_struct, l::_1); + set_float_funcs["block"] = l::bind(&InfoStruct::set_block, &info_struct, l::_1); set_int16_funcs["block_base"] = l::bind(&InfoStruct::set_block_base, &info_struct, l::_1); set_float_funcs["str"] = l::bind(&InfoStruct::set_str, &info_struct, l::_1); set_float_funcs["sta"] = l::bind(&InfoStruct::set_sta, &info_struct, l::_1); @@ -365,6 +374,12 @@ void Entity::MapInfoStruct() set_string_funcs["biography"] = l::bind(&InfoStruct::set_biography, &info_struct, l::_1); set_float_funcs["drunk"] = l::bind(&InfoStruct::set_drunk, &info_struct, l::_1); + set_sint16_funcs["power_regen"] = l::bind(&InfoStruct::set_power_regen, &info_struct, l::_1); + set_sint16_funcs["hp_region"] = l::bind(&InfoStruct::set_hp_regen, &info_struct, l::_1); + + set_int8_funcs["power_regen_override"] = l::bind(&InfoStruct::set_power_regen_override, &info_struct, l::_1); + set_int8_funcs["hp_region_override"] = l::bind(&InfoStruct::set_hp_regen_override, &info_struct, l::_1); + } bool Entity::HasMoved(bool include_heading){ @@ -667,55 +682,11 @@ int8 Entity::GetWieldType(){ } bool Entity::BehindTarget(Spawn* target){ - float target_angle = 360 - target->GetHeading(); - float angle = 360 - GetHeading(); - if(target_angle > angle) - angle = target_angle - angle; - else - angle -= target_angle; - return (angle < 90 || angle > 270); + return BehindSpawn(target, GetX(), GetZ()); } bool Entity::FlankingTarget(Spawn* target) { - float angle; - double diff_x = target->GetX() - GetX(); - double diff_z = target->GetZ() - GetZ(); - if (diff_z == 0) { - if (diff_x > 0) - angle = 90; - else - angle = 270; - } - else - angle = ((atan(diff_x / diff_z)) * 180) / 3.14159265358979323846; - if (angle < 0) - angle = angle + 360; - else - angle = angle + 180; - if (diff_x < 0) - angle = angle + 180; - - if (angle > GetHeading()) - angle = angle - GetHeading(); - else - angle = GetHeading() - angle; - - if (angle > 360) - angle -= 360; - - //LogWrite(SPAWN__ERROR, 0, "Angle", "spawn heading = %f", GetHeading()); - //LogWrite(SPAWN__ERROR, 0, "Angle", "angle = %f", angle); - - return (angle >= 45 && angle <= 315); -} - -float Entity::GetShieldBlockChance(){ - float ret = 0; - Item* item = equipment_list.GetItem(1); - if(item && item->details.item_id > 0 && item->IsShield()){ - - } - return ret; + return IsFlankingSpawn(target, GetX(), GetZ()); } float Entity::GetDodgeChance(){ @@ -729,20 +700,14 @@ bool Entity::EngagedInCombat(){ } void Entity::InCombat(bool val){ - in_combat = val; -} + bool changeCombatState = false; + if((in_combat && !val) || (!in_combat && val)) + changeCombatState = true; -void Entity::SetHPRegen(int16 new_val){ - regen_hp_rate = new_val; -} -void Entity::SetPowerRegen(int16 new_val){ - regen_power_rate = new_val; -} -int16 Entity::GetHPRegen(){ - return regen_hp_rate; -} -int16 Entity::GetPowerRegen(){ - return regen_power_rate; + in_combat = val; + + if(changeCombatState) + SetRegenValues(GetInfoStruct()->get_effective_level()); } void Entity::DoRegenUpdate(){ @@ -751,19 +716,9 @@ void Entity::DoRegenUpdate(){ sint32 hp = GetHP(); sint32 power = GetPower(); - int16 effective_level = GetInfoStruct()->get_effective_level(); - if(!effective_level) - effective_level = GetLevel(); - - // No regen for NPC's while in combat - // Temp solution for now - if (IsNPC() && EngagedInCombat()) - return; - if(hp < GetTotalHP()){ - if(regen_hp_rate == 0) - regen_hp_rate = (int)(effective_level*.75)+(int)(effective_level/10) + 1; - int16 temp = regen_hp_rate + stats[ITEM_STAT_HPREGEN]; + sint16 temp = GetInfoStruct()->get_hp_regen(); + if((hp + temp) > GetTotalHP()) SetHP(GetTotalHP()); else @@ -773,16 +728,14 @@ void Entity::DoRegenUpdate(){ } if(GetPower() < GetTotalPower()){ - if(regen_power_rate == 0) - regen_power_rate = effective_level + (int)(effective_level/10) + 1; - cout << "regen_power_rate: " << regen_power_rate << endl; - if((power + regen_power_rate) > GetTotalPower()) + sint16 temp = GetInfoStruct()->get_power_regen(); + + if((power + temp) > GetTotalPower()) SetPower(GetTotalPower()); else - SetPower(power + regen_power_rate); + SetPower(power + temp); LogWrite(MISC__TODO, 1, "TODO", "Fix this later for mobs\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__); - } } @@ -979,8 +932,39 @@ void Entity::SetMaxSpeed(float val){ max_speed = val; } +float Entity::CalculateSkillStatChance(char* skillName, int16 item_stat, float max_cap, float modifier, bool add_to_skill) +{ + float skillAndItemsChance = 0.0f; + + Skill* skill = GetSkillByName(skillName, false); + if(skill){ + MStats.lock(); + float item_chance_or_skill = stats[item_stat]; + MStats.unlock(); + if(add_to_skill) + { + skillAndItemsChance = (((float)skill->current_val+item_chance_or_skill)/10.0f); // do we know 25 is accurate? 10 gives more 'skill' space, most cap at 70% with items + } + else + { + skillAndItemsChance = ((float)skill->current_val/10.0f); // do we know 25 is accurate? 10 gives more 'skill' space, most cap at 70% with items + + // take chance percentage and add the item stats % (+1 = 1% or .01f) + skillAndItemsChance += (skillAndItemsChance*((item_chance_or_skill + modifier)/100.0f)); + } + } + + if ( max_cap > 0.0f && skillAndItemsChance > max_cap ) + skillAndItemsChance = max_cap; + + return skillAndItemsChance; +} + void Entity::CalculateBonuses(){ InfoStruct* info = &info_struct; + + int16 effective_level = info->get_effective_level() != 0 ? info->get_effective_level() : GetLevel(); + info->set_block(info->get_block_base()); info->set_cur_attack(info->get_attack_base()); @@ -989,10 +973,6 @@ void Entity::CalculateBonuses(){ LogWrite(MISC__TODO, 1, "TODO", "Calculate via current spells\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__); - //info->cur_concentration = 0; - info->set_parry(info->get_parry_base()); - info->set_deflection(info->get_deflection_base()); - info->set_disease(info->get_disease_base()); info->set_divine(info->get_divine_base()); info->set_heat(info->get_heat_base()); @@ -1102,9 +1082,173 @@ void Entity::CalculateBonuses(){ info->add_uncontested_parry(values->uncontested_parry); info->add_uncontested_dodge(values->uncontested_dodge); info->add_uncontested_riposte(values->uncontested_riposte); + + + float full_pct_hit = 100.0f; + + //info->cur_concentration = 0; + MStats.lock(); + float parryStat = stats[ITEM_STAT_PARRY]; + MStats.unlock(); + float parry_pct = CalculateSkillStatChance("Parry", ITEM_STAT_PARRYCHANCE, 70.0f, parryStat); + parry_pct += parry_pct * (info->get_cur_avoidance()/100.0f); + if(parry_pct > 70.0f) + parry_pct = 70.0f; + + info->set_parry(parry_pct); + + full_pct_hit -= parry_pct; + + float block_pct = 0.0f; + + if(GetAdventureClass() != BRAWLER) + { + Item* item = equipment_list.GetItem(EQ2_SECONDARY_SLOT); + if(item && item->details.item_id > 0 && item->IsShield()){ + // if high is set and greater than low use high, otherwise use low + int16 mitigation = item->armor_info->mitigation_high > item->armor_info->mitigation_low ? item->armor_info->mitigation_high : item->armor_info->mitigation_low; + // we frankly don't know the formula for Block, only that it uses the 'Protection' of the shield, which is the mitigation_low/mitigation_high in the armor_info + if(mitigation) + { + /*DOF Prima Guide: Shields now have the following base chances + to block: Tower (10%), Kite (10%), Round + (5%), Buckler (3%). Your chances to block + scale up or down based on the con of your + opponent.*/ + Skill* skill = master_skill_list.GetSkill(item->generic_info.skill_req1); + float baseBlock = 0.0f; + if(skill) + { + if(skill->short_name.data == "towershield" || skill->short_name.data == "kiteshield") + baseBlock = 10.0f; + else if (skill->short_name.data == "roundshield") + baseBlock = 5.0f; + else if (skill->short_name.data == "buckler") + baseBlock = 3.0f; + } + if(GetLevel() > mitigation) + block_pct = log10f((float)mitigation/((float)GetLevel()*10.0f)); + else + block_pct = log10f(((float)GetLevel()/(float)mitigation)) * log10f(GetLevel()) * 2.0f; + + if(block_pct < 0.0f) + block_pct *= -1.0f; + + block_pct += baseBlock; + + block_pct += block_pct * (info->get_cur_avoidance()/100.0f); + if(block_pct > 70.0f) + block_pct = 70.0f; + } + } + } + else + { + //info->cur_concentration = 0; + MStats.lock(); + float deflectionStat = stats[ITEM_STAT_DEFLECTION]; + MStats.unlock(); + block_pct = CalculateSkillStatChance("Deflection", ITEM_STAT_MINIMUMDEFLECTIONCHANCE, 70.0f, deflectionStat+1.0f); + block_pct += block_pct * (info->get_cur_avoidance()/100.0f); + } + + float block_actual = 0.0f; + if(full_pct_hit > 0.0f) + block_actual = block_pct * (full_pct_hit / 100.0f); + + info->set_block(block_actual); + full_pct_hit -= block_actual; + + + //info->cur_concentration = 0; + MStats.lock(); + float defenseStat = stats[ITEM_STAT_DEFENSE]; + MStats.unlock(); + + float dodge_pct = CalculateSkillStatChance("Defense", ITEM_STAT_DODGECHANCE, 100.0f, defenseStat); + dodge_pct += dodge_pct * (info->get_cur_avoidance()/100.0f); + + float dodge_actual = 0.0f; + if(full_pct_hit > 0.0f) + dodge_actual = dodge_pct * (full_pct_hit / 100.0f) + (log10f(GetLevel() * GetAgi()) / 100.0f); + + info->set_avoidance_base(dodge_actual); + + float total_avoidance = parry_pct + block_actual + dodge_actual; + info->set_avoidance_display(total_avoidance); + + SetRegenValues(effective_level); + safe_delete(values); } +void Entity::SetRegenValues(int16 effective_level) +{ + bool classicRegen = rule_manager.GetGlobalRule(R_Spawn, ClassicRegen)->GetBool(); + if(!GetInfoStruct()->get_hp_regen_override()) + { + sint16 regen_hp_rate = 0; + sint16 temp = 0; + + MStats.lock(); + if(!IsAggroed()) + { + if(classicRegen) + { + // classic regen only gives OUT OF COMBAT, doesn't combine in+out of combat + regen_hp_rate = (int)(effective_level*.75)+1; + temp = regen_hp_rate + stats[ITEM_STAT_HPREGEN]; + temp += stats[ITEM_STAT_HPREGENPPT]; + } + else + { + regen_hp_rate = (int)(effective_level*.75)+(int)(effective_level/10) + 1; + temp = regen_hp_rate + stats[ITEM_STAT_HPREGEN]; + temp += stats[ITEM_STAT_HPREGENPPT] + stats[ITEM_STAT_COMBATHPREGENPPT]; + } + } + else + { + regen_hp_rate = (sint16)(effective_level / 10) + 1; + temp = regen_hp_rate + stats[ITEM_STAT_COMBATHPREGENPPT]; + } + MStats.unlock(); + + GetInfoStruct()->set_hp_regen(temp); + } + + if(!GetInfoStruct()->get_power_regen_override()) + { + sint16 regen_power_rate = 0; + sint16 temp = 0; + + MStats.lock(); + if(!IsAggroed()) + { + if(classicRegen) + { + regen_power_rate = effective_level + 1; + temp = regen_power_rate + stats[ITEM_STAT_MANAREGEN]; + temp += stats[ITEM_STAT_MPREGENPPT]; + } + else + { + regen_power_rate = effective_level + (int)(effective_level/10) + 1; + temp = regen_power_rate + stats[ITEM_STAT_MANAREGEN]; + temp += stats[ITEM_STAT_MPREGENPPT] + stats[ITEM_STAT_COMBATMPREGENPPT]; + } + } + else + { + regen_power_rate = (sint16)(effective_level / 10) + 1; + temp = regen_power_rate + stats[ITEM_STAT_COMBATMPREGENPPT]; + } + MStats.unlock(); + + GetInfoStruct()->set_power_regen(temp); + } +} + EquipmentItemList* Entity::GetEquipmentList(){ return &equipment_list; } @@ -1664,6 +1808,7 @@ float Entity::GetSpeed() { if (EngagedInCombat() && GetMaxSpeed() > 0.0f) ret = GetMaxSpeed(); + MStats.lock(); if ((IsStealthed() || IsInvis()) && stats.count(ITEM_STAT_STEALTHINVISSPEEDMOD)) ret += stats[ITEM_STAT_STEALTHINVISSPEEDMOD]; else if (EngagedInCombat() && stats.count(ITEM_STAT_OFFENSIVESPEED)) @@ -1674,6 +1819,7 @@ float Entity::GetSpeed() { ret += stats[ITEM_STAT_SPEED]; else if (stats.count(ITEM_STAT_MOUNTSPEED)) ret += stats[ITEM_STAT_MOUNTSPEED]; + MStats.unlock(); ret *= speed_multiplier; return ret; diff --git a/EQ2/source/WorldServer/Entity.h b/EQ2/source/WorldServer/Entity.h index 13efb4f3e..eff344d8e 100644 --- a/EQ2/source/WorldServer/Entity.h +++ b/EQ2/source/WorldServer/Entity.h @@ -133,12 +133,12 @@ struct InfoStruct{ max_mitigation_ = 0; mitigation_base_ = 0; avoidance_display_ = 0; - cur_avoidance_ = 0; + cur_avoidance_ = 0.0f; base_avoidance_pct_ = 0; avoidance_base_ = 0; max_avoidance_ = 0; - parry_ = 0; - parry_base_ = 0; + parry_ = 0.0f; + parry_base_ = 0.0f; deflection_ = 0; deflection_base_ = 0; block_ = 0; @@ -246,7 +246,11 @@ struct InfoStruct{ breathe_underwater_ = 0; biography_ = std::string(""); drunk_ = 0; + power_regen_ = 0; + hp_regen_ = 0; + power_regen_override_ = 0; + hp_regen_override_ = 0; } @@ -388,7 +392,11 @@ struct InfoStruct{ breathe_underwater_ = oldStruct->get_breathe_underwater(); biography_ = std::string(oldStruct->get_biography()); drunk_ = oldStruct->get_drunk(); + power_regen_ = oldStruct->get_power_regen(); + hp_regen_ = oldStruct->get_hp_regen(); + power_regen_override_ = oldStruct->get_power_regen_override(); + hp_regen_override_ = oldStruct->get_hp_regen_override(); } //mutable std::shared_mutex mutex_; @@ -413,19 +421,19 @@ struct InfoStruct{ int16 get_mitigation_base() { std::lock_guard<std::mutex> lk(classMutex); return mitigation_base_; } int16 get_avoidance_display() { std::lock_guard<std::mutex> lk(classMutex); return avoidance_display_; } - int16 get_cur_avoidance() { std::lock_guard<std::mutex> lk(classMutex); return cur_avoidance_; } + float get_cur_avoidance() { std::lock_guard<std::mutex> lk(classMutex); return cur_avoidance_; } int16 get_base_avoidance_pct() { std::lock_guard<std::mutex> lk(classMutex); return base_avoidance_pct_; } int16 get_avoidance_base() { std::lock_guard<std::mutex> lk(classMutex); return avoidance_base_; } - int16 get_parry() { std::lock_guard<std::mutex> lk(classMutex); return parry_; } - int16 get_parry_base() { std::lock_guard<std::mutex> lk(classMutex); return parry_base_; } + float get_parry() { std::lock_guard<std::mutex> lk(classMutex); return parry_; } + float get_parry_base() { std::lock_guard<std::mutex> lk(classMutex); return parry_base_; } int16 get_max_avoidance() { std::lock_guard<std::mutex> lk(classMutex); return max_avoidance_; } - int16 get_deflection() { std::lock_guard<std::mutex> lk(classMutex); return deflection_; } + float get_deflection() { std::lock_guard<std::mutex> lk(classMutex); return deflection_; } int16 get_deflection_base() { std::lock_guard<std::mutex> lk(classMutex); return deflection_base_; } - int16 get_block() { std::lock_guard<std::mutex> lk(classMutex); return block_; } + float get_block() { std::lock_guard<std::mutex> lk(classMutex); return block_; } int16 get_block_base() { std::lock_guard<std::mutex> lk(classMutex); return block_base_; } float get_str() { std::lock_guard<std::mutex> lk(classMutex); return str_; } @@ -540,6 +548,12 @@ struct InfoStruct{ std::string get_biography() { std::lock_guard<std::mutex> lk(classMutex); return biography_; } float get_drunk() { std::lock_guard<std::mutex> lk(classMutex); return drunk_; } + sint16 get_power_regen() { std::lock_guard<std::mutex> lk(classMutex); return power_regen_; } + sint16 get_hp_regen() { std::lock_guard<std::mutex> lk(classMutex); return hp_regen_; } + + int8 get_power_regen_override() { std::lock_guard<std::mutex> lk(classMutex); return power_regen_override_; } + int8 get_hp_regen_override() { std::lock_guard<std::mutex> lk(classMutex); return hp_regen_override_; } + void set_name(std::string value) { std::lock_guard<std::mutex> lk(classMutex); name_ = value; } void set_deity(std::string value) { std::lock_guard<std::mutex> lk(classMutex); deity_ = value; } @@ -568,15 +582,15 @@ struct InfoStruct{ void add_mitigation_base(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_base_ += value; } void set_avoidance_display(int16 value) { std::lock_guard<std::mutex> lk(classMutex); avoidance_display_ = value; } - void set_cur_avoidance(int16 value) { std::lock_guard<std::mutex> lk(classMutex); cur_avoidance_ = value; } + void set_cur_avoidance(float value) { std::lock_guard<std::mutex> lk(classMutex); cur_avoidance_ = value; } void set_base_avoidance_pct(int16 value) { std::lock_guard<std::mutex> lk(classMutex); base_avoidance_pct_ = value; } void set_avoidance_base(int16 value) { std::lock_guard<std::mutex> lk(classMutex); avoidance_base_ = value; } void set_max_avoidance(int16 value) { std::lock_guard<std::mutex> lk(classMutex); max_avoidance_ = value; } - void set_parry(int16 value) { std::lock_guard<std::mutex> lk(classMutex); parry_ = value; } - void set_parry_base(int16 value) { std::lock_guard<std::mutex> lk(classMutex); parry_base_ = value; } + void set_parry(float value) { std::lock_guard<std::mutex> lk(classMutex); parry_ = value; } + void set_parry_base(float value) { std::lock_guard<std::mutex> lk(classMutex); parry_base_ = value; } void set_deflection(int16 value) { std::lock_guard<std::mutex> lk(classMutex); deflection_ = value; } - void set_deflection_base(int16 value) { std::lock_guard<std::mutex> lk(classMutex); deflection_base_ = value; } - void set_block(int16 value) { std::lock_guard<std::mutex> lk(classMutex); block_ = value; } + void set_deflection_base(float value) { std::lock_guard<std::mutex> lk(classMutex); deflection_base_ = value; } + void set_block(float value) { std::lock_guard<std::mutex> lk(classMutex); block_ = value; } void set_block_base(int16 value) { std::lock_guard<std::mutex> lk(classMutex); block_base_ = value; } void set_str(float value) { std::lock_guard<std::mutex> lk(classMutex); str_ = value; } @@ -773,6 +787,12 @@ struct InfoStruct{ void set_biography(std::string value) { std::lock_guard<std::mutex> lk(classMutex); biography_ = value; } + void set_power_regen(sint16 value) { std::lock_guard<std::mutex> lk(classMutex); power_regen_ = value; } + void set_hp_regen(sint16 value) { std::lock_guard<std::mutex> lk(classMutex); hp_regen_ = value; } + + void set_power_regen_override(int8 value) { std::lock_guard<std::mutex> lk(classMutex); power_regen_override_ = value; } + void set_hp_regen_override(int8 value) { std::lock_guard<std::mutex> lk(classMutex); hp_regen_override_ = value; } + void ResetEffects(Spawn* spawn) { for(int i=0;i<45;i++){ @@ -811,16 +831,18 @@ private: int16 max_mitigation_; int16 mitigation_base_; int16 avoidance_display_; - int16 cur_avoidance_; + float cur_avoidance_; int16 base_avoidance_pct_; int16 avoidance_base_; int16 max_avoidance_; - int16 parry_; - int16 parry_base_; - int16 deflection_; + float parry_; + float parry_base_; + float deflection_; int16 deflection_base_; - int16 block_; + float block_; int16 block_base_; + float riposte_; + float riposte_base_; float str_; //int16 float sta_; //int16 float agi_;//int16 @@ -928,6 +950,11 @@ private: std::string biography_; float drunk_; + sint16 power_regen_; + sint16 hp_regen_; + + int8 power_regen_override_; + int8 hp_regen_override_; // when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock std::mutex classMutex; }; @@ -1034,7 +1061,6 @@ public: virtual ~Entity(); void MapInfoStruct(); - virtual float GetShieldBlockChance(); virtual float GetDodgeChance(); virtual void AddMaintainedSpell(LuaSpell* spell); virtual void AddSpellEffect(LuaSpell* spell); @@ -1054,7 +1080,9 @@ public: EquipmentItemList* GetEquipmentList(); bool IsEntity(){ return true; } + float CalculateSkillStatChance(char* skill, int16 item_stat, float max_cap = 0.0f, float modifier = 0.0f, bool add_to_skill = false); void CalculateBonuses(); + void SetRegenValues(int16 effective_level); float CalculateBonusMod(); float CalculateDPSMultiplier(); float CalculateCastingSpeedMod(); @@ -1098,9 +1126,7 @@ public: bool HasMoved(bool include_heading); void SetHPRegen(int16 new_val); - void SetPowerRegen(int16 new_val); int16 GetHPRegen(); - int16 GetPowerRegen(); void DoRegenUpdate(); MaintainedEffects* GetFreeMaintainedSpellSlot(); SpellEffects* GetFreeSpellEffectSlot(); @@ -1499,6 +1525,17 @@ public: // Keep track of entities that hate this spawn. set<int32> HatedBy; + std::mutex MHatedBy; + + bool IsAggroed() { + int32 size = 0; + + MHatedBy.lock(); + size = HatedBy.size(); + MHatedBy.unlock(); + + return size > 0; + } Mutex MCommandMutex; @@ -1539,6 +1576,7 @@ public: // when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock std::mutex MEquipment; + std::mutex MStats; protected: bool in_combat; diff --git a/EQ2/source/WorldServer/GroundSpawn.cpp b/EQ2/source/WorldServer/GroundSpawn.cpp index d2a55d731..3beb62dc8 100644 --- a/EQ2/source/WorldServer/GroundSpawn.cpp +++ b/EQ2/source/WorldServer/GroundSpawn.cpp @@ -132,6 +132,15 @@ void GroundSpawn::ProcessHarvest(Client* client) { return; } + int16 totalSkill = skill->current_val; + int32 skillID = master_item_list.GetItemStatIDByName(collection_skill); + if(skillID != 0xFFFFFFFF) + { + ((Entity*)client->GetPlayer())->MStats.lock(); + totalSkill += ((Entity*)client->GetPlayer())->stats[skillID]; + ((Entity*)client->GetPlayer())->MStats.unlock(); + } + for (int8 i = 0; i < num_attempts_per_harvest; i++) { vector<GroundSpawnEntry*> mod_groundspawn_entries; @@ -147,7 +156,7 @@ void GroundSpawn::ProcessHarvest(Client* client) { entry = *itr; // if player lacks skill, skip table - if (entry->min_skill_level > skill->current_val) + if (entry->min_skill_level > totalSkill) continue; // if bonus, but player lacks level, skip table if (entry->bonus_table && (client->GetPlayer()->GetLevel() < entry->min_adventure_level)) @@ -182,7 +191,7 @@ void GroundSpawn::ProcessHarvest(Client* client) { } // now roll to see which table to use - table_choice = MakeRandomInt(lowest_skill_level, skill->current_val); + table_choice = MakeRandomInt(lowest_skill_level, totalSkill); LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Random INT for Table by skill level: %i", table_choice); int16 highest_score = 0; @@ -251,7 +260,7 @@ void GroundSpawn::ProcessHarvest(Client* client) { harvest_type = 2; reward_total = 3; } - else if (chance <= selected_table->harvest1 || skill->current_val == skill->max_val || is_collection) { + else if (chance <= selected_table->harvest1 || totalSkill >= skill->max_val || is_collection) { LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest 1 Item from table : %i", selected_table->min_skill_level); harvest_type = 1; } diff --git a/EQ2/source/WorldServer/Items/Items.cpp b/EQ2/source/WorldServer/Items/Items.cpp index d89301f85..9062707ca 100644 --- a/EQ2/source/WorldServer/Items/Items.cpp +++ b/EQ2/source/WorldServer/Items/Items.cpp @@ -30,6 +30,7 @@ #include "../Recipes/Recipe.h" #include <algorithm> #include <sstream> +#include <boost/algorithm/string.hpp> extern World world; extern MasterSpellList master_spell_list; @@ -38,6 +39,49 @@ extern MasterRecipeList master_recipe_list; extern ConfigReader configReader; extern LuaInterface* lua_interface; +MasterItemList::MasterItemList(){ + AddMappedItemStat(ITEM_STAT_ADORNING, std::string("adorning")); + AddMappedItemStat(ITEM_STAT_AGGRESSION, std::string("aggression")); + AddMappedItemStat(ITEM_STAT_ARTIFICING, std::string("artificing")); + AddMappedItemStat(ITEM_STAT_ARTISTRY, std::string("artistry")); + AddMappedItemStat(ITEM_STAT_CHEMISTRY, std::string("chemistry")); + AddMappedItemStat(ITEM_STAT_CRUSHING, std::string("crushing")); + AddMappedItemStat(ITEM_STAT_DEFENSE, std::string("defense")); + AddMappedItemStat(ITEM_STAT_DEFLECTION, std::string("deflection")); + AddMappedItemStat(ITEM_STAT_DISRUPTION, std::string("disruption")); + AddMappedItemStat(ITEM_STAT_FISHING, std::string("fishing")); + AddMappedItemStat(ITEM_STAT_FLETCHING, std::string("fletching")); + AddMappedItemStat(ITEM_STAT_FOCUS, std::string("focus")); + AddMappedItemStat(ITEM_STAT_FORESTING, std::string("foresting")); + AddMappedItemStat(ITEM_STAT_GATHERING, std::string("gathering")); + AddMappedItemStat(ITEM_STAT_METAL_SHAPING, std::string("metal shaping")); + AddMappedItemStat(ITEM_STAT_METALWORKING, std::string("metalworking")); + AddMappedItemStat(ITEM_STAT_MINING, std::string("mining")); + AddMappedItemStat(ITEM_STAT_MINISTRATION, std::string("ministration")); + AddMappedItemStat(ITEM_STAT_ORDINATION, std::string("ordination")); + AddMappedItemStat(ITEM_STAT_ADORNING, std::string("adorning")); + AddMappedItemStat(ITEM_STAT_PARRY, std::string("parry")); + AddMappedItemStat(ITEM_STAT_PIERCING, std::string("piercing")); + AddMappedItemStat(ITEM_STAT_RANGED, std::string("ranged")); + AddMappedItemStat(ITEM_STAT_SAFE_FALL, std::string("safe fall")); + AddMappedItemStat(ITEM_STAT_SCRIBING, std::string("scribing")); + AddMappedItemStat(ITEM_STAT_SCULPTING, std::string("sculpting")); + AddMappedItemStat(ITEM_STAT_SLASHING, std::string("slashing")); + AddMappedItemStat(ITEM_STAT_SUBJUGATION, std::string("subjugation")); + AddMappedItemStat(ITEM_STAT_SWIMMING, std::string("swimming")); + AddMappedItemStat(ITEM_STAT_TAILORING, std::string("tailoring")); + AddMappedItemStat(ITEM_STAT_TINKERING, std::string("tinkering")); + AddMappedItemStat(ITEM_STAT_TRANSMUTING, std::string("transmuting")); + AddMappedItemStat(ITEM_STAT_TRAPPING, std::string("trapping")); + AddMappedItemStat(ITEM_STAT_WEAPON_SKILLS, std::string("weapon skills")); +} + +void MasterItemList::AddMappedItemStat(int32 id, std::string lower_case_name) +{ + mappedItemStatsStrings[lower_case_name] = id; + mappedItemStatTypeIDs[id] = lower_case_name; +} + MasterItemList::~MasterItemList(){ RemoveAll(); } @@ -707,7 +751,28 @@ ItemStatsValues* MasterItemList::CalculateItemBonuses(Item* item, Entity* entity } for(int32 i=0;i<item->item_stats.size();i++){ ItemStat* stat = item->item_stats[i]; - world.AddBonuses(values, stat->stat_type*100 + stat->stat_subtype, stat->value, entity); + int multiplier = 100; + if(stat->stat_subtype > 99) + multiplier = 1000; + + int32 id = 0; + sint32 value = stat->value; + if(stat->stat_type != 1) + id = stat->stat_type*multiplier + stat->stat_subtype; + else + { + int32 tmp_id = master_item_list.GetItemStatIDByName(stat->stat_name); + if(tmp_id != 0xFFFFFFFF) + { + id = tmp_id; + if(!value) + value = stat->stat_subtype; + } + else + id = stat->stat_type*multiplier + stat->stat_subtype; + } + + world.AddBonuses(values, id, stat->value, entity); } return values; } @@ -3122,7 +3187,6 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 } } else{ - printf("7\n"); MoveItem(item_from, item_to->details.bag_id, 0); MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); return true; @@ -3841,4 +3905,23 @@ string Item::CreateItemLink(int16 client_Version, bool bUseUniqueID) { ss << "\\aITEM " << details.item_id << ' ' << name << ':' << name << "\\/a"; } return ss.str(); +} + +int32 MasterItemList::GetItemStatIDByName(std::string name) +{ + boost::to_lower(name); + map<std::string, int32>::iterator itr = mappedItemStatsStrings.find(name.c_str()); + if(itr != mappedItemStatsStrings.end()) + return itr->second; + + return 0xFFFFFFFF; +} + +std::string MasterItemList::GetItemStatNameByID(int32 id) +{ + map<int32, std::string>::iterator itr = mappedItemStatTypeIDs.find(id); + if(itr != mappedItemStatTypeIDs.end()) + return itr->second; + + return std::string(""); } \ No newline at end of file diff --git a/EQ2/source/WorldServer/Items/Items.h b/EQ2/source/WorldServer/Items/Items.h index 5478761d7..c29d33320 100644 --- a/EQ2/source/WorldServer/Items/Items.h +++ b/EQ2/source/WorldServer/Items/Items.h @@ -347,6 +347,40 @@ extern MasterItemList master_item_list; #define ITEM_STAT_WIS 3 #define ITEM_STAT_INT 4 +#define ITEM_STAT_ADORNING 100 +#define ITEM_STAT_AGGRESSION 101 +#define ITEM_STAT_ARTIFICING 102 +#define ITEM_STAT_ARTISTRY 103 +#define ITEM_STAT_CHEMISTRY 104 +#define ITEM_STAT_CRUSHING 105 +#define ITEM_STAT_DEFENSE 106 +#define ITEM_STAT_DEFLECTION 107 +#define ITEM_STAT_DISRUPTION 108 +#define ITEM_STAT_FISHING 109 +#define ITEM_STAT_FLETCHING 110 +#define ITEM_STAT_FOCUS 111 +#define ITEM_STAT_FORESTING 112 +#define ITEM_STAT_GATHERING 113 +#define ITEM_STAT_METAL_SHAPING 114 +#define ITEM_STAT_METALWORKING 115 +#define ITEM_STAT_MINING 116 +#define ITEM_STAT_MINISTRATION 117 +#define ITEM_STAT_ORDINATION 118 +#define ITEM_STAT_PARRY 119 +#define ITEM_STAT_PIERCING 120 +#define ITEM_STAT_RANGED 121 +#define ITEM_STAT_SAFE_FALL 122 +#define ITEM_STAT_SCRIBING 123 +#define ITEM_STAT_SCULPTING 124 +#define ITEM_STAT_SLASHING 125 +#define ITEM_STAT_SUBJUGATION 126 +#define ITEM_STAT_SWIMMING 127 +#define ITEM_STAT_TAILORING 128 +#define ITEM_STAT_TINKERING 129 +#define ITEM_STAT_TRANSMUTING 130 +#define ITEM_STAT_TRAPPING 131 +#define ITEM_STAT_WEAPON_SKILLS 132 + #define ITEM_STAT_VS_PHYSICAL 200 #define ITEM_STAT_VS_HEAT 201 //elemental #define ITEM_STAT_VS_POISON 202 //noxious @@ -932,6 +966,7 @@ public: }; class MasterItemList{ public: + MasterItemList(); ~MasterItemList(); map<int32,Item*> items; @@ -947,6 +982,11 @@ public: static int32 NextUniqueID(); static void ResetUniqueID(int32 new_id); static int32 next_unique_id; + int32 GetItemStatIDByName(std::string name); + std::string GetItemStatNameByID(int32 id); + void AddMappedItemStat(int32 id, std::string lower_case_name); + map<std::string, int32> mappedItemStatsStrings; + map<int32, std::string> mappedItemStatTypeIDs; }; class PlayerItemList { public: diff --git a/EQ2/source/WorldServer/Items/Items_CoE.h b/EQ2/source/WorldServer/Items/Items_CoE.h index 0cb566402..e042f31b9 100644 --- a/EQ2/source/WorldServer/Items/Items_CoE.h +++ b/EQ2/source/WorldServer/Items/Items_CoE.h @@ -248,6 +248,40 @@ extern MasterItemList master_item_list; #define ITEM_STAT_WIS 3 #define ITEM_STAT_INT 4 +#define ITEM_STAT_ADORNING 100 +#define ITEM_STAT_AGGRESSION 101 +#define ITEM_STAT_ARTIFICING 102 +#define ITEM_STAT_ARTISTRY 103 +#define ITEM_STAT_CHEMISTRY 104 +#define ITEM_STAT_CRUSHING 105 +#define ITEM_STAT_DEFENSE 106 +#define ITEM_STAT_DEFLECTION 107 +#define ITEM_STAT_DISRUPTION 108 +#define ITEM_STAT_FISHING 109 +#define ITEM_STAT_FLETCHING 110 +#define ITEM_STAT_FOCUS 111 +#define ITEM_STAT_FORESTING 112 +#define ITEM_STAT_GATHERING 113 +#define ITEM_STAT_METAL_SHAPING 114 +#define ITEM_STAT_METALWORKING 115 +#define ITEM_STAT_MINING 116 +#define ITEM_STAT_MINISTRATION 117 +#define ITEM_STAT_ORDINATION 118 +#define ITEM_STAT_PARRY 119 +#define ITEM_STAT_PIERCING 120 +#define ITEM_STAT_RANGED 121 +#define ITEM_STAT_SAFE_FALL 122 +#define ITEM_STAT_SCRIBING 123 +#define ITEM_STAT_SCULPTING 124 +#define ITEM_STAT_SLASHING 125 +#define ITEM_STAT_SUBJUGATION 126 +#define ITEM_STAT_SWIMMING 127 +#define ITEM_STAT_TAILORING 128 +#define ITEM_STAT_TINKERING 129 +#define ITEM_STAT_TRANSMUTING 130 +#define ITEM_STAT_TRAPPING 131 +#define ITEM_STAT_WEAPON_SKILLS 132 + #define ITEM_STAT_VS_PHYSICAL 200 #define ITEM_STAT_VS_ELEMENTAL 201 #define ITEM_STAT_VS_NOXIOUS 202 diff --git a/EQ2/source/WorldServer/Items/Items_DoV.h b/EQ2/source/WorldServer/Items/Items_DoV.h index 9bdce6798..d3196503d 100644 --- a/EQ2/source/WorldServer/Items/Items_DoV.h +++ b/EQ2/source/WorldServer/Items/Items_DoV.h @@ -261,6 +261,40 @@ extern MasterItemList master_item_list; #define ITEM_STAT_WIS 3 #define ITEM_STAT_INT 4 +#define ITEM_STAT_ADORNING 100 +#define ITEM_STAT_AGGRESSION 101 +#define ITEM_STAT_ARTIFICING 102 +#define ITEM_STAT_ARTISTRY 103 +#define ITEM_STAT_CHEMISTRY 104 +#define ITEM_STAT_CRUSHING 105 +#define ITEM_STAT_DEFENSE 106 +#define ITEM_STAT_DEFLECTION 107 +#define ITEM_STAT_DISRUPTION 108 +#define ITEM_STAT_FISHING 109 +#define ITEM_STAT_FLETCHING 110 +#define ITEM_STAT_FOCUS 111 +#define ITEM_STAT_FORESTING 112 +#define ITEM_STAT_GATHERING 113 +#define ITEM_STAT_METAL_SHAPING 114 +#define ITEM_STAT_METALWORKING 115 +#define ITEM_STAT_MINING 116 +#define ITEM_STAT_MINISTRATION 117 +#define ITEM_STAT_ORDINATION 118 +#define ITEM_STAT_PARRY 119 +#define ITEM_STAT_PIERCING 120 +#define ITEM_STAT_RANGED 121 +#define ITEM_STAT_SAFE_FALL 122 +#define ITEM_STAT_SCRIBING 123 +#define ITEM_STAT_SCULPTING 124 +#define ITEM_STAT_SLASHING 125 +#define ITEM_STAT_SUBJUGATION 126 +#define ITEM_STAT_SWIMMING 127 +#define ITEM_STAT_TAILORING 128 +#define ITEM_STAT_TINKERING 129 +#define ITEM_STAT_TRANSMUTING 130 +#define ITEM_STAT_TRAPPING 131 +#define ITEM_STAT_WEAPON_SKILLS 132 + #define ITEM_STAT_VS_PHYSICAL 200 #define ITEM_STAT_VS_HEAT 201 //elemental #define ITEM_STAT_VS_POISON 202 //noxious diff --git a/EQ2/source/WorldServer/Items/Items_ToV.h b/EQ2/source/WorldServer/Items/Items_ToV.h index 4363249e1..30bfbc274 100644 --- a/EQ2/source/WorldServer/Items/Items_ToV.h +++ b/EQ2/source/WorldServer/Items/Items_ToV.h @@ -175,3 +175,37 @@ #define TOV_ITEM_STAT_INT 4 + +#define TOV_ITEM_STAT_ADORNING 100 +#define TOV_ITEM_STAT_AGGRESSION 101 +#define TOV_ITEM_STAT_ARTIFICING 102 +#define TOV_ITEM_STAT_ARTISTRY 103 +#define TOV_ITEM_STAT_CHEMISTRY 104 +#define TOV_ITEM_STAT_CRUSHING 105 +#define TOV_ITEM_STAT_DEFENSE 106 +#define TOV_ITEM_STAT_DEFLECTION 107 +#define TOV_ITEM_STAT_DISRUPTION 108 +#define TOV_ITEM_STAT_FISHING 109 +#define TOV_ITEM_STAT_FLETCHING 110 +#define TOV_ITEM_STAT_FOCUS 111 +#define TOV_ITEM_STAT_FORESTING 112 +#define TOV_ITEM_STAT_GATHERING 113 +#define TOV_ITEM_STAT_METAL_SHAPING 114 +#define TOV_ITEM_STAT_METALWORKING 115 +#define TOV_ITEM_STAT_MINING 116 +#define TOV_ITEM_STAT_MINISTRATION 117 +#define TOV_ITEM_STAT_ORDINATION 118 +#define TOV_ITEM_STAT_PARRY 119 +#define TOV_ITEM_STAT_PIERCING 120 +#define TOV_ITEM_STAT_RANGED 121 +#define TOV_ITEM_STAT_SAFE_FALL 122 +#define TOV_ITEM_STAT_SCRIBING 123 +#define TOV_ITEM_STAT_SCULPTING 124 +#define TOV_ITEM_STAT_SLASHING 125 +#define TOV_ITEM_STAT_SUBJUGATION 126 +#define TOV_ITEM_STAT_SWIMMING 127 +#define TOV_ITEM_STAT_TAILORING 128 +#define TOV_ITEM_STAT_TINKERING 129 +#define TOV_ITEM_STAT_TRANSMUTING 130 +#define TOV_ITEM_STAT_TRAPPING 131 +#define TOV_ITEM_STAT_WEAPON_SKILLS 132 \ No newline at end of file diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index d1e09e0ae..5b068af25 100644 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/EQ2/source/WorldServer/LuaFunctions.cpp @@ -2132,6 +2132,34 @@ int EQ2Emu_lua_AddSpawnSpellBonus(lua_State* state) { return 0; } +int EQ2Emu_lua_RemoveSpawnSpellBonus(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* spawn = lua_interface->GetSpawn(state); + LuaSpell* luaspell = lua_interface->GetCurrentSpell(state); + + if (!spawn) { + lua_interface->LogError("%s: LUA AddSpawnSpellBonus command error: spawn is not valid", lua_interface->GetScriptName(state)); + return 0; + } + + if (!spawn->IsEntity()) { + lua_interface->LogError("%s: LUA AddSpawnSpellBonus command error: spawn is not an entity", lua_interface->GetScriptName(state)); + return 0; + } + + if (!luaspell || !luaspell->spell) { + lua_interface->LogError("%s: LUA AddSpawnSpellBonus command error: can only be used in a spell script", lua_interface->GetScriptName(state)); + return 0; + } + + ((Entity*)spawn)->RemoveSpellBonus(luaspell); + if (spawn->IsPlayer()) + ((Player*)spawn)->SetCharSheetChanged(true); + + return 0; +} + int EQ2Emu_lua_RemoveSpellBonus(lua_State* state) { if (!lua_interface) return 0; @@ -7065,7 +7093,15 @@ int EQ2Emu_lua_CanHarvest(lua_State* state) { GroundSpawnEntry* entry = 0; bool can_harvest = false; sint32 min_skill = -1; - + int16 totalSkill = skill->current_val; + int32 skillID = master_item_list.GetItemStatIDByName(collection_skill); + if(skillID != 0xFFFFFFFF) + { + ((Entity*)player)->MStats.lock(); + totalSkill += ((Entity*)player)->stats[skillID]; + ((Entity*)player)->MStats.unlock(); + } + // first, iterate through groundspawn_entries, discard tables player cannot use for (itr = groundspawn_entries->begin(); itr != groundspawn_entries->end(); itr++) { @@ -7074,7 +7110,7 @@ int EQ2Emu_lua_CanHarvest(lua_State* state) { if (min_skill == -1 || entry->min_skill_level < min_skill) min_skill = entry->min_skill_level; // if player lacks skill, skip table - if (entry->min_skill_level > skill->current_val) + if (entry->min_skill_level > totalSkill) continue; // if bonus, but player lacks level, skip table if (entry->bonus_table && (player->GetLevel() < entry->min_adventure_level)) @@ -7103,7 +7139,7 @@ int EQ2Emu_lua_CanHarvest(lua_State* state) { msg.append("catch"); msg.append(" the %s. It requires %i %s skill, and your skill is %i."); - client->Message(CHANNEL_HARVESTING_WARNINGS, msg.c_str(), ground->GetName(), min_skill, skill->name.data.c_str(), skill->current_val); + client->Message(CHANNEL_HARVESTING_WARNINGS, msg.c_str(), ground->GetName(), min_skill, skill->name.data.c_str(), totalSkill); // You do not have enough skill to catch the band of fish. It requires 20 Fishing skill, and your skill is 12. } } @@ -9984,12 +10020,16 @@ int EQ2Emu_lua_Evac(lua_State* state) { PacketStruct* packet = configReader.getStruct("WS_TeleportWithinZone", client->GetVersion()); if (packet) { + client->SetReloadingZone(true); packet->setDataByName("x", x); packet->setDataByName("y", y); packet->setDataByName("z", z); client->QueuePacket(packet->serialize()); safe_delete(packet); } + + client->GetCurrentZone()->ClearHate(client->GetPlayer()); + client->GetCurrentZone()->RemoveSpawn(client->GetPlayer(), false, false, false, false); } } } @@ -10807,6 +10847,7 @@ int EQ2Emu_lua_GetSpell(lua_State* state) { return 0; int32 spell_id = lua_interface->GetInt32Value(state); int8 spell_tier = lua_interface->GetInt8Value(state, 2); + string custom_lua_script = lua_interface->GetStringValue(state, 3); if (spell_id > 0) { if (spell_tier == 0) @@ -10814,8 +10855,10 @@ int EQ2Emu_lua_GetSpell(lua_State* state) { Spell* spell = master_spell_list.GetSpell(spell_id, spell_tier); LuaSpell* lua_spell = 0; + if(custom_lua_script.size() < 1) + custom_lua_script = spell->GetSpellData()->lua_script; if (lua_interface) - lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str()); + lua_spell = lua_interface->GetSpell(custom_lua_script.c_str()); if (!lua_spell) return 0; @@ -11793,5 +11836,49 @@ int EQ2Emu_lua_MakeRandomFloat(lua_State* state) { float max = lua_interface->GetFloatValue(state, 2); float result = MakeRandomFloat(min, max); lua_interface->SetFloatValue(state, result); + return 1; +} + +int EQ2Emu_lua_AddIconValue(lua_State* state) { + if (!lua_interface) + return 0; + + Spawn* spawn = lua_interface->GetSpawn(state); + int32 value = lua_interface->GetInt32Value(state, 2); + + lua_interface->ResetFunctionStack(state); + + if(!spawn) + { + lua_interface->LogError("%s: LUA AddIconValue command error: spawn is not valid, does not exist", lua_interface->GetScriptName(state)); + lua_interface->SetBooleanValue(state, false); + return 1; + } + + spawn->AddIconValue(value); + lua_interface->SetBooleanValue(state, true); + + return 1; +} + +int EQ2Emu_lua_RemoveIconValue(lua_State* state) { + if (!lua_interface) + return 0; + + Spawn* spawn = lua_interface->GetSpawn(state); + int32 value = lua_interface->GetInt32Value(state, 2); + + lua_interface->ResetFunctionStack(state); + + if(!spawn) + { + lua_interface->LogError("%s: LUA RemoveIconValue command error: spawn is not valid, does not exist", lua_interface->GetScriptName(state)); + lua_interface->SetBooleanValue(state, false); + return 1; + } + + spawn->RemoveIconValue(value); + lua_interface->SetBooleanValue(state, true); + return 1; } \ No newline at end of file diff --git a/EQ2/source/WorldServer/LuaFunctions.h b/EQ2/source/WorldServer/LuaFunctions.h index 52aefc219..65b9359fc 100644 --- a/EQ2/source/WorldServer/LuaFunctions.h +++ b/EQ2/source/WorldServer/LuaFunctions.h @@ -409,6 +409,7 @@ int EQ2Emu_lua_RemoveThreatTransfer(lua_State* state); int EQ2Emu_lua_CureByType(lua_State* state); int EQ2Emu_lua_CureByControlEffect(lua_State* state); int EQ2Emu_lua_AddSpawnSpellBonus(lua_State* state); +int EQ2Emu_lua_RemoveSpawnSpellBonus(lua_State* state); int EQ2Emu_lua_CancelSpell(lua_State* state); int EQ2Emu_lua_RemoveStealth(lua_State* state); int EQ2Emu_lua_RemoveInvis(lua_State* state); @@ -559,4 +560,7 @@ int EQ2Emu_lua_IsOpen(lua_State* state); int EQ2Emu_lua_MakeRandomInt(lua_State* state); int EQ2Emu_lua_MakeRandomFloat(lua_State* state); + +int EQ2Emu_lua_AddIconValue(lua_State* state); +int EQ2Emu_lua_RemoveIconValue(lua_State* state); #endif \ No newline at end of file diff --git a/EQ2/source/WorldServer/LuaInterface.cpp b/EQ2/source/WorldServer/LuaInterface.cpp index 3393a06fd..ad8757a18 100644 --- a/EQ2/source/WorldServer/LuaInterface.cpp +++ b/EQ2/source/WorldServer/LuaInterface.cpp @@ -1124,6 +1124,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "CureByType", EQ2Emu_lua_CureByType); lua_register(state, "CureByControlEffect", EQ2Emu_lua_CureByControlEffect); lua_register(state, "AddSpawnSpellBonus", EQ2Emu_lua_AddSpawnSpellBonus); + lua_register(state, "RemoveSpawnSpellBonus", EQ2Emu_lua_RemoveSpawnSpellBonus); lua_register(state, "CancelSpell", EQ2Emu_lua_CancelSpell); lua_register(state, "RemoveStealth", EQ2Emu_lua_RemoveStealth); lua_register(state, "RemoveInvis", EQ2Emu_lua_RemoveInvis); @@ -1273,6 +1274,9 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "MakeRandomInt", EQ2Emu_lua_MakeRandomInt); lua_register(state, "MakeRandomFloat", EQ2Emu_lua_MakeRandomFloat); + + lua_register(state, "AddIconValue", EQ2Emu_lua_AddIconValue); + lua_register(state, "RemoveIconValue", EQ2Emu_lua_RemoveIconValue); } void LuaInterface::LogError(const char* error, ...) { diff --git a/EQ2/source/WorldServer/NPC_AI.cpp b/EQ2/source/WorldServer/NPC_AI.cpp index 87cce9a4b..5cc034237 100644 --- a/EQ2/source/WorldServer/NPC_AI.cpp +++ b/EQ2/source/WorldServer/NPC_AI.cpp @@ -212,8 +212,10 @@ void Brain::AddHate(Entity* entity, sint32 hate) { else m_hatelist.insert(std::pair<int32, sint32>(entity->GetID(), hate)); + entity->MHatedBy.lock(); if (entity->HatedBy.count(m_body->GetID()) == 0) entity->HatedBy.insert(m_body->GetID()); + entity->MHatedBy.unlock(); // Unlock the list MHateList.releasewritelock(__FUNCTION__, __LINE__); @@ -227,7 +229,11 @@ void Brain::ClearHate() { for (itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++) { Spawn* spawn = m_body->GetZone()->GetSpawnByID(itr->first); if (spawn && spawn->IsEntity()) - ((Entity*)spawn)->HatedBy.erase(itr->first); + { + ((Entity*)spawn)->MHatedBy.lock(); + ((Entity*)spawn)->HatedBy.erase(m_body->GetID()); + ((Entity*)spawn)->MHatedBy.unlock(); + } } // Clear the list @@ -245,7 +251,9 @@ void Brain::ClearHate(Entity* entity) { // Erase the entity from the hate list m_hatelist.erase(entity->GetID()); + entity->MHatedBy.lock(); entity->HatedBy.erase(m_body->GetID()); + entity->MHatedBy.unlock(); // Unlock the hate list MHateList.releasewritelock(__FUNCTION__, __LINE__); diff --git a/EQ2/source/WorldServer/Player.cpp b/EQ2/source/WorldServer/Player.cpp index 0758fef95..46d15972f 100644 --- a/EQ2/source/WorldServer/Player.cpp +++ b/EQ2/source/WorldServer/Player.cpp @@ -171,8 +171,6 @@ Player::~Player(){ world.RemoveLottoPlayer(GetCharacterID()); safe_delete(info); index_mutex.writelock(__FUNCTION__, __LINE__); - player_spawn_index_map.clear(); - player_spawn_map.clear(); player_spawn_reverse_id_map.clear(); player_removed_spawns.clear(); player_spawn_id_map.clear(); @@ -444,12 +442,8 @@ PacketStruct* PlayerInfo::serialize2(int16 version){ else packet->setDataByName("house_zone", "None"); //packet->setDataByName("account_age_base", 14); - if(player->GetHPRegen() == 0) - player->SetHPRegen((int)(info_struct->get_level()*.75)+(int)(info_struct->get_level()/10)+3); - if(player->GetPowerRegen() == 0) - player->SetPowerRegen(info_struct->get_level()+(int)(info_struct->get_level()/10)+4); - packet->setDataByName("hp_regen", player->GetHPRegen()); - packet->setDataByName("power_regen", player->GetPowerRegen()); + packet->setDataByName("hp_regen", info_struct->get_hp_regen()); + packet->setDataByName("power_regen", info_struct->get_power_regen()); /*packet->setDataByName("unknown11", -1, 0); packet->setDataByName("unknown11", -1, 1); packet->setDataByName("unknown13", 201, 0); @@ -677,12 +671,9 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setDataByName("base_power", player->GetTotalPowerBase()); packet->setDataByName("conc_used", info_struct->get_cur_concentration()); packet->setDataByName("conc_max", info_struct->get_max_concentration()); - if (player->GetHPRegen() == 0) - player->SetHPRegen((int)(info_struct->get_level() * .75) + (int)(info_struct->get_level() / 10) + 1); - if (player->GetPowerRegen() == 0) - player->SetPowerRegen(info_struct->get_level() + (int)(info_struct->get_level() / 10) + 4); - packet->setDataByName("hp_regen", player->GetHPRegen() + player->stats[ITEM_STAT_HPREGEN]); - packet->setDataByName("power_regen", player->GetPowerRegen() + player->stats[ITEM_STAT_MANAREGEN]); + packet->setDataByName("hp_regen", player->GetInfoStruct()->get_hp_regen()); + packet->setDataByName("power_regen", player->GetInfoStruct()->get_power_regen()); + // packet->setDataByName("unknown_1_4a_MJ", 96); //-1// was unknown11 // packet->setDataByName("unknown_1_4b_MJ", 96); //-1 packet->setDataByName("stat_bonus_health", player->CalculateBonusMod());//bonus health and bonus power getting same value? @@ -697,15 +688,19 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setDataByName("mitigation_pct_pvp", 559); // % calculation Mitigation % vs PvP 559 = 55.9%// confirmed DoV packet->setDataByName("toughness", 0);//toughness// confirmed DoV packet->setDataByName("toughness_resist_dmg_pvp", 0);//toughness_resist_dmg_pvp 73 = 7300% // confirmed DoV - packet->setDataByName("avoidance_pct", 0);//avoidance_pct 192 = 19.2% // confirmed DoV - packet->setDataByName("avoidance_base", info_struct->get_avoidance_base()); // confirmed DoV + packet->setDataByName("avoidance_pct", (int16)info_struct->get_avoidance_display()*10.0f);//avoidance_pct 192 = 19.2% // confirmed DoV + packet->setDataByName("avoidance_base", (int16)info_struct->get_avoidance_base()*10.0f); // confirmed DoV packet->setDataByName("avoidance", info_struct->get_cur_avoidance()); // packet->setDataByName("unknown_1096_1_MJ", 90);//unknown_1096_1_MJ packet->setDataByName("base_avoidance_pct", info_struct->get_base_avoidance_pct());// confirmed DoV // packet->setDataByName("unknown_1096_2_MJ", 89);//unknown_1096_2_MJ - packet->setDataByName("parry", info_struct->get_parry_base());// confirmed DoV + float parry_pct = info_struct->get_parry(); // client works off of int16, but we use floats to track the actual x/100% + packet->setDataByName("parry",(int16)(parry_pct*10.0f));// confirmed DoV // packet->setDataByName("unknown_1096_3_MJ", 88);//unknown_1096_3_MJ - packet->setDataByName("block", info_struct->get_block_base());// confirmed DoV + + float block_pct = info_struct->get_block()*10.0f; + + packet->setDataByName("block", (int16)block_pct);// confirmed DoV // packet->setDataByName("unknown_1096_4_MJ", 87);//unknown_1096_4_MJ packet->setDataByName("uncontested_block", info_struct->get_uncontested_block());// confirmed DoV // packet->setDataByName("unknown_1096_5_MJ", 86);//unknown_1096_5_MJ @@ -895,7 +890,9 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setDataByName("crit_chance", info_struct->get_crit_chance());// dov confirmed //unknown_1096_32_MJ packet->setDataByName("crit_bonus", info_struct->get_crit_bonus());// dov confirmed + ((Entity*)player)->MStats.lock(); packet->setDataByName("potency", player->stats[ITEM_STAT_POTENCY]);//info_struct->get_potency);// dov confirmed + ((Entity*)player)->MStats.unlock(); //unknown_1096_33_MJ packet->setDataByName("reuse_speed", info_struct->get_reuse_speed());// dov confirmed packet->setDataByName("recovery_speed", info_struct->get_recovery_speed());// dov confirmed @@ -910,12 +907,14 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal //unknown_1096_37_MJ //toughness_resist_crit_pvp //unknown_1096_38_MJ + ((Entity*)player)->MStats.lock(); packet->setDataByName("durability_mod", player->stats[ITEM_STAT_DURABILITY_MOD]);// dov confirmed packet->setDataByName("durability_add", player->stats[ITEM_STAT_DURABILITY_ADD]);// dov confirmed packet->setDataByName("progress_mod", player->stats[ITEM_STAT_PROGRESS_MOD]);// dov confirmed packet->setDataByName("progress_add", player->stats[ITEM_STAT_PROGRESS_ADD]);// dov confirmed packet->setDataByName("success_mod", player->stats[ITEM_STAT_SUCCESS_MOD]);// dov confirmed packet->setDataByName("crit_success_mod", player->stats[ITEM_STAT_CRIT_SUCCESS_MOD]);// dov confirmed + ((Entity*)player)->MStats.unlock(); //unknown_1096_39_MJ /////GRoup Members @@ -966,6 +965,7 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal + ((Entity*)player)->MStats.lock(); packet->setDataByName("rare_harvest_chance", player->stats[ITEM_STAT_RARE_HARVEST_CHANCE]); packet->setDataByName("max_crafting", player->stats[ITEM_STAT_MAX_CRAFTING]); packet->setDataByName("component_refund", player->stats[ITEM_STAT_COMPONENT_REFUND]); @@ -976,6 +976,7 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setDataByName("ex_progress_mod", player->stats[ITEM_STAT_EX_PROGRESS_MOD]); packet->setDataByName("ex_progress_add", player->stats[ITEM_STAT_EX_PROGRESS_ADD]); packet->setDataByName("ex_success_mod", player->stats[ITEM_STAT_EX_SUCCESS_MOD]); + ((Entity*)player)->MStats.unlock(); packet->setDataByName("flurry", info_struct->get_flurry()); packet->setDataByName("unknown153", 153); @@ -3010,8 +3011,6 @@ SpellEffects* Player::GetSpellEffects() { void Player::ClearEverything(){ index_mutex.writelock(__FUNCTION__, __LINE__); player_removed_spawns.clear(); - player_spawn_map.clear(); - player_spawn_index_map.clear(); player_spawn_id_map.clear(); player_spawn_reverse_id_map.clear(); index_mutex.releasewritelock(__FUNCTION__, __LINE__); @@ -3524,13 +3523,22 @@ void Player::InCombat(bool val, bool range) { else GetInfoStruct()->set_flags(GetInfoStruct()->get_flags() & ~(1 << (range?CF_RANGED_AUTO_ATTACK:CF_AUTO_ATTACK))); + bool changeCombatState = false; + + if((in_combat && !val) || (!in_combat && val)) + changeCombatState = true; + in_combat = val; if(in_combat) AddIconValue(64); else RemoveIconValue(64); + if(changeCombatState) + SetRegenValues(GetInfoStruct()->get_effective_level()); + charsheet_changed = true; + info_changed = true; } void Player::SetCharSheetChanged(bool val){ @@ -3821,7 +3829,9 @@ int32 Player::GetTSXP() { } bool Player::AddXP(int32 xp_amount){ + MStats.lock(); xp_amount += ((xp_amount) * stats[ITEM_STAT_COMBATEXPMOD]) / 100; + MStats.unlock(); float current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100; float miniding_min_percent = ((int)(current_xp_percent/10)+1)*10; @@ -3845,7 +3855,9 @@ bool Player::AddXP(int32 xp_amount){ } bool Player::AddTSXP(int32 xp_amount){ + MStats.lock(); xp_amount += ((xp_amount)*stats[ITEM_STAT_TRADESKILLEXPMOD]) / 100; + MStats.unlock(); float current_xp_percent = ((float)GetTSXP()/(float)GetNeededTSXP())*100; float miniding_min_percent = ((int)(current_xp_percent/10)+1)*10; @@ -3901,8 +3913,8 @@ Spawn* Player::GetSpawnByIndex(int16 index){ Spawn* spawn = 0; index_mutex.readlock(__FUNCTION__, __LINE__); - if(player_spawn_map.count(index) > 0) - spawn = player_spawn_map[index]; + if(player_spawn_id_map.count(index) > 0) + spawn = player_spawn_id_map[index]; index_mutex.releasereadlock(__FUNCTION__, __LINE__); return spawn; @@ -3912,8 +3924,8 @@ int16 Player::GetIndexForSpawn(Spawn* spawn) { int16 val = 0; index_mutex.readlock(__FUNCTION__, __LINE__); - if(player_spawn_index_map.count(spawn) > 0) - val = player_spawn_index_map[spawn]; + if(player_spawn_reverse_id_map.count(spawn) > 0) + val = player_spawn_reverse_id_map[spawn]; index_mutex.releasereadlock(__FUNCTION__, __LINE__); return val; @@ -3942,11 +3954,11 @@ void Player::RemoveSpawn(Spawn* spawn) player_removed_spawns[spawn] = 1; - if (player_spawn_index_map[spawn] && player_spawn_map.count(player_spawn_index_map[spawn]) > 0) - player_spawn_map.erase(player_spawn_index_map[spawn]); + if (player_spawn_reverse_id_map[spawn] && player_spawn_id_map.count(player_spawn_reverse_id_map[spawn]) > 0) + player_spawn_id_map.erase(player_spawn_reverse_id_map[spawn]); - if (player_spawn_index_map.count(spawn) > 0) - player_spawn_index_map.erase(spawn); + if (player_spawn_reverse_id_map.count(spawn) > 0) + player_spawn_reverse_id_map.erase(spawn); if (player_spawn_id_map.count(spawn->GetID()) && player_spawn_id_map[spawn->GetID()] == spawn) player_spawn_id_map.erase(spawn->GetID()); @@ -5019,8 +5031,11 @@ PlayerItemList* Player::GetPlayerItemList(){ return &item_list; } -void Player::ResetSavedSpawns(){ +void Player::ResetRemovedSpawns(){ player_removed_spawns.clear(); +} +void Player::ResetSavedSpawns(){ + ResetRemovedSpawns(); vis_mutex.writelock(__FUNCTION__, __LINE__); spawn_vis_packet_list.clear(); @@ -5037,8 +5052,6 @@ void Player::ResetSavedSpawns(){ index_mutex.writelock(__FUNCTION__, __LINE__); player_spawn_reverse_id_map.clear(); player_spawn_id_map.clear(); - player_spawn_map.clear(); - player_spawn_index_map.clear(); index_mutex.releasewritelock(__FUNCTION__, __LINE__); m_playerSpawnQuestsRequired.writelock(__FUNCTION__, __LINE__); @@ -5116,7 +5129,10 @@ bool Player::CheckLevelStatus(int16 new_level) { Skill* Player::GetSkillByName(const char* name, bool check_update){ Skill* ret = skill_list.GetSkillByName(name); if(check_update) - skill_list.CheckSkillIncrease(ret); + { + if(skill_list.CheckSkillIncrease(ret)) + CalculateBonuses(); + } return ret; } @@ -5793,7 +5809,6 @@ void Player::InitXPTable() { void Player::SendQuestRequiredSpawns(int32 quest_id){ bool locked = true; m_playerSpawnQuestsRequired.readlock(__FUNCTION__, __LINE__); - Quest* quest = GetQuest(quest_id); if (player_spawn_quests_required.size() > 0 ) { ZoneServer* zone = GetZone(); Client* client = zone->GetClientBySpawn(this); @@ -6122,4 +6137,22 @@ void Player::UpdateTargetInvisHistory(int32 targetID, bool canSeeStatus) void Player::RemoveTargetInvisHistory(int32 targetID) { target_invis_history.erase(targetID); +} + +void Player::SetSpawnMap(Spawn* spawn) +{ + index_mutex.writelock(__FUNCTION__, __LINE__); + spawn_id += 1; + if (spawn_index == 255) + spawn_index += 1; //just so we dont have to worry about overloading + + int32 tmp_id = spawn_id; + + player_spawn_id_map[tmp_id] = spawn; + + if(player_spawn_reverse_id_map.count(spawn)) + player_spawn_reverse_id_map.erase(spawn); + + player_spawn_reverse_id_map.insert(make_pair(spawn,tmp_id)); + index_mutex.releasewritelock(__FUNCTION__, __LINE__); } \ No newline at end of file diff --git a/EQ2/source/WorldServer/Player.h b/EQ2/source/WorldServer/Player.h index eda211795..f617b77d0 100644 --- a/EQ2/source/WorldServer/Player.h +++ b/EQ2/source/WorldServer/Player.h @@ -599,30 +599,19 @@ public: if (player_spawn_reverse_id_map.count(spawn) > 0) id = player_spawn_reverse_id_map[spawn]; index_mutex.releasereadlock(__FUNCTION__, __LINE__); + return id; } - void SetSpawnMap(Spawn* spawn) + void SetSpawnMap(Spawn* spawn); + + void SetSpawnMapIndex(Spawn* spawn, int32 index) { index_mutex.writelock(__FUNCTION__, __LINE__); - spawn_id += 1; - int32 tmp_id = spawn_id; - player_spawn_id_map[tmp_id] = spawn; - - if(player_spawn_reverse_id_map.count(spawn)) - player_spawn_reverse_id_map.erase(spawn); - - player_spawn_reverse_id_map.insert(make_pair(spawn,tmp_id)); - index_mutex.releasewritelock(__FUNCTION__, __LINE__); - } - - void SetSpawnMapIndex(Spawn* spawn, int16 index) - { - index_mutex.writelock(__FUNCTION__, __LINE__); - if (player_spawn_map.count(index)) - player_spawn_map[index] = spawn; + if (player_spawn_id_map.count(index)) + player_spawn_id_map[index] = spawn; else - player_spawn_map[index] = spawn; + player_spawn_id_map[index] = spawn; index_mutex.releasewritelock(__FUNCTION__, __LINE__); } @@ -636,16 +625,8 @@ public: new_index = spawn_index; - if (player_spawn_index_map.count(spawn)) - player_spawn_index_map.erase(spawn); - - player_spawn_index_map.insert(make_pair(spawn,new_index)); - - if (player_spawn_map.count(new_index)) - player_spawn_map[new_index] = spawn; - else - player_spawn_map.insert(make_pair(new_index, spawn)); - + player_spawn_id_map[new_index] = spawn; + player_spawn_reverse_id_map[spawn] = new_index; index_mutex.releasewritelock(__FUNCTION__, __LINE__); return new_index; @@ -711,7 +692,7 @@ public: void SetGroupInformation(PacketStruct* packet); - + void ResetRemovedSpawns(); void ResetSavedSpawns(); bool IsReturningFromLD(); void SetReturningFromLD(bool val); @@ -960,6 +941,20 @@ public: bool HasGMVision() { return gm_vision; } void SetGMVision(bool val) { gm_vision = val; } + void StopCombat(int8 type=0) { + switch(type) + { + case 2: + SetRangeAttack(false); + InCombat(false, true); + break; + default: + InCombat(false); + InCombat(false, true); + SetRangeAttack(false); + break; + } + } @@ -1092,8 +1087,6 @@ private: bool gm_vision; - map<Spawn*, int16> player_spawn_index_map; - map<int16, Spawn*> player_spawn_map; map<int32, Spawn*> player_spawn_id_map; map<Spawn*, int32> player_spawn_reverse_id_map; map<Spawn*, int8> player_removed_spawns; diff --git a/EQ2/source/WorldServer/Rules/Rules.cpp b/EQ2/source/WorldServer/Rules/Rules.cpp index ccb25a16f..95593bf70 100644 --- a/EQ2/source/WorldServer/Rules/Rules.cpp +++ b/EQ2/source/WorldServer/Rules/Rules.cpp @@ -220,7 +220,7 @@ void RuleManager::Init() RULE_INIT(R_Combat, MaxCombatRange, "4.0"); /* SPAWN */ RULE_INIT(R_Spawn, SpeedMultiplier, "300"); // note: this value was 1280 until 6/1/2009, then was 600 til Sep 2009, when it became 300...? - //RULE_INIT(R_Spawn, SpeedRatio, "0"); // was 1280/7.5 and 600/7.5 until it became 300. + RULE_INIT(R_Spawn, ClassicRegen, "0"); /* TIMER */ diff --git a/EQ2/source/WorldServer/Rules/Rules.h b/EQ2/source/WorldServer/Rules/Rules.h index acd8cb3a8..38dee490c 100644 --- a/EQ2/source/WorldServer/Rules/Rules.h +++ b/EQ2/source/WorldServer/Rules/Rules.h @@ -81,6 +81,7 @@ enum RuleType { /* SPAWN */ SpeedMultiplier, + ClassicRegen, //SpeedRatio, /* UI */ diff --git a/EQ2/source/WorldServer/Spawn.cpp b/EQ2/source/WorldServer/Spawn.cpp index aeabc3fa3..33ae5a8cb 100644 --- a/EQ2/source/WorldServer/Spawn.cpp +++ b/EQ2/source/WorldServer/Spawn.cpp @@ -3950,3 +3950,40 @@ int32 Spawn::GetRegionType(Region_Node* inNode, ZBSP_Node* rootNode) return false; } + +float Spawn::SpawnAngle(Spawn* target, float selfx, float selfz) +{ + if (!target || target == this) + return 0.0f; + + float angle, lengthb, vectorx, vectorz, dotp; + float spx = (target->GetX()); // mob xloc (inverse because eq) + float spz = -(target->GetZ()); // mob yloc + float heading = target->GetHeading(); // mob heading + if (heading < 270) + heading += 90; + else + heading -= 270; + + heading = heading * 3.1415f / 180.0f; // convert to radians + vectorx = spx + (10.0f * std::cos(heading)); // create a vector based on heading + vectorz = spz + (10.0f * std::sin(heading)); // of spawn length 10 + + // length of spawn to player vector + lengthb = (float) std::sqrt(((selfx - spx) * (selfx - spx)) + ((-selfz - spz) * (-selfz - spz))); + + // calculate dot product to get angle + // Handle acos domain errors due to floating point rounding errors + dotp = ((vectorx - spx) * (selfx - spx) + + (vectorz - spz) * (-selfz - spz)) / (10.0f * lengthb); + + if (dotp > 1) + return 0.0f; + else if (dotp < -1) + return 180.0f; + + angle = std::acos(dotp); + angle = angle * 180.0f / 3.1415f; + + return angle; +} \ No newline at end of file diff --git a/EQ2/source/WorldServer/Spawn.h b/EQ2/source/WorldServer/Spawn.h index 5fca5566d..272c1b011 100644 --- a/EQ2/source/WorldServer/Spawn.h +++ b/EQ2/source/WorldServer/Spawn.h @@ -1194,6 +1194,14 @@ public: bool InRegion(Region_Node* inNode, ZBSP_Node* rootNode); int32 GetRegionType(Region_Node* inNode, ZBSP_Node* rootNode); + float SpawnAngle(Spawn* target, float selfx, float selfz); + bool BehindSpawn(Spawn *target, float selfx, float selfz) + { return (!target || target == this) ? false : SpawnAngle(target, selfx, selfz) > 90.0f; } + bool InFrontSpawn(Spawn *target, float selfx, float selfz) + { return (!target || target == this) ? false : SpawnAngle(target, selfx, selfz) < 63.0f; } + bool IsFlankingSpawn(Spawn *target, float selfx, float selfz) + { return (!target || target == this) ? false : SpawnAngle(target, selfx, selfz) > 63.0f; } + std::map<std::map<Region_Node*, ZBSP_Node*>, Region_Status> Regions; Mutex RegionMutex; protected: diff --git a/EQ2/source/WorldServer/Tradeskills/Tradeskills.cpp b/EQ2/source/WorldServer/Tradeskills/Tradeskills.cpp index 005f351a7..b48512677 100644 --- a/EQ2/source/WorldServer/Tradeskills/Tradeskills.cpp +++ b/EQ2/source/WorldServer/Tradeskills/Tradeskills.cpp @@ -102,8 +102,10 @@ void TradeskillMgr::Process() { float crit_fail = m_critFail; // Modify the % chance for success based off of stats + client->GetPlayer()->MStats.lock(); fail -= client->GetPlayer()->stats[ITEM_STAT_SUCCESS_MOD]; success += client->GetPlayer()->stats[ITEM_STAT_SUCCESS_MOD]; + client->GetPlayer()->MStats.unlock(); // add values together for the if crit_success += crit_fail; @@ -157,8 +159,10 @@ void TradeskillMgr::Process() { } // Modify the progress/durability by the players stats + client->GetPlayer()->MStats.lock(); progress += client->GetPlayer()->stats[ITEM_STAT_PROGRESS_ADD]; durability += client->GetPlayer()->stats[ITEM_STAT_DURABILITY_ADD]; + client->GetPlayer()->MStats.unlock(); tradeskill->currentDurability += durability; tradeskill->currentProgress += progress; diff --git a/EQ2/source/WorldServer/Zone/region_map_v1.cpp b/EQ2/source/WorldServer/Zone/region_map_v1.cpp index c5a1275d1..b5c84cea3 100644 --- a/EQ2/source/WorldServer/Zone/region_map_v1.cpp +++ b/EQ2/source/WorldServer/Zone/region_map_v1.cpp @@ -430,9 +430,9 @@ void RegionMapV1::TicRegionsNearSpawn(Spawn *spawn, Client *client) const node->dist, node->x, node->y, node->z, node->regionScriptName.c_str()); WaterRegionType whatWasRegionType = RegionTypeNormal; // default will be 0 - if (BSP_Root->special == -3) + if (BSP_Root->special == SPECIAL_REGION_LAVA_OR_DEATH) whatWasRegionType = RegionTypeLava; // 2 - else if (BSP_Root->special == 1) + else if (BSP_Root->special == SPECIAL_REGION_WATER) whatWasRegionType = RegionTypeWater; // 1 int32 returnValue = 0; @@ -652,10 +652,10 @@ WaterRegionType RegionMapV1::BSPReturnRegionWaterRegion(const Region_Node* regio else if (current_node->left == -2) { switch(current_node->special) { - case -3: + case SPECIAL_REGION_LAVA_OR_DEATH: return(RegionTypeLava); break; - case 1: + case SPECIAL_REGION_WATER: return(RegionTypeWater); break; default: diff --git a/EQ2/source/WorldServer/Zone/region_map_v1.h b/EQ2/source/WorldServer/Zone/region_map_v1.h index 0d090fa44..a9d050eae 100644 --- a/EQ2/source/WorldServer/Zone/region_map_v1.h +++ b/EQ2/source/WorldServer/Zone/region_map_v1.h @@ -7,6 +7,9 @@ class Client; class Spawn; +#define SPECIAL_REGION_LAVA_OR_DEATH 4294967293 +#define SPECIAL_REGION_WATER 1 + #pragma pack(1) typedef struct ZBSP_Node { int32 node_number; diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index 4e01ba97b..99b32090c 100644 --- a/EQ2/source/WorldServer/client.cpp +++ b/EQ2/source/WorldServer/client.cpp @@ -198,6 +198,7 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb rejoin_group_id = 0; lastRegionRemapTime = 0; regionDebugMessaging = false; + client_reloading_zone = false; } Client::~Client() { @@ -632,7 +633,13 @@ void Client::HandlePlayerRevive(int32 point_id) void Client::SendCharInfo() { EQ2Packet* app; + if(IsReloadingZone()) + { + GetPlayer()->ResetRemovedSpawns(); + SetReloadingZone(false); + } + player->SetEquippedItemAppearances(); ClientPacketFunctions::SendCharacterData(this); @@ -663,7 +670,7 @@ void Client::SendCharInfo() { ClientPacketFunctions::SendAbilities(this); ClientPacketFunctions::SendSkillBook(this); - if (!player->IsResurrecting()) { + if (!IsReloadingZone() && !player->IsResurrecting()) { if(GetVersion() > 546) //we will send this later on for 546 and below ClientPacketFunctions::SendUpdateSpellBook(this); } @@ -673,7 +680,7 @@ void Client::SendCharInfo() { ClientPacketFunctions::SendLoginCommandMessages(this); GetCurrentZone()->AddSpawn(player); - + //SendCollectionList(); Guild* guild = player->GetGuild(); if (guild) @@ -1420,8 +1427,11 @@ bool Client::HandlePacket(EQApplicationPacket* app) { }*/ float safe_height = 13.0f; Skill* skill = GetPlayer()->GetSkillByName("Safe Fall", true); + GetPlayer()->MStats.lock(); + float deflectionStat = GetPlayer()->stats[ITEM_STAT_SAFE_FALL]; + GetPlayer()->MStats.unlock(); if (skill) - safe_height += (skill->current_val + 1) / 5; + safe_height += (skill->current_val + 1 + deflectionStat) / 5; if (height > safe_height) { int16 damage = (int16)ceil((height - safe_height) * 125); @@ -4210,8 +4220,6 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) { info->set_magic_base((int16)(new_level * 1.5 + 10)); info->set_divine_base((int16)(new_level * 1.5 + 10)); info->set_poison_base((int16)(new_level * 1.5 + 10)); - GetPlayer()->SetHPRegen((int)(new_level * .75) + (int)(new_level / 10) + 3); - GetPlayer()->SetPowerRegen(new_level + (int)(new_level / 10) + 4); GetPlayer()->GetInfoStruct()->set_poison_base((int16)(new_level * 1.5 + 10)); UpdateTimeStampFlag(LEVEL_UPDATE_FLAG); GetPlayer()->SetCharSheetChanged(true); @@ -8283,7 +8291,7 @@ void Client::InspectPlayer(Player* player_to_inspect) { packet->setDataByName("power_base", player_to_inspect->GetTotalPowerBase()); packet->setDataByName("mitigation", 0); packet->setDataByName("unknown1", 0); - packet->setDataByName("avoidance", 0); + packet->setDataByName("avoidance", player_to_inspect->GetInfoStruct()->get_cur_avoidance()); packet->setDataByName("unknown2", 0); packet->setDataByName("mitigation_percentage", 0); packet->setDataByName("strength", player_to_inspect->GetStr()); diff --git a/EQ2/source/WorldServer/client.h b/EQ2/source/WorldServer/client.h index e7e789372..3ae322424 100644 --- a/EQ2/source/WorldServer/client.h +++ b/EQ2/source/WorldServer/client.h @@ -467,6 +467,9 @@ public: int8 mailType, int32 copper, int32 silver, int32 gold, int32 platinum, int32 item_id, int16 stack_size, int32 time_sent, int32 expire_time); void SendEquipOrInvUpdateBySlot(int8 slot); + + void SetReloadingZone(bool val) { client_reloading_zone = val; } + bool IsReloadingZone() { return client_reloading_zone; } private: void SavePlayerImages(); void SkillChanged(Skill* skill, int16 previous_value, int16 new_value); @@ -576,6 +579,8 @@ private: int32 lastRegionRemapTime; bool regionDebugMessaging; + + bool client_reloading_zone; }; class ClientList { diff --git a/EQ2/source/WorldServer/zoneserver.cpp b/EQ2/source/WorldServer/zoneserver.cpp index a17b7abc4..1a31a165f 100644 --- a/EQ2/source/WorldServer/zoneserver.cpp +++ b/EQ2/source/WorldServer/zoneserver.cpp @@ -96,6 +96,8 @@ extern int errno; // extern volatile bool RunLoops; // never used in the zone server? // extern Classes classes; // never used in the zone server? +#define NO_CATCH 1 + extern WorldDatabase database; extern sint32 numzones; extern ClientList client_list; @@ -1821,7 +1823,7 @@ void ZoneServer::ProcessDrowning(){ Client* client = itr->first; Player* player = client->GetPlayer(); drowning_victims.Get(client) = Timer::GetCurrentTime2() + 2000; - damage = player->GetTotalHP()/20 + player->GetHPRegen(); + damage = player->GetTotalHP()/20 + player->GetInfoStruct()->get_hp_regen(); player->TakeDamage(damage); if(player->GetHP() == 0) dead_list.push_back(client); @@ -3780,7 +3782,7 @@ void ZoneServer::RemoveFromRangeMap(Client* client){ } */ -void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool lock) +void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool lock, bool erase_from_spawn_list) { LogWrite(ZONE__DEBUG, 3, "Zone", "Processing RemoveSpawn function for %s (%i)...", spawn->GetName(),spawn->GetID()); @@ -3807,7 +3809,8 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool spawn_expire_timers.erase(spawn->GetID()); // we will remove the spawn ptr and entry in the spawn_list later.. it is not safe right now (lua? client process? spawn process? etc? too many factors) - AddPendingSpawnRemove(spawn->GetID()); + if(erase_from_spawn_list) + AddPendingSpawnRemove(spawn->GetID()); PacketStruct* packet = 0; int16 packet_version = 0; @@ -4172,7 +4175,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo // Remove hate towards dead from all npc's in the zone ClearHate((Entity*)dead); - + // Check kill and death procs if (killer && dead != killer){ if (dead->IsEntity()) @@ -4525,6 +4528,7 @@ void ZoneServer::SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, in packet->setArrayDataByName("damage_type", damage_type); packet->setArrayDataByName("damage", damage); } + if (!attacker) packet->setSubstructDataByName("header", "attacker", 0xFFFFFFFF); else @@ -4796,6 +4800,11 @@ void ZoneServer::SendZoneSpawns(Client* client){ for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) { Spawn* spawn = itr->second; if (spawn) { + if(spawn == client->GetPlayer() && client->IsReloadingZone()) + { + client->GetPlayer()->SetSpawnMap(spawn); + } + CheckSpawnRange(client, spawn, true); } } @@ -6508,8 +6517,10 @@ void ZoneServer::ResurrectSpawn(Spawn* spawn, Client* client) { if(!no_calcs && caster){ //Potency Mod + ((Entity*)caster)->MStats.lock(); heal_amt *= ((caster->stats[ITEM_STAT_POTENCY] / 100) + 1); power_amt *= ((caster->stats[ITEM_STAT_POTENCY] / 100) + 1); + ((Entity*)caster)->MStats.unlock(); //Ability Mod heal_amt += (int32)min((int32)info->get_ability_modifier(), (int32)(heal_amt / 2)); diff --git a/EQ2/source/WorldServer/zoneserver.h b/EQ2/source/WorldServer/zoneserver.h index 32b266605..f06d56164 100644 --- a/EQ2/source/WorldServer/zoneserver.h +++ b/EQ2/source/WorldServer/zoneserver.h @@ -304,7 +304,7 @@ public: void AddSpawnGroupChance(int32 group_id, float percent); - void RemoveSpawn(Spawn* spawn, bool delete_spawn = true, bool respawn = true, bool lock = true); + void RemoveSpawn(Spawn* spawn, bool delete_spawn = true, bool respawn = true, bool lock = true, bool erase_from_spawn_list = true); void ProcessSpawnLocations(); void SendQuestUpdates(Client* client, Spawn* spawn = 0); @@ -664,6 +664,8 @@ public: void AddPendingSpawnRemove(int32 id); void ProcessSpawnRemovals(); + + bool SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* packet = 0, bool delete_spawn = false); private: #ifndef WIN32 pthread_t ZoneThread; @@ -697,7 +699,6 @@ private: void SaveClients(); // never used outside zone server void CheckSendSpawnToClient(); // never used outside zone server void CheckSendSpawnToClient(Client* client, bool initial_login = false); // never used outside zone server - bool SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* packet = 0, bool delete_spawn = false); // never used outside zone server void CheckRemoveSpawnFromClient(Spawn* spawn); // never used outside zone server void SaveClient(Client* client); // never used outside zone server void ProcessFaction(Spawn* spawn, Client* client); // never used outside zone server