Multiple combat / functionality updates

Fix  - hp / power regen rewrite
Also added power_regen_override and hp_regen_override, when set to 1, you can LUA manually set hp_regen and power_regen to enforce it in code

RULE R_Spawn, ClassicRegen added.  Set to 1 means we do not have both regens when out of combat (eg. out of combat = out of combat regen + in combat regen).  In classic you only received in combat or out of combat regen individually.

Fix  - HatedBy now properly handled, we know when a player/spawn is being hated by other targets

Fix  - Parry/Riposte, Block and Dodge implemented.  Missing Block formula which will become its own issue.

Entity GetInfoStruct/SetInfoStruct, cur_avoidance, parry, parry_base, deflection, block are now floats.  Added sint16 power_regen adn hp_regen, lastly power_regen_override and hp_regen_override are int8's.

Fix  - Implemented stats Crushing, Defense, Deflection, Disruption, Fishing, Focus, Foresting, Gathering, Mining, Parry, Piercing, Safe Fall, Slashing and Trapping

/waypoint command now allows flushing waypoint if you do not have active target

/spawn details [x] supports behind, infront and flank

/craftitem added per EmemJr update

INSERT INTO commands SET TYPE=1,command='craftitem',subcommand='',HANDLER=526,required_status=100;

Crash fix for /add_aa hitting bad spell id

LUA Functions:

RemoveSpawnSpellBonus(spawn) - used in LUASpell script
GetSpell(spell_id, tier, custom_lua_script) - third argument added to setup custom script file

AddIconValue(spawn, value)
RemoveIconValue(spawn, value)

Fix  - evac now works correctly, no ghost spawn of self and you can go into combat and see damage taken/given.

Also simplified the player spawn / index map (had duplicates unneeded)

Fixed region_map_v1 throwing errors on special variable for signed vs unsigned
This commit is contained in:
Image 2021-02-01 21:46:26 -05:00
parent b344a3f894
commit 6f92367102
29 changed files with 973 additions and 238 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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("");
}

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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, ...) {

View file

@ -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__);

View file

@ -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__);
}

View file

@ -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;

View file

@ -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 */

View file

@ -81,6 +81,7 @@ enum RuleType {
/* SPAWN */
SpeedMultiplier,
ClassicRegen,
//SpeedRatio,
/* UI */

View file

@ -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;
}

View file

@ -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:

View file

@ -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;

View file

@ -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:

View file

@ -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;

View file

@ -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());

View file

@ -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 {

View file

@ -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));

View file

@ -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