Albireo Stage First Update
Issue #305 - partially implemented cross zone spells. Buffs will cross with you for self, group spells can re-attach if the caster enters the zone first. Pet spells will recreate the pet, but name and other buffs to the pet do not persist cross zone. WORK IN PROGRESS!! Fix #310 - no sale option supported on merchants and saved to database, see additionalfields_mar6_2021.sql Fix #309 - mastery skills all update now and relate to fizzle (ordination, ministration, etc) Fix #308 - fizzle support added RULE_INIT(R_Spells, EnableFizzleSpells, "1"); // enables/disables the 'fizzling' of spells based on can_fizzle in the spells table. This also enables increasing specialized skills for classes based on spells/abilities. RULE_INIT(R_Spells, DefaultFizzleChance, "10.0"); // default percentage x / 100, eg 10% is 10.0 RULE_INIT(R_Spells, FizzleMaxSkill, "1.2"); // 1.0 is 100%, 1.2 is 120%, so you get 120% your max skill against a spell, no fizzle RULE_INIT(R_Spells, FizzleDefaultSkill, ".2"); // offset against MaxSkill to average out to 100%, default of .2f so we don't go over the threshold if no skill Fix #303 - Brokers now use /frombroker instead of /itemsearch to show correct icon (money stack like merchants) Fix #291 - implemented selling for status, no buy back, city merchant type (set merchant_type in spawn to 64) allows selling for status
This commit is contained in:
parent
69ac4a9649
commit
963d40521e
27 changed files with 743 additions and 97 deletions
|
@ -376,12 +376,10 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
|
|||
Skill* skill = nullptr;
|
||||
if(spell->GetSpellData()->resistibility > 0)
|
||||
bonus -= (1 - spell->GetSpellData()->resistibility)*100;
|
||||
skill = master_skill_list.GetSkill(spell->GetSpellData()->mastery_skill);
|
||||
if(skill){
|
||||
skill = GetSkillByName(skill->name.data.c_str(), true);
|
||||
if(skill)
|
||||
bonus += skill->current_val / 25;
|
||||
}
|
||||
|
||||
skill = GetSkillByID(spell->GetSpellData()->mastery_skill, false);
|
||||
if(skill)
|
||||
bonus += skill->current_val / 25;
|
||||
|
||||
int8 hit_result = 0;
|
||||
bool is_tick = false; // if spell is already active, this is a tick
|
||||
|
@ -1103,6 +1101,35 @@ void Entity::AddHate(Entity* attacker, sint32 hate) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Entity::CheckFizzleSpell(LuaSpell* spell) {
|
||||
if(!spell || !rule_manager.GetGlobalRule(R_Spells, EnableFizzleSpells)->GetInt8()
|
||||
|| spell->spell->GetSpellData()->can_fizzle == false)
|
||||
return false;
|
||||
|
||||
float fizzleMaxSkill = rule_manager.GetGlobalRule(R_Spells, FizzleMaxSkill)->GetFloat();
|
||||
float baseFizzle = rule_manager.GetGlobalRule(R_Spells, DefaultFizzleChance)->GetFloat()/100.0f; // 10%
|
||||
float skillObtained = 0.2f; // default of .2f so we don't go over the threshold if no skill
|
||||
Skill* skill = GetSkillByID(spell->spell->GetSpellData()->mastery_skill, false);
|
||||
if(skill && spell->spell->GetSpellData()->min_class_skill_req > 0)
|
||||
{
|
||||
float skillObtained = skill->current_val / spell->spell->GetSpellData()->min_class_skill_req;
|
||||
if(skillObtained > fizzleMaxSkill) // 120% over the skill value
|
||||
{
|
||||
skillObtained = fizzleMaxSkill;
|
||||
}
|
||||
|
||||
baseFizzle = (fizzleMaxSkill - skillObtained) * baseFizzle;
|
||||
|
||||
float totalSuccessChance = 1.0f - baseFizzle;
|
||||
|
||||
float randResult = MakeRandomFloat(0.0f, 1.0f);
|
||||
if(randResult > totalSuccessChance)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Entity::CheckInterruptSpell(Entity* attacker) {
|
||||
if(!IsCasting())
|
||||
return false;
|
||||
|
|
|
@ -4508,7 +4508,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
client->QueuePacket(app);
|
||||
break;
|
||||
}
|
||||
case COMMAND_ITEMSEARCH:{
|
||||
case COMMAND_ITEMSEARCH:
|
||||
case COMMAND_FROMBROKER:{
|
||||
PacketStruct* packet = configReader.getStruct("WS_StartBroker", client->GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()));
|
||||
|
@ -6191,6 +6192,22 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
|||
}
|
||||
}
|
||||
}
|
||||
else if(sep->arg[2][0] && strncasecmp("nosale", sep->arg[0], 6) == 0 && sep->IsNumber(1) && sep->IsNumber(2))
|
||||
{
|
||||
sint64 data = strtoull(sep->arg[1], NULL, 0);
|
||||
|
||||
int32 character_item_id = (int32) (data >> 32);
|
||||
int32 item_id = (int32) (data & 0xffffffffL);
|
||||
|
||||
int8 sale_setting = atoi(sep->arg[2]);
|
||||
Item* item = client->GetPlayer()->item_list.GetItemFromUniqueID(character_item_id);
|
||||
if(item)
|
||||
{
|
||||
item->no_sale = sale_setting;
|
||||
item->save_needed = true;
|
||||
client->SendSellMerchantList();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage: /inventory {destroy|move|equip|unequip|swap_equip|pop} {item_id} [to_slot] [bag_id]");
|
||||
|
|
|
@ -894,6 +894,8 @@ private:
|
|||
|
||||
#define COMMAND_CRAFTITEM 526
|
||||
|
||||
#define COMMAND_FROMBROKER 527
|
||||
|
||||
|
||||
#define GET_AA_XML 750
|
||||
#define ADD_AA 751
|
||||
|
|
|
@ -105,7 +105,8 @@ Entity::~Entity(){
|
|||
for (itr4 = immunities.begin(); itr4 != immunities.end(); itr4++)
|
||||
safe_delete(itr4->second);
|
||||
immunities.clear();
|
||||
DeleteSpellEffects();
|
||||
if(!IsPlayer())
|
||||
DeleteSpellEffects();
|
||||
}
|
||||
|
||||
void Entity::DeleteSpellEffects()
|
||||
|
@ -116,11 +117,16 @@ void Entity::DeleteSpellEffects()
|
|||
if(i<30){
|
||||
if(GetInfoStruct()->maintained_effects[i].spell_id != 0xFFFFFFFF)
|
||||
{
|
||||
lua_interface->RemoveSpell(GetInfoStruct()->maintained_effects[i].spell);
|
||||
if (IsPlayer())
|
||||
GetInfoStruct()->maintained_effects[i].icon = 0xFFFF;
|
||||
if(deletedPtrs.find(GetInfoStruct()->spell_effects[i].spell) == deletedPtrs.end())
|
||||
{
|
||||
lua_interface->RemoveSpell(GetInfoStruct()->maintained_effects[i].spell, IsPlayer() ? false: true);
|
||||
if (IsPlayer())
|
||||
GetInfoStruct()->maintained_effects[i].icon = 0xFFFF;
|
||||
|
||||
deletedPtrs[GetInfoStruct()->maintained_effects[i].spell] = true;
|
||||
deletedPtrs[GetInfoStruct()->maintained_effects[i].spell] = true;
|
||||
}
|
||||
|
||||
GetInfoStruct()->maintained_effects[i].spell_id = 0xFFFFFFFF;
|
||||
GetInfoStruct()->maintained_effects[i].spell = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -128,10 +134,11 @@ void Entity::DeleteSpellEffects()
|
|||
{
|
||||
if(deletedPtrs.find(GetInfoStruct()->spell_effects[i].spell) == deletedPtrs.end())
|
||||
{
|
||||
lua_interface->RemoveSpell(GetInfoStruct()->spell_effects[i].spell);
|
||||
lua_interface->RemoveSpell(GetInfoStruct()->spell_effects[i].spell, IsPlayer() ? false: true);
|
||||
deletedPtrs[GetInfoStruct()->spell_effects[i].spell] = true;
|
||||
GetInfoStruct()->spell_effects[i].spell = nullptr;
|
||||
}
|
||||
GetInfoStruct()->spell_effects[i].spell_id = 0xFFFFFFFF;
|
||||
GetInfoStruct()->spell_effects[i].spell = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -999,7 +1006,7 @@ SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer
|
|||
}
|
||||
|
||||
LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWithOtherPlayers) {
|
||||
if(!spell->spell->GetSpellData()->linked_timer)
|
||||
if(!spell->spell->GetSpellData()->linked_timer && !spell->spell->GetSpellData()->type_group_spell_id)
|
||||
return nullptr;
|
||||
LuaSpell* ret = nullptr;
|
||||
InfoStruct* info = GetInfoStruct();
|
||||
|
@ -1040,6 +1047,11 @@ Skill* Entity::GetSkillByName(const char* name, bool check_update){
|
|||
return 0;
|
||||
}
|
||||
|
||||
Skill* Entity::GetSkillByID(int32 id, bool check_update){
|
||||
LogWrite(MISC__TODO, 1, "TODO", "This does nothing... yet...\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
float Entity::GetMaxSpeed(){
|
||||
return max_speed;
|
||||
}
|
||||
|
@ -2165,7 +2177,7 @@ vector<DetrimentalEffects>* Entity::GetDetrimentalSpellEffects() {
|
|||
return &detrimental_spell_effects;
|
||||
}
|
||||
|
||||
void Entity::AddDetrimentalSpell(LuaSpell* luaspell){
|
||||
void Entity::AddDetrimentalSpell(LuaSpell* luaspell, int32 override_expire_timestamp){
|
||||
if(!luaspell || !luaspell->caster)
|
||||
return;
|
||||
|
||||
|
@ -2183,6 +2195,8 @@ void Entity::AddDetrimentalSpell(LuaSpell* luaspell){
|
|||
new_det.spell = luaspell;
|
||||
if (spell->GetSpellData()->duration_until_cancel)
|
||||
new_det.expire_timestamp = 0xFFFFFFFF;
|
||||
else if(override_expire_timestamp)
|
||||
new_det.expire_timestamp = override_expire_timestamp;
|
||||
else
|
||||
new_det.expire_timestamp = Timer::GetCurrentTime2() + (spell->GetSpellDuration()*100);
|
||||
new_det.icon = data->icon;
|
||||
|
|
|
@ -1096,7 +1096,7 @@ public:
|
|||
virtual bool HasActiveMaintainedSpell(Spell* spell, Spawn* target);
|
||||
virtual bool HasActiveSpellEffect(Spell* spell, Spawn* target);
|
||||
virtual void AddSkillBonus(int32 spell_id, int32 skill_id, float value);
|
||||
void AddDetrimentalSpell(LuaSpell* spell);
|
||||
void AddDetrimentalSpell(LuaSpell* spell, int32 override_expire_timestamp = 0);
|
||||
DetrimentalEffects* GetDetrimentalEffect(int32 spell_id, Entity* caster);
|
||||
virtual MaintainedEffects* GetMaintainedSpell(int32 spell_id);
|
||||
void RemoveDetrimentalSpell(LuaSpell* spell);
|
||||
|
@ -1208,6 +1208,7 @@ public:
|
|||
void ChangeSecondaryWeapon();
|
||||
void ChangeRangedWeapon();
|
||||
virtual Skill* GetSkillByName(const char* name, bool check_update = false);
|
||||
virtual Skill* GetSkillByID(int32 id, bool check_update = false);
|
||||
bool AttackAllowed(Entity* target, float distance = 0, bool range_attack = false);
|
||||
bool PrimaryWeaponReady();
|
||||
bool SecondaryWeaponReady();
|
||||
|
@ -1223,6 +1224,7 @@ public:
|
|||
bool DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod = 0, bool is_tick = false, bool no_damage_calcs = false, bool ignore_attacker = false);
|
||||
void AddHate(Entity* attacker, sint32 hate);
|
||||
bool CheckInterruptSpell(Entity* attacker);
|
||||
bool CheckFizzleSpell(LuaSpell* spell);
|
||||
void KillSpawn(Spawn* dead, int8 damage_type = 0, int16 kill_blow_type = 0);
|
||||
void HandleDeathExperienceDebt(Spawn* killer);
|
||||
void SetAttackDelay(bool primary = false, bool ranged = false);
|
||||
|
@ -1668,6 +1670,9 @@ 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;
|
||||
|
||||
Mutex MMaintainedSpells;
|
||||
Mutex MSpellEffects;
|
||||
protected:
|
||||
bool in_combat;
|
||||
int8 m_petType;
|
||||
|
@ -1676,7 +1681,6 @@ protected:
|
|||
int32 m_petSpellID;
|
||||
int8 m_petSpellTier;
|
||||
bool m_petDismissing;
|
||||
|
||||
private:
|
||||
MutexList<BonusValues*> bonus_list;
|
||||
map<int8, MutexList<LuaSpell*>*> control_effects;
|
||||
|
@ -1695,8 +1699,6 @@ private:
|
|||
CombatData ranged_combat_data;
|
||||
map<int8, int8> det_count_list;
|
||||
Mutex MDetriments;
|
||||
Mutex MMaintainedSpells;
|
||||
Mutex MSpellEffects;
|
||||
vector<DetrimentalEffects> detrimental_spell_effects;
|
||||
// Pointers for the 4 types of pets (Summon, Charm, Deity, Cosmetic)
|
||||
Entity* pet;
|
||||
|
|
|
@ -818,6 +818,8 @@ Item::Item(){
|
|||
memset(&details, 0, sizeof(ItemCore));
|
||||
memset(&generic_info, 0, sizeof(Generic_Info));
|
||||
generic_info.condition = 100;
|
||||
no_buy_back = false;
|
||||
no_sale = false;
|
||||
}
|
||||
|
||||
Item::Item(Item* in_item){
|
||||
|
@ -833,6 +835,8 @@ Item::Item(Item* in_item){
|
|||
generic_info.condition = 100;
|
||||
spell_id = in_item->spell_id;
|
||||
spell_tier = in_item->spell_tier;
|
||||
no_buy_back = in_item->no_buy_back;
|
||||
no_sale = in_item->no_sale;
|
||||
}
|
||||
|
||||
Item::~Item(){
|
||||
|
|
|
@ -594,6 +594,11 @@ extern MasterItemList master_item_list;
|
|||
#define ITEM_STAT_UNCONTESTED_DODGE 852
|
||||
#define ITEM_STAT_UNCONTESTED_RIPOSTE 853
|
||||
|
||||
#define DISPLAY_FLAG_RED_TEXT 1 // old clients
|
||||
#define DISPLAY_FLAG_NO_GUILD_STATUS 8
|
||||
#define DISPLAY_FLAG_NO_BUYBACK 16
|
||||
#define DISPLAY_FLAG_NOT_FOR_SALE 64
|
||||
|
||||
#pragma pack(1)
|
||||
struct ItemStatsValues{
|
||||
sint16 str;
|
||||
|
@ -884,6 +889,8 @@ public:
|
|||
int32 spell_id;
|
||||
int8 spell_tier;
|
||||
string item_script;
|
||||
bool no_buy_back;
|
||||
bool no_sale;
|
||||
void AddEffect(string effect, int8 percentage, int8 subbulletflag);
|
||||
void AddBookPage(int8 page, string page_text,int8 valign, int8 halign);
|
||||
int32 GetMaxSellValue();
|
||||
|
|
|
@ -283,6 +283,8 @@ void WorldDatabase::LoadDataFromRow(DatabaseResult* result, Item* item)
|
|||
|
||||
item->generic_info.harvest = result->GetInt8Str("harvest");
|
||||
item->generic_info.body_drop = result->GetInt8Str("body_drop");
|
||||
|
||||
item->no_buy_back = (result->GetInt8Str("no_buy_back") == 1);
|
||||
}
|
||||
|
||||
int32 WorldDatabase::LoadSkillItems()
|
||||
|
@ -1105,9 +1107,9 @@ void WorldDatabase::SaveItem(int32 account_id, int32 char_id, Item* item, const
|
|||
LogWrite(ITEM__DEBUG, 1, "Items", "Saving ItemID: %u (Type: %s) for account: %u, player: %u", item->details.item_id, type, account_id, char_id);
|
||||
|
||||
Query query;
|
||||
string update_item = string("REPLACE INTO character_items (id, type, char_id, slot, item_id, creator,adorn0,adorn1,adorn2, condition_, attuned, bag_id, count, max_sell_value, account_id, login_checksum) VALUES (%u, '%s', %u, %i, %u, '%s', %i, %i, %i, %i, %i, %i, %i, %u, %u, 0)");
|
||||
string update_item = string("REPLACE INTO character_items (id, type, char_id, slot, item_id, creator,adorn0,adorn1,adorn2, condition_, attuned, bag_id, count, max_sell_value, no_sale, account_id, login_checksum) VALUES (%u, '%s', %u, %i, %u, '%s', %i, %i, %i, %i, %i, %i, %i, %u, %u, %u, 0)");
|
||||
query.AddQueryAsync(char_id, this, Q_REPLACE, update_item.c_str(), item->details.unique_id, type, char_id, item->details.slot_id, item->details.item_id,
|
||||
getSafeEscapeString(item->creator.c_str()).c_str(),item->adorn0,item->adorn1,item->adorn2, item->generic_info.condition, item->CheckFlag(ATTUNED) ? 1 : 0, item->details.inv_slot_id, item->details.count, item->GetMaxSellValue(), account_id);
|
||||
getSafeEscapeString(item->creator.c_str()).c_str(),item->adorn0,item->adorn1,item->adorn2, item->generic_info.condition, item->CheckFlag(ATTUNED) ? 1 : 0, item->details.inv_slot_id, item->details.count, item->GetMaxSellValue(), item->no_sale, account_id);
|
||||
}
|
||||
|
||||
void WorldDatabase::DeleteItem(int32 char_id, Item* item, const char* type)
|
||||
|
@ -1137,7 +1139,7 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
|||
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type, id, slot, item_id, creator,adorn0,adorn1,adorn2, condition_, attuned, bag_id, count, max_sell_value FROM character_items where char_id = %u or (bag_id = -4 and account_id = %u) ORDER BY slot asc", char_id, account_id);
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type, id, slot, item_id, creator,adorn0,adorn1,adorn2, condition_, attuned, bag_id, count, max_sell_value, no_sale FROM character_items where char_id = %u or (bag_id = -4 and account_id = %u) ORDER BY slot asc", char_id, account_id);
|
||||
|
||||
if(result)
|
||||
{
|
||||
|
@ -1180,6 +1182,7 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
|||
item->details.inv_slot_id = atol(row[10]); //bag_id
|
||||
item->details.count = atoi(row[11]); //count
|
||||
item->SetMaxSellValue(atoul(row[12])); //max sell value
|
||||
item->no_sale = (atoul(row[13]) == 1);
|
||||
|
||||
|
||||
if(strncasecmp(row[0], "EQUIPPED", 8)==0)
|
||||
|
|
|
@ -276,6 +276,7 @@ bool LuaInterface::LoadLuaSpell(const char* name) {
|
|||
spell->slot_pos = 0;
|
||||
spell->damage_remaining = 0;
|
||||
spell->effect_bitmask = 0;
|
||||
spell->restored = false;
|
||||
|
||||
MSpells.lock();
|
||||
if (spells.count(lua_script) > 0) {
|
||||
|
@ -530,15 +531,28 @@ bool LuaInterface::LoadRegionScript(string name) {
|
|||
return LoadRegionScript(name.c_str());
|
||||
}
|
||||
|
||||
void LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast, const char* function, SpellScriptTimer* timer, bool passLuaSpell) {
|
||||
std::string LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast, const char* function, SpellScriptTimer* timer, bool passLuaSpell) {
|
||||
std::string functionCalled = string("");
|
||||
if (function)
|
||||
{
|
||||
functionCalled = string(function);
|
||||
lua_getglobal(spell->state, function);
|
||||
}
|
||||
else if (precast)
|
||||
{
|
||||
functionCalled = "precast";
|
||||
lua_getglobal(spell->state, "precast");
|
||||
}
|
||||
else if(first_cast)
|
||||
{
|
||||
functionCalled = "cast";
|
||||
lua_getglobal(spell->state, "cast");
|
||||
}
|
||||
else
|
||||
{
|
||||
functionCalled = "tick";
|
||||
lua_getglobal(spell->state, "tick");
|
||||
}
|
||||
|
||||
if(passLuaSpell)
|
||||
SetSpellValue(spell->state, spell);
|
||||
|
@ -571,6 +585,8 @@ void LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, bool preca
|
|||
else
|
||||
SetSpawnValue(spell->state, 0);
|
||||
}
|
||||
|
||||
return functionCalled;
|
||||
}
|
||||
|
||||
LuaSpell* LuaInterface::GetCurrentSpell(lua_State* state) {
|
||||
|
@ -579,7 +595,7 @@ LuaSpell* LuaInterface::GetCurrentSpell(lua_State* state) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool LuaInterface::CallSpellProcess(LuaSpell* spell, int8 num_parameters) {
|
||||
bool LuaInterface::CallSpellProcess(LuaSpell* spell, int8 num_parameters, std::string customFunction) {
|
||||
if(shutting_down || !spell || !spell->caster)
|
||||
return false;
|
||||
|
||||
|
@ -587,9 +603,9 @@ bool LuaInterface::CallSpellProcess(LuaSpell* spell, int8 num_parameters) {
|
|||
current_spells[spell->state] = spell;
|
||||
MSpells.unlock();
|
||||
if(lua_pcall(spell->state, num_parameters, 0, 0) != 0){
|
||||
LogError("Error running %s", lua_tostring(spell->state, -1));
|
||||
LogError("Error running function '%s' in %s: %s", customFunction.c_str(), spell->spell->GetName(), lua_tostring(spell->state, -1));
|
||||
lua_pop(spell->state, 1);
|
||||
RemoveSpell(spell, false);
|
||||
RemoveSpell(spell, false); // may be in a lock
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -694,7 +710,7 @@ lua_State* LuaInterface::LoadLuaFile(const char* name) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool can_delete, string reason) {
|
||||
void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool can_delete, string reason, bool removing_all_spells) {
|
||||
if(shutting_down)
|
||||
return;
|
||||
|
||||
|
@ -752,7 +768,7 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
|
|||
spell->caster->RemoveMaintainedSpell(spell);
|
||||
|
||||
int8 spell_type = spell->spell->GetSpellData()->spell_type;
|
||||
if(spell->caster->IsPlayer())
|
||||
if(spell->caster->IsPlayer() && !removing_all_spells)
|
||||
{
|
||||
Player* player = (Player*)spell->caster;
|
||||
switch(spell_type)
|
||||
|
@ -1766,6 +1782,7 @@ LuaSpell* LuaInterface::GetSpell(const char* name) {
|
|||
new_spell->caster = 0;
|
||||
new_spell->initial_target = 0;
|
||||
new_spell->spell = 0;
|
||||
new_spell->restored = false;
|
||||
return new_spell;
|
||||
}
|
||||
else{
|
||||
|
|
|
@ -91,6 +91,7 @@ struct LuaSpell{
|
|||
bool had_dmg_remaining;
|
||||
Mutex MSpellTargets;
|
||||
int32 effect_bitmask;
|
||||
bool restored; // restored spell cross zone
|
||||
|
||||
};
|
||||
|
||||
|
@ -190,7 +191,7 @@ public:
|
|||
bool LoadZoneScript(const char* name);
|
||||
bool LoadRegionScript(string name);
|
||||
bool LoadRegionScript(const char* name);
|
||||
void RemoveSpell(LuaSpell* spell, bool call_remove_function = true, bool can_delete = true, string reason = "");
|
||||
void RemoveSpell(LuaSpell* spell, bool call_remove_function = true, bool can_delete = true, string reason = "", bool removing_all_spells = false);
|
||||
Spawn* GetSpawn(lua_State* state, int8 arg_num = 1);
|
||||
Item* GetItem(lua_State* state, int8 arg_num = 1);
|
||||
Quest* GetQuest(lua_State* state, int8 arg_num = 1);
|
||||
|
@ -229,9 +230,9 @@ public:
|
|||
void SetConversationValue(lua_State* state, vector<ConversationOption>* conversation);
|
||||
void SetOptionWindowValue(lua_State* state, vector<OptionWindowOption>* optionWindow);
|
||||
|
||||
void AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast = false, const char* function = 0, SpellScriptTimer* timer = 0, bool passLuaSpell=false);
|
||||
std::string AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast = false, const char* function = 0, SpellScriptTimer* timer = 0, bool passLuaSpell=false);
|
||||
LuaSpell* GetCurrentSpell(lua_State* state);
|
||||
bool CallSpellProcess(LuaSpell* spell, int8 num_parameters);
|
||||
bool CallSpellProcess(LuaSpell* spell, int8 num_parameters, std::string functionCalled);
|
||||
LuaSpell* GetSpell(const char* name);
|
||||
void UseItemScript(const char* name, lua_State* state, bool val);
|
||||
void UseSpawnScript(const char* name, lua_State* state, bool val);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "NPC_AI.h"
|
||||
#include "Appearances.h"
|
||||
#include "SpellProcess.h"
|
||||
#include "Skills.h"
|
||||
|
||||
extern MasterSpellList master_spell_list;
|
||||
extern ConfigReader configReader;
|
||||
|
@ -34,6 +35,7 @@ extern WorldDatabase database;
|
|||
extern World world;
|
||||
extern Races races;
|
||||
extern Appearance master_appearance_list;
|
||||
extern MasterSkillList master_skill_list;
|
||||
|
||||
NPC::NPC(){
|
||||
Initialize();
|
||||
|
@ -668,7 +670,7 @@ void NPC::Randomize(NPC* npc, int32 flags)
|
|||
color1.green = MakeRandomInt(min_val, max_val);
|
||||
color1.blue = MakeRandomInt(min_val, max_val);
|
||||
LogWrite(NPC__DEBUG, 5, "NPCs", "Randomizing Hair Type Highlight - R: %i, G: %i, B: %i", color1.red, color1.green, color1.blue);
|
||||
npc->SetHairHighlightColor(color1);
|
||||
npc->SetHairTypeHighlightColor(color1);
|
||||
}
|
||||
if (flags & RANDOMIZE_SKIN_COLOR) {
|
||||
npc->features.skin_color.red = MakeRandomInt(min_val, max_val);
|
||||
|
@ -704,6 +706,18 @@ Skill* NPC::GetSkillByName(const char* name, bool check_update){
|
|||
return 0;
|
||||
}
|
||||
|
||||
Skill* NPC::GetSkillByID(int32 id, bool check_update){
|
||||
Skill* skill = master_skill_list.GetSkill(id);
|
||||
|
||||
if(skill && skills && skills->count(skill->name.data) > 0){
|
||||
Skill* ret = (*skills)[skill->name.data];
|
||||
if(ret && check_update && ret->current_val < ret->max_val && (rand()%100) >= 90)
|
||||
ret->current_val++;
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NPC::SetAttackType(int8 type){
|
||||
attack_type = type;
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ public:
|
|||
bool CheckSameAppearance(string name, int16 id);
|
||||
void Randomize(NPC* npc, int32 flags);
|
||||
Skill* GetSkillByName(const char* name, bool check_update = false);
|
||||
Skill* GetSkillByID(int32 id, bool check_update = false);
|
||||
void SetAttackType(int8 type);
|
||||
int8 GetAttackType();
|
||||
void SetAIStrategy(int8 strategy);
|
||||
|
|
|
@ -119,8 +119,11 @@ Player::Player(){
|
|||
m_playerSpawnQuestsRequired.SetName("Player::player_spawn_quests_required");
|
||||
m_playerSpawnHistoryRequired.SetName("Player::player_spawn_history_required");
|
||||
gm_vision = false;
|
||||
SetSaveSpellEffects(true);
|
||||
}
|
||||
Player::~Player(){
|
||||
SetSaveSpellEffects(true);
|
||||
DeleteSpellEffects();
|
||||
for(int32 i=0;i<spells.size();i++){
|
||||
safe_delete(spells[i]);
|
||||
}
|
||||
|
@ -5241,6 +5244,16 @@ Skill* Player::GetSkillByName(const char* name, bool check_update){
|
|||
return ret;
|
||||
}
|
||||
|
||||
Skill* Player::GetSkillByID(int32 id, bool check_update){
|
||||
Skill* ret = skill_list.GetSkill(id);
|
||||
if(check_update)
|
||||
{
|
||||
if(skill_list.CheckSkillIncrease(ret))
|
||||
CalculateBonuses();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Player::SetRangeAttack(bool val){
|
||||
range_attack = val;
|
||||
}
|
||||
|
@ -6338,4 +6351,128 @@ NPC* Player::InstantiateSpiritShard(float origX, float origY, float origZ, float
|
|||
npc->SetSpawnScript(script);
|
||||
|
||||
return npc;
|
||||
}
|
||||
|
||||
void Player::SaveSpellEffects()
|
||||
{
|
||||
if(stop_save_spell_effects)
|
||||
{
|
||||
LogWrite(PLAYER__WARNING, 0, "Player", "SaveSpellEffects called while player constructing / deconstructing!");
|
||||
return;
|
||||
}
|
||||
|
||||
SpellProcess* spellProcess = 0;
|
||||
// Get the current zones spell process
|
||||
spellProcess = GetZone()->GetSpellProcess();
|
||||
|
||||
Query savedEffects;
|
||||
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_spell_effects where charid=%u", GetCharacterID());
|
||||
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_spell_effect_targets where caster_char_id=%u", GetCharacterID());
|
||||
InfoStruct* info = GetInfoStruct();
|
||||
MSpellEffects.readlock(__FUNCTION__, __LINE__);
|
||||
MMaintainedSpells.readlock(__FUNCTION__, __LINE__);
|
||||
for(int i = 0; i < 45; i++) {
|
||||
if(info->spell_effects[i].spell_id != 0xFFFFFFFF)
|
||||
{
|
||||
Spawn* spawn = GetZone()->GetSpawnByID(info->spell_effects[i].spell->initial_target);
|
||||
|
||||
int32 target_char_id = 0;
|
||||
if(spawn && spawn->IsPlayer())
|
||||
target_char_id = ((Player*)spawn)->GetCharacterID();
|
||||
|
||||
int32 timestamp = 0xFFFFFFFF;
|
||||
if(!info->spell_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
|
||||
timestamp = info->spell_effects[i].expire_timestamp - Timer::GetCurrentTime2();
|
||||
|
||||
int32 caster_char_id = (info->spell_effects[i].caster && info->spell_effects[i].caster->IsPlayer()) ? ((Player*)info->spell_effects[i].caster)->GetCharacterID() : 0;
|
||||
|
||||
if(caster_char_id == 0)
|
||||
continue;
|
||||
|
||||
Query effectSave;
|
||||
effectSave.AddQueryAsync(GetCharacterID(), &database, Q_INSERT,
|
||||
"insert into character_spell_effects (name, caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, charid, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, custom_function) values ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %f, %u, '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s')",
|
||||
database.getSafeEscapeString(info->spell_effects[i].spell->spell->GetName()).c_str(), caster_char_id,
|
||||
target_char_id, 0 /*no target_type for spell_effects*/, DB_TYPE_SPELLEFFECTS /* db_effect_type for spell_effects */, info->spell_effects[i].spell_id, i, info->spell_effects[i].spell->slot_pos,
|
||||
info->spell_effects[i].icon, info->spell_effects[i].icon_backdrop, 0 /* no conc_used for spell_effects */, info->spell_effects[i].tier,
|
||||
info->spell_effects[i].total_time, timestamp, database.getSafeEscapeString(info->spell_effects[i].spell->file_name.c_str()).c_str(), info->spell_effects[i].spell->spell->IsCopiedSpell(), GetCharacterID(),
|
||||
info->spell_effects[i].spell->damage_remaining, info->spell_effects[i].spell->effect_bitmask, info->spell_effects[i].spell->num_triggers, info->spell_effects[i].spell->had_triggers, info->spell_effects[i].spell->cancel_after_all_triggers,
|
||||
info->spell_effects[i].spell->crit, info->spell_effects[i].spell->last_spellattack_hit, info->spell_effects[i].spell->interrupted, info->spell_effects[i].spell->resisted, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->spell_effects[i].spell).c_str()).c_str());
|
||||
|
||||
/* info->spell_effects[i].spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
|
||||
std::string insertTargets = string("insert into character_spell_effect_targets (caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos) values ");
|
||||
bool firstTarget = true;
|
||||
for (int8 t = 0; t < info->spell_effects[i].spell->targets.size(); t++) {
|
||||
Spawn* spawn = GetZone()->GetSpawnByID(info->spell_effects[i].spell->targets.at(t));
|
||||
if(spawn && spawn->IsPlayer())
|
||||
{
|
||||
if(!firstTarget)
|
||||
insertTargets.append(", ");
|
||||
|
||||
insertTargets.append("(" + caster_char_id + ", " + target_char_id + ", " + "0" + ", " std::to_string(DB_TYPE_SPELLEFFECTS) + ", " + info->spell_effects[i].spell_id + ", " + i + ", " + info->spell_effects[i].spell->slot_pos + ")");
|
||||
firstTarget = false;
|
||||
}
|
||||
}
|
||||
info->spell_effects[i].spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
|
||||
if(!firstTarget)
|
||||
{
|
||||
Query targetSave;
|
||||
targetSave.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, insertTarget.c_str());
|
||||
}*/
|
||||
}
|
||||
if (i < NUM_MAINTAINED_EFFECTS && info->maintained_effects[i].spell_id != 0xFFFFFFFF){
|
||||
Spawn* spawn = GetZone()->GetSpawnByID(info->maintained_effects[i].spell->initial_target);
|
||||
|
||||
int32 target_char_id = 0;
|
||||
if(spawn && spawn->IsPlayer())
|
||||
target_char_id = ((Player*)spawn)->GetCharacterID();
|
||||
|
||||
int32 caster_char_id = (info->maintained_effects[i].spell->caster && info->maintained_effects[i].spell->caster->IsPlayer()) ? ((Player*)info->maintained_effects[i].spell->caster)->GetCharacterID() : 0;
|
||||
|
||||
int32 timestamp = 0xFFFFFFFF;
|
||||
if(!info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
|
||||
timestamp = info->maintained_effects[i].expire_timestamp - Timer::GetCurrentTime2();
|
||||
Query effectSave;
|
||||
effectSave.AddQueryAsync(GetCharacterID(), &database, Q_INSERT,
|
||||
"insert into character_spell_effects (name, caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, charid, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, custom_function) values ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %f, %u, '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s')",
|
||||
database.getSafeEscapeString(info->maintained_effects[i].name).c_str(), caster_char_id, target_char_id, info->maintained_effects[i].target_type, DB_TYPE_MAINTAINEDEFFECTS /* db_effect_type for maintained_effects */, info->maintained_effects[i].spell_id, i, info->maintained_effects[i].slot_pos,
|
||||
info->maintained_effects[i].icon, info->maintained_effects[i].icon_backdrop, info->maintained_effects[i].conc_used, info->maintained_effects[i].tier,
|
||||
info->maintained_effects[i].total_time, timestamp, database.getSafeEscapeString(info->maintained_effects[i].spell->file_name.c_str()).c_str(), info->maintained_effects[i].spell->spell->IsCopiedSpell(), GetCharacterID(),
|
||||
info->maintained_effects[i].spell->damage_remaining, info->maintained_effects[i].spell->effect_bitmask, info->maintained_effects[i].spell->num_triggers, info->maintained_effects[i].spell->had_triggers, info->maintained_effects[i].spell->cancel_after_all_triggers,
|
||||
info->maintained_effects[i].spell->crit, info->maintained_effects[i].spell->last_spellattack_hit, info->maintained_effects[i].spell->interrupted, info->maintained_effects[i].spell->resisted, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->maintained_effects[i].spell).c_str()).c_str());
|
||||
|
||||
info->maintained_effects[i].spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
|
||||
std::string insertTargets = string("insert into character_spell_effect_targets (caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos) values ");
|
||||
bool firstTarget = true;
|
||||
map<int32, bool> targetsInserted;
|
||||
for (int8 t = 0; t < info->maintained_effects[i].spell->targets.size(); t++) {
|
||||
Spawn* spawn = GetZone()->GetSpawnByID(info->maintained_effects[i].spell->targets.at(t));
|
||||
if(spawn && spawn->IsPlayer())
|
||||
{
|
||||
int32 tmpCharID = ((Player*)spawn)->GetCharacterID();
|
||||
|
||||
if(targetsInserted.find(tmpCharID) != targetsInserted.end())
|
||||
continue;
|
||||
|
||||
if(!firstTarget)
|
||||
insertTargets.append(", ");
|
||||
|
||||
targetsInserted.insert(make_pair(tmpCharID, true));
|
||||
|
||||
insertTargets.append("(" + std::to_string(caster_char_id) + ", " + std::to_string(tmpCharID) + ", " + "0" + ", " +
|
||||
std::to_string(DB_TYPE_MAINTAINEDEFFECTS) + ", " + std::to_string(info->maintained_effects[i].spell_id) + ", " + std::to_string(i) +
|
||||
", " + std::to_string(info->maintained_effects[i].spell->slot_pos) + ")");
|
||||
firstTarget = false;
|
||||
}
|
||||
}
|
||||
info->maintained_effects[i].spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
|
||||
if(!firstTarget)
|
||||
{
|
||||
Query targetSave;
|
||||
targetSave.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, insertTargets.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
MMaintainedSpells.releasereadlock(__FUNCTION__, __LINE__);
|
||||
MSpellEffects.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
|
@ -444,6 +444,7 @@ public:
|
|||
PlayerItemList item_list;
|
||||
PlayerSkillList skill_list;
|
||||
Skill* GetSkillByName(const char* name, bool check_update = false);
|
||||
Skill* GetSkillByID(int32 skill_id, bool check_update = false);
|
||||
PlayerSkillList* GetSkills();
|
||||
bool DamageEquippedItems(int8 amount = 10, Client* client = 0);
|
||||
vector<EQ2Packet*> EquipItem(int16 index, int16 version, int8 slot_id = 255);
|
||||
|
@ -963,14 +964,16 @@ public:
|
|||
|
||||
void DismissAllPets();
|
||||
|
||||
void SaveSpellEffects();
|
||||
|
||||
void SetSaveSpellEffects(bool val) { stop_save_spell_effects = val; }
|
||||
AppearanceData SavedApp;
|
||||
CharFeatures SavedFeatures;
|
||||
bool custNPC;
|
||||
Entity* custNPCTarget;
|
||||
// bot index, spawn id
|
||||
map<int32, int32> SpawnedBots;
|
||||
|
||||
bool StopSaveSpellEffects() { return stop_save_spell_effects; }
|
||||
private:
|
||||
bool range_attack;
|
||||
int16 last_movement_activity;
|
||||
|
@ -1091,6 +1094,7 @@ private:
|
|||
EQ2_Color tmp_mount_saddle_color;
|
||||
|
||||
bool gm_vision;
|
||||
bool stop_save_spell_effects;
|
||||
|
||||
map<int32, Spawn*> player_spawn_id_map;
|
||||
map<Spawn*, int32> player_spawn_reverse_id_map;
|
||||
|
|
|
@ -332,6 +332,7 @@ Quest::Quest(int32 in_id){
|
|||
tmp_reward_status = 0;
|
||||
tmp_reward_coins = 0;
|
||||
completed_description = string("");
|
||||
quest_temporary_description = string("");
|
||||
}
|
||||
|
||||
Quest::Quest(Quest* old_quest){
|
||||
|
@ -406,6 +407,7 @@ Quest::Quest(Quest* old_quest){
|
|||
tmp_reward_status = 0;
|
||||
tmp_reward_coins = 0;
|
||||
completed_description = string("");
|
||||
quest_temporary_description = string("");
|
||||
}
|
||||
|
||||
Quest::~Quest(){
|
||||
|
|
|
@ -324,6 +324,10 @@ void RuleManager::Init()
|
|||
RULE_INIT(R_Loot, AllowChestUnlockByTrapTime, "1"); // when set to 1 we will allow unlocking the chest to all players after the trap is triggered (or chest is open) and period ChestUnlockedTimeTrap elapsed
|
||||
|
||||
RULE_INIT(R_Spells, NoInterruptBaseChance, "50");
|
||||
RULE_INIT(R_Spells, EnableFizzleSpells, "1"); // enables/disables the 'fizzling' of spells based on can_fizzle in the spells table. This also enables increasing specialized skills for classes based on spells/abilities.
|
||||
RULE_INIT(R_Spells, DefaultFizzleChance, "10.0"); // default percentage x / 100, eg 10% is 10.0
|
||||
RULE_INIT(R_Spells, FizzleMaxSkill, "1.2"); // 1.0 is 100%, 1.2 is 120%, so you get 120% your max skill against a spell, no fizzle
|
||||
RULE_INIT(R_Spells, FizzleDefaultSkill, ".2"); // offset against MaxSkill to average out to 100%, default of .2f so we don't go over the threshold if no skill
|
||||
|
||||
RULE_INIT(R_Expansion, GlobalExpansionFlag, "0");
|
||||
RULE_INIT(R_Expansion, GlobalHolidayFlag, "0");
|
||||
|
|
|
@ -173,6 +173,10 @@ enum RuleType {
|
|||
|
||||
/* SPELLS */
|
||||
NoInterruptBaseChance,
|
||||
EnableFizzleSpells,
|
||||
DefaultFizzleChance,
|
||||
FizzleMaxSkill,
|
||||
FizzleDefaultSkill,
|
||||
|
||||
/* ZONE TIMERS */
|
||||
RegenTimer,
|
||||
|
|
|
@ -148,6 +148,7 @@
|
|||
#define MERCHANT_TYPE_CRAFTING 8
|
||||
#define MERCHANT_TYPE_REPAIR 16
|
||||
#define MERCHANT_TYPE_LOTTO 32
|
||||
#define MERCHANT_TYPE_CITYMERCHANT 64
|
||||
|
||||
#define INFO_VIS_FLAG_INVIS 1
|
||||
#define INFO_VIS_FLAG_HIDE_HOOD 2
|
||||
|
|
|
@ -131,7 +131,8 @@ void SpellProcess::Process(){
|
|||
// to counter this check to see if the spell has a call_frequency > 0 before we call ProcessSpell()
|
||||
if (spell->spell->GetSpellData()->call_frequency > 0 && !ProcessSpell(spell, false))
|
||||
active_spells.Remove(spell, true, 2000);
|
||||
else if ((spell->timer.GetDuration() * spell->num_calls) >= spell->spell->GetSpellData()->duration1 * 100)
|
||||
else if (((spell->timer.GetDuration() * spell->num_calls) >= spell->spell->GetSpellData()->duration1 * 100) ||
|
||||
(spell->restored && (spell->timer.GetSetAtTrigger() * spell->num_calls) >= spell->spell->GetSpellData()->duration1 * 100))
|
||||
DeleteCasterSpell(spell, "expired");
|
||||
}
|
||||
else
|
||||
|
@ -361,7 +362,7 @@ bool SpellProcess::DeleteCasterSpell(Spawn* caster, Spell* spell, string reason)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason){
|
||||
bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removing_all_spells){
|
||||
bool ret = false;
|
||||
Spawn* target = 0;
|
||||
if(spell) {
|
||||
|
@ -441,7 +442,7 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason){
|
|||
ret = true;
|
||||
}
|
||||
if(lua_interface)
|
||||
lua_interface->RemoveSpell(spell, true, SpellScriptTimersHasSpell(spell), reason);
|
||||
lua_interface->RemoveSpell(spell, true, SpellScriptTimersHasSpell(spell), reason, removing_all_spells);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -453,7 +454,7 @@ bool SpellProcess::ProcessSpell(LuaSpell* spell, bool first_cast, const char* fu
|
|||
LogWrite(SPELL__ERROR, 0, "Spell", "Error: State is NULL! SpellProcess::ProcessSpell for Spell '%s'", (spell->spell != nullptr) ? spell->spell->GetName() : "Unknown");
|
||||
}
|
||||
else if(lua_interface && !spell->interrupted){
|
||||
lua_interface->AddSpawnPointers(spell, first_cast, false, function, timer);
|
||||
std::string functionCall = lua_interface->AddSpawnPointers(spell, first_cast, false, function, timer);
|
||||
vector<LUAData*>* data = spell->spell->GetLUAData();
|
||||
for(int32 i=0;i<data->size();i++){
|
||||
switch(data->at(i)->type){
|
||||
|
@ -479,7 +480,7 @@ bool SpellProcess::ProcessSpell(LuaSpell* spell, bool first_cast, const char* fu
|
|||
}
|
||||
}
|
||||
}
|
||||
ret = lua_interface->CallSpellProcess(spell, 2 + data->size());
|
||||
ret = lua_interface->CallSpellProcess(spell, 2 + data->size(), functionCall);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -629,10 +630,15 @@ bool SpellProcess::CheckPower(LuaSpell* spell){
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SpellProcess::TakePower(LuaSpell* spell){
|
||||
bool SpellProcess::TakePower(LuaSpell* spell, int32 custom_power_req){
|
||||
int16 req = 0;
|
||||
if(spell->caster){
|
||||
req = spell->spell->GetPowerRequired(spell->caster);
|
||||
|
||||
if(custom_power_req)
|
||||
req = custom_power_req;
|
||||
else
|
||||
req = spell->spell->GetPowerRequired(spell->caster);
|
||||
|
||||
if(spell->caster->GetPower() >= req){
|
||||
spell->caster->SetPower(spell->caster->GetPower() - req);
|
||||
if(spell->caster->IsPlayer())
|
||||
|
@ -653,10 +659,13 @@ bool SpellProcess::CheckHP(LuaSpell* spell) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SpellProcess::TakeHP(LuaSpell* spell) {
|
||||
bool SpellProcess::TakeHP(LuaSpell* spell, int32 custom_hp_req) {
|
||||
int16 req = 0;
|
||||
if(spell->caster && spell->caster->IsPlayer()){
|
||||
req = spell->spell->GetHPRequired(spell->caster);
|
||||
if(spell->caster){
|
||||
if(custom_hp_req)
|
||||
req = custom_hp_req;
|
||||
else
|
||||
req = spell->spell->GetHPRequired(spell->caster);
|
||||
if(spell->caster->GetHP() >= req){
|
||||
spell->caster->SetHP(spell->caster->GetHP() - req);
|
||||
if(spell->caster->IsPlayer())
|
||||
|
@ -1022,6 +1031,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
|
|||
if(tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
zone->RemoveTargetFromSpell(conflictSpell, tmpTarget);
|
||||
CheckRemoveTargetFromSpell(conflictSpell);
|
||||
((Entity*)tmpTarget)->RemoveSpellEffect(conflictSpell);
|
||||
if(client)
|
||||
UnlockSpell(client, conflictSpell->spell);
|
||||
|
@ -1369,14 +1379,38 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
|
|||
//Apply casting speed mod
|
||||
spell->ModifyCastTime(caster);
|
||||
|
||||
LockAllSpells(client);
|
||||
|
||||
//cancel stealth effects on cast
|
||||
if(caster->IsStealthed() || caster->IsInvis())
|
||||
caster->CancelAllStealth();
|
||||
|
||||
if(caster && caster->IsEntity() && caster->CheckFizzleSpell(lua_spell))
|
||||
{
|
||||
caster->GetZone()->SendCastSpellPacket(0, target ? target : caster, caster);
|
||||
caster->GetZone()->SendInterruptPacket(caster, lua_spell, true);
|
||||
caster->IsCasting(false);
|
||||
|
||||
if(caster->IsPlayer())
|
||||
{
|
||||
((Player*)caster)->UnlockSpell(spell);
|
||||
SendSpellBookUpdate(((Player*)caster)->GetClient());
|
||||
}
|
||||
|
||||
// fizzle takes half
|
||||
int16 power_req = spell->GetPowerRequired(caster) / 2;
|
||||
TakePower(lua_spell, power_req);
|
||||
|
||||
int16 hp_req = spell->GetHPRequired(caster) / 2;
|
||||
TakeHP(lua_spell, hp_req);
|
||||
|
||||
lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
|
||||
DeleteSpell(lua_spell);
|
||||
return;
|
||||
}
|
||||
|
||||
SendStartCast(lua_spell, client);
|
||||
|
||||
|
||||
LockAllSpells(client);
|
||||
|
||||
if(spell->GetSpellData()->cast_time > 0)
|
||||
{
|
||||
CastTimer* cast_timer = new CastTimer;
|
||||
|
@ -1521,6 +1555,11 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her
|
|||
if (!spell->caster)
|
||||
return true;
|
||||
|
||||
Skill* skill = spell->caster->GetSkillByID(spell->spell->GetSpellData()->mastery_skill, false);
|
||||
// trigger potential skill increase if we succeed in casting a mastery skill and it still has room to grow (against this spell)
|
||||
if(skill && skill->current_val < spell->spell->GetSpellData()->min_class_skill_req)
|
||||
spell->caster->GetSkillByID(spell->spell->GetSpellData()->mastery_skill, true);
|
||||
|
||||
ZoneServer* zone = spell->caster->GetZone();
|
||||
Spawn* target = 0;
|
||||
if(processedSpell){
|
||||
|
@ -1796,7 +1835,7 @@ void SpellProcess::Interrupted(Entity* caster, Spawn* interruptor, int16 error_c
|
|||
}
|
||||
}
|
||||
|
||||
void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast){
|
||||
void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast, bool call_expire_function){
|
||||
int32 i = 0;
|
||||
if(cast_timers.size() > 0){
|
||||
CastTimer* cast_timer = 0;
|
||||
|
@ -1818,8 +1857,8 @@ void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, boo
|
|||
continue;
|
||||
if (spell->spell->GetSpellData()->persist_though_death)
|
||||
continue;
|
||||
if(spell->caster == spawn){
|
||||
DeleteCasterSpell(spell, "expired");
|
||||
if(spell->caster == spawn && call_expire_function){
|
||||
DeleteCasterSpell(spell, "expired", remove_all);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2138,7 +2177,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
|
|||
} // end is player
|
||||
} // end is friendly
|
||||
|
||||
else if (target && data->group_spell > 0 || data->icon_backdrop == 312) // is not friendly, but is a group spell, icon_backdrop 312 is green (encounter AE)
|
||||
else if (target && (data->group_spell > 0 || data->icon_backdrop == 312)) // is not friendly, but is a group spell, icon_backdrop 312 is green (encounter AE)
|
||||
{
|
||||
// target is non-player
|
||||
if (target->IsNPC())
|
||||
|
@ -2421,6 +2460,24 @@ bool SpellProcess::SpellScriptTimersHasSpell(LuaSpell* spell) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string SpellProcess::SpellScriptTimerCustomFunction(LuaSpell* spell) {
|
||||
bool ret = false;
|
||||
std::string val = string("");
|
||||
vector<SpellScriptTimer*>::iterator itr;
|
||||
|
||||
MSpellScriptTimers.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = m_spellScriptList.begin(); itr != m_spellScriptList.end(); itr++) {
|
||||
SpellScriptTimer* timer = *itr;
|
||||
if (timer && timer->spell == spell) {
|
||||
val = string(timer->customFunction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
MSpellScriptTimers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void SpellProcess::ClearSpellScriptTimerList() {
|
||||
vector<SpellScriptTimer*>::iterator itr;
|
||||
MSpellScriptTimers.writelock(__FUNCTION__, __LINE__);
|
||||
|
@ -2625,4 +2682,10 @@ void SpellProcess::SpellCannotStack(ZoneServer* zone, Client* client, Entity* ca
|
|||
zone->SendSpellFailedPacket(client, SPELL_ERROR_TAKE_EFFECT_MOREPOWERFUL);
|
||||
lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
|
||||
DeleteSpell(lua_spell);
|
||||
}
|
||||
|
||||
void SpellProcess::AddActiveSpell(LuaSpell* spell)
|
||||
{
|
||||
if(!active_spells.count(spell))
|
||||
active_spells.Add(spell);
|
||||
}
|
|
@ -185,7 +185,7 @@ public:
|
|||
/// <summary>Checks to see if the caster has enough power and takes it</summary>
|
||||
/// <param name='spell'>LuaSpell to check and take power for (LuaSpell contains the caster)</param>
|
||||
/// <returns>True if caster had enough power</returns>
|
||||
bool TakePower(LuaSpell* spell);
|
||||
bool TakePower(LuaSpell* spell, int32 custom_power_req = 0);
|
||||
|
||||
/// <summary>Check to see if the caster has enough power to cast the spell</summary>
|
||||
/// <param name='spell'>LuaSpell to check (LuaSpell contains the caster)</param>
|
||||
|
@ -195,7 +195,7 @@ public:
|
|||
/// <summary>Check to see if the caster has enough hp and take it</summary>
|
||||
/// <param name='spell'>LuaSpell to check and take hp for (LuaSpell contains the caster)</param>
|
||||
/// <returns>True if the caster had enough hp</returns>
|
||||
bool TakeHP(LuaSpell* spell);
|
||||
bool TakeHP(LuaSpell* spell, int32 custom_hp_req = 0);
|
||||
|
||||
/// <summary>Check to see if the caster has enough hp to cast the spell</summary>
|
||||
/// <param name='spell'>LuaSpell to check (LuaSpell contains the caster)</param>
|
||||
|
@ -259,7 +259,7 @@ public:
|
|||
|
||||
/// <summary>Remove the given spell from the ZpellProcess</summary>
|
||||
/// <param name='spell'>LuaSpell to remove</param>
|
||||
bool DeleteCasterSpell(LuaSpell* spell, string reason="");
|
||||
bool DeleteCasterSpell(LuaSpell* spell, string reason="", bool removing_all_spells = false);
|
||||
|
||||
/// <summary>Interrupt the spell</summary>
|
||||
/// <param name='interrupt'>InterruptStruct that contains all the info</param>
|
||||
|
@ -268,7 +268,7 @@ public:
|
|||
/// <summary>Removes the timers for the given spawn</summary>
|
||||
/// <param name='spawn'>Spawn to remove the timers for</param>
|
||||
/// <param name='remove_all'>Remove all timers (cast, recast, active, queue, interrupted)? If false only cast timers are removed</param>
|
||||
void RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all = false, bool delete_recast = true);
|
||||
void RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all = false, bool delete_recast = true, bool call_expire_function = true);
|
||||
|
||||
/// <summary>Sets the recast timer for the spell </summary>
|
||||
/// <param name='spell'>The spell to set the recast for</param>
|
||||
|
@ -356,6 +356,7 @@ public:
|
|||
|
||||
/// <summary>Checks to see if the list has the spell</summary>
|
||||
bool SpellScriptTimersHasSpell(LuaSpell* spell);
|
||||
std::string SpellScriptTimerCustomFunction(LuaSpell* spell);
|
||||
|
||||
void ClearSpellScriptTimerList();
|
||||
|
||||
|
@ -383,12 +384,11 @@ public:
|
|||
void DeleteSpell(LuaSpell* spell);
|
||||
|
||||
void SpellCannotStack(ZoneServer* zone, Client* client, Entity* caster, LuaSpell* lua_spell, LuaSpell* conflictSpell);
|
||||
private:
|
||||
/// <summary>Sends the spell data to the lua script</summary>
|
||||
/// <param name='spell'>LuaSpell to call the lua script for</param>
|
||||
/// <param name='first_cast'>No clue, not currently used</param>
|
||||
/// <returns>True if the spell script was called successfully</returns>
|
||||
|
||||
bool ProcessSpell(LuaSpell* spell, bool first_cast = true, const char* function = 0, SpellScriptTimer* timer = 0);
|
||||
|
||||
void AddActiveSpell(LuaSpell* spell);
|
||||
private:
|
||||
Mutex MSpellProcess;
|
||||
MutexMap<Entity*,Spell*> spell_que;
|
||||
MutexList<LuaSpell*> active_spells;
|
||||
|
|
|
@ -141,6 +141,7 @@ Spell::Spell(Spell* host_spell)
|
|||
spell->ts_loc_index = host_spell->GetSpellData()->ts_loc_index;
|
||||
spell->type = host_spell->GetSpellData()->type;
|
||||
spell->type_group_spell_id = host_spell->GetSpellData()->type_group_spell_id;
|
||||
spell->can_fizzle = host_spell->GetSpellData()->can_fizzle;
|
||||
}
|
||||
|
||||
heal_spell = host_spell->IsHealSpell();
|
||||
|
@ -1649,6 +1650,11 @@ bool Spell::GetSpellData(lua_State* state, std::string field)
|
|||
lua_interface->SetSInt32Value(state, GetSpellData()->type_group_spell_id);
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "can_fizzle")
|
||||
{
|
||||
lua_interface->SetBooleanValue(state, GetSpellData()->can_fizzle);
|
||||
valSet = true;
|
||||
}
|
||||
|
||||
return valSet;
|
||||
}
|
||||
|
@ -2056,6 +2062,12 @@ bool Spell::SetSpellData(lua_State* state, std::string field, int8 fieldArg)
|
|||
GetSpellData()->type_group_spell_id = type_group_spell_id;
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "can_fizzle")
|
||||
{
|
||||
bool can_fizzle = lua_interface->GetBooleanValue(state, fieldArg);
|
||||
GetSpellData()->can_fizzle = can_fizzle;
|
||||
valSet = true;
|
||||
}
|
||||
|
||||
return valSet;
|
||||
}
|
||||
|
|
|
@ -290,6 +290,7 @@ struct SpellData{
|
|||
int8 spell_type;
|
||||
int32 spell_name_crc;
|
||||
sint32 type_group_spell_id;
|
||||
bool can_fizzle;
|
||||
};
|
||||
class Spell{
|
||||
public:
|
||||
|
|
|
@ -46,6 +46,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "ClientPacketFunctions.h"
|
||||
#include "Zone/ChestTrap.h"
|
||||
#include "../common/version.h"
|
||||
#include "SpellProcess.h"
|
||||
|
||||
extern Classes classes;
|
||||
extern Commands commands;
|
||||
|
@ -4555,7 +4556,7 @@ void WorldDatabase::LoadSpells()
|
|||
int32 total = 0;
|
||||
map<int32, vector<LevelArray*> >* level_data = LoadSpellClasses();
|
||||
|
||||
if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `min_class_skill_req`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `fade_message_others`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc', type_group_spell_id "
|
||||
if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `min_class_skill_req`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `fade_message_others`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc', type_group_spell_id, can_fizzle "
|
||||
"FROM (spells s, spell_tiers st) "
|
||||
"LEFT JOIN spell_ts_ability_index ts "
|
||||
"ON s.`id` = ts.spell_id "
|
||||
|
@ -4654,6 +4655,7 @@ void WorldDatabase::LoadSpells()
|
|||
data->linked_timer = result.GetInt32Str("linked_timer_id");
|
||||
data->spell_name_crc = result.GetInt32Str("spell_name_crc");
|
||||
data->type_group_spell_id = result.GetSInt32Str("type_group_spell_id");
|
||||
data->can_fizzle = ( result.GetInt8Str("can_fizzle") == 1);
|
||||
|
||||
/* Cast Messaging */
|
||||
string message = result.GetStringStr("success_message");
|
||||
|
@ -7348,4 +7350,232 @@ int32 WorldDatabase::CreateSpiritShard(const char* name, int32 level, int8 race,
|
|||
safe_delete_array(lastname_escaped);
|
||||
|
||||
return query.GetLastInsertedID();
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int8 db_spell_type)
|
||||
{
|
||||
SpellProcess* spellProcess = client->GetCurrentZone()->GetSpellProcess();
|
||||
|
||||
if(!spellProcess)
|
||||
return;
|
||||
DatabaseResult result;
|
||||
|
||||
Player* player = client->GetPlayer();
|
||||
// Use -1 on type and subtype to turn the enum into an int and make it a 0 index
|
||||
if (!database_new.Select(&result, "SELECT name, caster_char_id, target_char_id, target_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, custom_function FROM character_spell_effects WHERE charid = %u and db_effect_type = %u", char_id, db_spell_type)) {
|
||||
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
|
||||
return;
|
||||
}
|
||||
InfoStruct* info = player->GetInfoStruct();
|
||||
while (result.Next()) {
|
||||
//result.GetInt8Str
|
||||
char spell_name[60];
|
||||
strncpy(spell_name, result.GetStringStr("name"), 60);
|
||||
|
||||
int32 caster_char_id = result.GetInt32Str("caster_char_id");
|
||||
int32 target_char_id = result.GetInt32Str("target_char_id");
|
||||
int8 target_type = result.GetInt8Str("target_type");
|
||||
int32 spell_id = result.GetInt32Str("spell_id");
|
||||
int32 effect_slot = result.GetInt32Str("effect_slot");
|
||||
int32 slot_pos = result.GetInt32Str("slot_pos");
|
||||
int16 icon = result.GetInt32Str("icon");
|
||||
int16 icon_backdrop = result.GetInt32Str("icon_backdrop");
|
||||
int8 conc_used = result.GetInt32Str("conc_used");
|
||||
int8 tier = result.GetInt32Str("tier");
|
||||
float total_time = result.GetFloatStr("total_time");
|
||||
int32 expire_timestamp = result.GetInt32Str("expire_timestamp");
|
||||
|
||||
string lua_file (result.GetStringStr("lua_file"));
|
||||
|
||||
int8 custom_spell = result.GetInt32Str("custom_spell");
|
||||
|
||||
int32 damage_remaining = result.GetInt32Str("damage_remaining");
|
||||
int32 effect_bitmask = result.GetInt32Str("effect_bitmask");
|
||||
int16 num_triggers = result.GetInt32Str("num_triggers");
|
||||
int8 had_triggers = result.GetInt32Str("had_triggers");
|
||||
int8 cancel_after_triggers = result.GetInt32Str("cancel_after_triggers");
|
||||
int8 crit = result.GetInt32Str("crit");
|
||||
int8 last_spellattack_hit = result.GetInt32Str("last_spellattack_hit");
|
||||
int8 interrupted = result.GetInt32Str("interrupted");
|
||||
int8 resisted = result.GetInt32Str("resisted");
|
||||
std::string custom_function = std::string(result.GetStringStr("custom_function"));
|
||||
LuaSpell* lua_spell = 0;
|
||||
if(custom_spell)
|
||||
{
|
||||
if((lua_spell = lua_interface->GetSpell(lua_file.c_str())) == nullptr)
|
||||
{
|
||||
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), custom lua script not loaded, when attempting to load.", spell_id, tier, lua_file.c_str());
|
||||
lua_interface->LoadLuaSpell(lua_file);
|
||||
}
|
||||
}
|
||||
|
||||
Spell* spell = master_spell_list.GetSpell(spell_id, tier);
|
||||
|
||||
bool isMaintained = false;
|
||||
bool isExistingLuaSpell = false;
|
||||
MaintainedEffects* effect;
|
||||
Client* tmpCaster = nullptr;
|
||||
if(caster_char_id == player->GetCharacterID() && target_char_id == player->GetCharacterID() && (effect = player->GetMaintainedSpell(spell_id)) != nullptr)
|
||||
{
|
||||
safe_delete(lua_spell);
|
||||
lua_spell = effect->spell;
|
||||
if(lua_spell)
|
||||
spell = lua_spell->spell;
|
||||
isMaintained = true;
|
||||
isExistingLuaSpell = true;
|
||||
}
|
||||
else if ( caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr
|
||||
&& tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id)) != nullptr)
|
||||
{
|
||||
if(tmpCaster->GetCurrentZone() != client->GetCurrentZone())
|
||||
{
|
||||
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), characters in different zones, cannot assign maintained spell.", spell_id, tier, lua_file.c_str());
|
||||
safe_delete(lua_spell);
|
||||
continue;
|
||||
}
|
||||
else if(effect->spell)
|
||||
{
|
||||
safe_delete(lua_spell);
|
||||
effect->spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
|
||||
effect->spell->targets.push_back(client->GetPlayer()->GetID());
|
||||
effect->spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
|
||||
lua_spell = effect->spell;
|
||||
spell = effect->spell->spell;
|
||||
isExistingLuaSpell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), something went wrong loading another characters maintained spell.", spell_id, tier, lua_file.c_str());
|
||||
safe_delete(lua_spell);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(custom_spell && lua_spell)
|
||||
{
|
||||
lua_spell->spell = new Spell(spell);
|
||||
|
||||
lua_interface->AddCustomSpell(lua_spell);
|
||||
}
|
||||
else
|
||||
{
|
||||
safe_delete(lua_spell);
|
||||
lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
|
||||
if(lua_spell)
|
||||
lua_spell->spell = spell;
|
||||
}
|
||||
|
||||
if(!lua_spell)
|
||||
{
|
||||
LogWrite(LUA__ERROR, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), lua_spell FAILED, when attempting to load.", spell_id, tier, lua_file.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
SpellScriptTimer* timer = nullptr;
|
||||
if(!isExistingLuaSpell && expire_timestamp != 0xFFFFFFFF && custom_function.size() > 0)
|
||||
{
|
||||
timer = new SpellScriptTimer;
|
||||
|
||||
timer->caster = 0;
|
||||
timer->deleteWhenDone = false;
|
||||
timer->target = 0;
|
||||
|
||||
timer->time = expire_timestamp;
|
||||
timer->customFunction = string(custom_function); // TODO
|
||||
timer->spell = lua_spell;
|
||||
timer->caster = (caster_char_id == player->GetCharacterID()) ? player->GetID() : 0;
|
||||
timer->target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
|
||||
}
|
||||
|
||||
lua_spell->crit = crit;
|
||||
lua_spell->damage_remaining = damage_remaining;
|
||||
lua_spell->effect_bitmask = effect_bitmask;
|
||||
lua_spell->had_dmg_remaining = (damage_remaining>0) ? true : false;
|
||||
lua_spell->had_triggers = had_triggers;
|
||||
lua_spell->initial_target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
|
||||
lua_spell->interrupted = interrupted;
|
||||
lua_spell->last_spellattack_hit = last_spellattack_hit;
|
||||
lua_spell->num_triggers = num_triggers;
|
||||
//lua_spell->num_calls ??
|
||||
//if(target_char_id == player->GetCharacterID())
|
||||
// lua_spell->targets.push_back(player->GetID());
|
||||
|
||||
if(db_spell_type == DB_TYPE_SPELLEFFECTS)
|
||||
{
|
||||
player->MSpellEffects.writelock();
|
||||
info->spell_effects[effect_slot].caster = (caster_char_id == player->GetCharacterID()) ? player : 0;
|
||||
info->spell_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
|
||||
info->spell_effects[effect_slot].icon = icon;
|
||||
info->spell_effects[effect_slot].icon_backdrop = icon_backdrop;
|
||||
info->spell_effects[effect_slot].spell_id = spell_id;
|
||||
info->spell_effects[effect_slot].tier = tier;
|
||||
info->spell_effects[effect_slot].total_time = total_time;
|
||||
info->spell_effects[effect_slot].spell = lua_spell;
|
||||
lua_spell->caster = player; // TODO: get actual player
|
||||
player->MSpellEffects.releasewritelock();
|
||||
|
||||
if(!isMaintained)
|
||||
spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
|
||||
}
|
||||
else if ( db_spell_type == DB_TYPE_MAINTAINEDEFFECTS )
|
||||
{
|
||||
player->MMaintainedSpells.writelock();
|
||||
|
||||
DatabaseResult targets;
|
||||
// Use -1 on type and subtype to turn the enum into an int and make it a 0 index
|
||||
if (database_new.Select(&targets, "SELECT target_char_id, target_type, db_effect_type, spell_id from character_spell_effect_targets where caster_char_id = %u and effect_slot = %u and slot_pos = %u", char_id, effect_slot, slot_pos)) {
|
||||
while (targets.Next()) {
|
||||
int32 target_char = targets.GetInt32Str("target_char_id");
|
||||
int16 target_type = targets.GetInt32Str("target_type");
|
||||
int32 in_spell_id = targets.GetInt32Str("spell_id");
|
||||
if(spell_id != in_spell_id)
|
||||
continue;
|
||||
|
||||
Client* client2 = zone_list.GetClientByCharID(target_char);
|
||||
lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
|
||||
if(client2 && client2->GetPlayer() && client2->GetCurrentZone() == client->GetCurrentZone())
|
||||
lua_spell->targets.push_back(client2->GetPlayer()->GetID());
|
||||
lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
info->maintained_effects[effect_slot].conc_used = conc_used;
|
||||
strncpy(info->maintained_effects[effect_slot].name, spell_name, 60);
|
||||
info->maintained_effects[effect_slot].slot_pos = slot_pos;
|
||||
info->maintained_effects[effect_slot].target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
|
||||
info->maintained_effects[effect_slot].target_type = target_type;
|
||||
info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
|
||||
info->maintained_effects[effect_slot].icon = icon;
|
||||
info->maintained_effects[effect_slot].icon_backdrop = icon_backdrop;
|
||||
info->maintained_effects[effect_slot].spell_id = spell_id;
|
||||
info->maintained_effects[effect_slot].tier = tier;
|
||||
info->maintained_effects[effect_slot].total_time = total_time;
|
||||
info->maintained_effects[effect_slot].spell = lua_spell;
|
||||
lua_spell->caster = player;
|
||||
player->MMaintainedSpells.releasewritelock();
|
||||
|
||||
spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
|
||||
}
|
||||
if(!isExistingLuaSpell && expire_timestamp != 0xFFFFFFFF && !isMaintained)
|
||||
{
|
||||
lua_spell->timer.SetTimer(expire_timestamp);
|
||||
lua_spell->timer.SetAtTrigger(lua_spell->spell->GetSpellData()->duration1 * 100);
|
||||
lua_spell->timer.Start();
|
||||
}
|
||||
|
||||
if(lua_spell->spell->GetSpellData()->det_type)
|
||||
player->AddDetrimentalSpell(lua_spell, expire_timestamp);
|
||||
|
||||
if(timer)
|
||||
spellProcess->AddSpellScriptTimer(timer);
|
||||
|
||||
lua_spell->num_calls = 1;
|
||||
lua_spell->restored = true;
|
||||
if(!lua_spell->resisted && (lua_spell->spell->GetSpellDuration() > 0 || lua_spell->spell->GetSpellData()->duration_until_cancel))
|
||||
spellProcess->AddActiveSpell(lua_spell);
|
||||
|
||||
if (num_triggers > 0)
|
||||
ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, num_triggers, 0);
|
||||
if (damage_remaining > 0)
|
||||
ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, damage_remaining, 1);
|
||||
}
|
||||
}
|
|
@ -111,6 +111,8 @@ using namespace std;
|
|||
#define CHAR_PROPERTY_GMVISION "modify_gmvision"
|
||||
#define CHAR_PROPERTY_LUADEBUG "modify_luadebug"
|
||||
|
||||
#define DB_TYPE_SPELLEFFECTS 1
|
||||
#define DB_TYPE_MAINTAINEDEFFECTS 2
|
||||
|
||||
struct StartingItem{
|
||||
string type;
|
||||
|
@ -605,6 +607,8 @@ public:
|
|||
int16 pos_state, int16 activity_status, char* sub_title, char* prefix_title, char* suffix_title, char* lastname,
|
||||
float x, float y, float z, float heading, int32 gridid, int32 charid, int32 zoneid, int32 instanceid);
|
||||
bool DeleteSpiritShard(int32 id);
|
||||
|
||||
void LoadCharacterSpellEffects(int32 char_id, Client *client, int8 db_spell_type);
|
||||
private:
|
||||
DatabaseNew database_new;
|
||||
map<int32, string> zone_names;
|
||||
|
|
|
@ -805,6 +805,11 @@ void Client::SendCharInfo() {
|
|||
|
||||
if (version > 546)
|
||||
ClientPacketFunctions::SendHousingList(this);
|
||||
|
||||
database.LoadCharacterSpellEffects(GetCharacterID(), this, DB_TYPE_MAINTAINEDEFFECTS);
|
||||
database.LoadCharacterSpellEffects(GetCharacterID(), this, DB_TYPE_SPELLEFFECTS);
|
||||
GetPlayer()->SetSaveSpellEffects(false);
|
||||
GetPlayer()->SetCharSheetChanged(true);
|
||||
}
|
||||
|
||||
void Client::SendZoneSpawns() {
|
||||
|
@ -3509,15 +3514,14 @@ void Client::Message(int8 type, const char* message, ...) {
|
|||
void Client::Disconnect(bool send_disconnect)
|
||||
{
|
||||
LogWrite(CCLIENT__DEBUG, 0, "CClient", "Client Disconnect...");
|
||||
this->Save();
|
||||
this->GetPlayer()->WritePlayerStatistics();
|
||||
|
||||
SetConnected(false);
|
||||
|
||||
if (send_disconnect && getConnection())
|
||||
getConnection()->SendDisconnect(true);
|
||||
|
||||
this->Save();
|
||||
this->GetPlayer()->WritePlayerStatistics();
|
||||
|
||||
eqs = 0;
|
||||
}
|
||||
|
||||
|
@ -3888,7 +3892,8 @@ void Client::Zone(ZoneServer* new_zone, bool set_coords) {
|
|||
|
||||
LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Removing player from fighting...", __FUNCTION__);
|
||||
//GetCurrentZone()->GetCombat()->RemoveHate(player);
|
||||
|
||||
player->SaveSpellEffects();
|
||||
player->SetSaveSpellEffects(true);
|
||||
// Remove players pet from zone if there is one
|
||||
((Entity*)player)->DismissAllPets();
|
||||
|
||||
|
@ -4060,6 +4065,7 @@ void Client::Save() {
|
|||
|
||||
GetPlayer()->SaveHistory();
|
||||
GetPlayer()->SaveLUAHistory();
|
||||
GetPlayer()->SaveSpellEffects();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5641,9 +5647,6 @@ void Client::AcceptQuestReward(Quest* quest, int32 item_id) {
|
|||
else
|
||||
player->GetFactions()->DecreaseFaction(faction_id, (amount * -1));
|
||||
}
|
||||
|
||||
player->GetInfoStruct()->add_status_points(quest->GetStatusPoints());
|
||||
player->SetCharSheetChanged(true);
|
||||
|
||||
if(quest->GetQuestTemporaryState())
|
||||
{
|
||||
|
@ -6408,7 +6411,8 @@ float Client::CalculateSellMultiplier(int32 merchant_id) {
|
|||
void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
|
||||
Spawn* spawn = GetMerchantTransaction();
|
||||
Guild* guild = GetPlayer()->GetGuild();
|
||||
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
|
||||
if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) &&
|
||||
spawn->IsClientInMerchantLevelRange(this)) {
|
||||
int32 total_sell_price = 0;
|
||||
int32 total_status_sell_price = 0; //for status
|
||||
float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID());
|
||||
|
@ -6422,7 +6426,7 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
|
|||
item = player->item_list.GetItemFromUniqueID(unique_id);
|
||||
|
||||
if (!item)
|
||||
player->item_list.GetItemFromID(item_id);
|
||||
item = player->item_list.GetItemFromID(item_id);
|
||||
if (item && master_item) {
|
||||
int32 sell_price = (int32)(master_item->sell_price * multiplier);
|
||||
if (sell_price > item->sell_price)
|
||||
|
@ -6437,19 +6441,40 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
|
|||
status_sell_price = item->sell_status;
|
||||
if (quantity > item->details.count)
|
||||
quantity = item->details.count;
|
||||
if (player->GetGuild()) {
|
||||
total_status_sell_price = status_sell_price * quantity;
|
||||
player->GetInfoStruct()->add_status_points(total_status_sell_price);
|
||||
|
||||
total_status_sell_price = status_sell_price * quantity;
|
||||
|
||||
if(total_status_sell_price > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT)))
|
||||
total_status_sell_price = 0;
|
||||
|
||||
player->GetInfoStruct()->add_status_points(total_status_sell_price);
|
||||
|
||||
int32 guildMaxLevel = 5 + item->details.recommended_level; // client hard codes +5 to the level
|
||||
|
||||
if (player->GetGuild() && guild->GetLevel() < guildMaxLevel) {
|
||||
guild->UpdateGuildStatus(GetPlayer(), total_status_sell_price / 10);
|
||||
guild->SendGuildMemberList();
|
||||
guild->AddEXPCurrent((total_status_sell_price / 10), true);
|
||||
}
|
||||
if (quantity > 1)
|
||||
Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %i %s to %s for %s.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str());
|
||||
{
|
||||
if(total_status_sell_price)
|
||||
Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %i %s to %s for %s and %u Status Points.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str(), status_sell_price);
|
||||
else
|
||||
Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %i %s to %s for %s%s.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str());
|
||||
}
|
||||
else
|
||||
Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %s to %s for %s.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str());
|
||||
{
|
||||
if(total_status_sell_price)
|
||||
Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %s to %s for %s and %u Status Points.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str(), status_sell_price);
|
||||
else
|
||||
Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %s to %s for %s.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str());
|
||||
}
|
||||
player->AddCoins(total_sell_price);
|
||||
AddBuyBack(unique_id, item_id, quantity, sell_price);
|
||||
|
||||
if(!item->no_buy_back && (total_status_sell_price == 0 || (total_status_sell_price > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT)))))
|
||||
AddBuyBack(unique_id, item_id, quantity, sell_price);
|
||||
|
||||
if (quantity >= item->details.count) {
|
||||
database.DeleteItem(GetCharacterID(), item, 0);
|
||||
player->item_list.DestroyItem(item->details.index);
|
||||
|
@ -6461,8 +6486,8 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
|
|||
EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY))
|
||||
SendSellMerchantList();
|
||||
|
||||
SendSellMerchantList();
|
||||
if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK))
|
||||
SendBuyBackList();
|
||||
}
|
||||
|
@ -6472,7 +6497,8 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
|
|||
|
||||
void Client::BuyBack(int32 item_id, int16 quantity) {
|
||||
Spawn* spawn = GetMerchantTransaction();
|
||||
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
|
||||
if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) &&
|
||||
spawn->IsClientInMerchantLevelRange(this)) {
|
||||
deque<BuyBackItem*>::iterator itr;
|
||||
BuyBackItem* buyback = 0;
|
||||
BuyBackItem* closest = 0;
|
||||
|
@ -6523,8 +6549,8 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
|
|||
database.DeleteBuyBack(GetCharacterID(), closest->item_id, closest->quantity, closest->price);
|
||||
safe_delete(closest);
|
||||
}
|
||||
if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY))
|
||||
SendSellMerchantList();
|
||||
|
||||
SendSellMerchantList();
|
||||
if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK))
|
||||
SendBuyBackList();
|
||||
}
|
||||
|
@ -6571,6 +6597,12 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
|
|||
if (quantity > total_available)
|
||||
quantity = total_available;
|
||||
}
|
||||
if(quantity < 1)
|
||||
{
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "Merchant does not have item for purchase (quantity < 1).");
|
||||
return;
|
||||
}
|
||||
|
||||
total_buy_price = sell_price * quantity;
|
||||
item = new Item(master_item);
|
||||
item->details.count = quantity;
|
||||
|
@ -6596,8 +6628,8 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
|
|||
//EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
|
||||
//if(outapp)
|
||||
// QueuePacket(outapp);
|
||||
if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY) && !(spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
SendSellMerchantList();
|
||||
|
||||
SendSellMerchantList();
|
||||
if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
|
||||
PlayLotto(total_buy_price, item->details.item_id);
|
||||
}
|
||||
|
@ -6612,7 +6644,7 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
|
|||
|
||||
// Check if the player has enough status, coins and staion cash to buy the item before checking the items
|
||||
// TODO: need to add support for station cash
|
||||
if (player->GetInfoStruct()->get_status_points() >= ItemInfo->price_status && player->HasCoins(ItemInfo->price_coins * quantity)) {
|
||||
if (player->GetInfoStruct()->get_status_points() >= (ItemInfo->price_status * quantity) && player->HasCoins(ItemInfo->price_coins * quantity)) {
|
||||
// Check items
|
||||
int16 item_quantity = 0;
|
||||
// Default these to true in case price_item_id or price_item2_id was never set
|
||||
|
@ -6651,7 +6683,7 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
|
|||
}
|
||||
// if we have every thing then remove the price and give the item
|
||||
if (hasItem1 && hasItem2) {
|
||||
player->GetInfoStruct()->set_status_points(player->GetInfoStruct()->get_status_points() - ItemInfo->price_status);
|
||||
player->GetInfoStruct()->set_status_points(player->GetInfoStruct()->get_status_points() - (ItemInfo->price_status * quantity));
|
||||
// TODO: station cash
|
||||
|
||||
// The update that would normally be sent after modifing the players inventory is automatically sent in AddItem wich is called later
|
||||
|
@ -6691,8 +6723,8 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
|
|||
world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity);
|
||||
SendBuyMerchantList();
|
||||
}
|
||||
if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY) && !(spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
SendSellMerchantList();
|
||||
|
||||
SendSellMerchantList();
|
||||
if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
|
||||
PlayLotto(total_buy_price, item->details.item_id);
|
||||
|
||||
|
@ -7022,6 +7054,9 @@ void Client::SendBuyMerchantList(bool sell) {
|
|||
|
||||
void Client::SendSellMerchantList(bool sell) {
|
||||
Spawn* spawn = GetMerchantTransaction();
|
||||
if (!spawn || (spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY) || (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
return;
|
||||
|
||||
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
|
||||
map<int32, Item*>* items = player->GetItemList();
|
||||
if (items) {
|
||||
|
@ -7056,10 +7091,32 @@ void Client::SendSellMerchantList(bool sell) {
|
|||
string thename = item->name;
|
||||
|
||||
packet->setArrayDataByName("price", sell_price, i);
|
||||
if (player->GetGuild()) {
|
||||
packet->setArrayDataByName("status", 0, i);//additive to status 2 maybe for server bonus etc
|
||||
packet->setArrayDataByName("status", 0, i);//additive to status 2 maybe for server bonus etc
|
||||
|
||||
int8 dispFlags = 0;
|
||||
|
||||
// only city merchants allow selling for status
|
||||
if(item->sell_status > 0 && (spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT))
|
||||
{
|
||||
packet->setArrayDataByName("status2", item->sell_status, i); //this one is the main status
|
||||
int32 guildMaxLevel = 5 + item->details.recommended_level; // client hard codes +5 to the level
|
||||
if (GetPlayer()->GetGuild() && GetPlayer()->GetGuild()->GetLevel() >= guildMaxLevel) {
|
||||
dispFlags += DISPLAY_FLAG_NO_GUILD_STATUS;
|
||||
}
|
||||
|
||||
}
|
||||
if(item->no_buy_back || (item->sell_status > 0 && (spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT)))
|
||||
{
|
||||
if(GetVersion() < 1188)
|
||||
dispFlags += DISPLAY_FLAG_RED_TEXT; // for older clients it isn't "no buy back", you can either have 1 for red text or 255 for 'not for sale' to be checked
|
||||
else
|
||||
dispFlags += DISPLAY_FLAG_NO_BUYBACK;
|
||||
}
|
||||
|
||||
if(item->no_sale)
|
||||
dispFlags += DISPLAY_FLAG_NOT_FOR_SALE;
|
||||
|
||||
packet->setArrayDataByName("display_flags", dispFlags, i);
|
||||
packet->setArrayDataByName("item_id", item->details.item_id, i);
|
||||
packet->setArrayDataByName("unique_item_id", item->details.unique_id, i);
|
||||
packet->setArrayDataByName("stack_size", item->details.count, i);
|
||||
|
@ -10158,6 +10215,9 @@ void Client::PurgeItem(Item* item)
|
|||
|
||||
void Client::ConsumeFoodDrink(Item* item, int32 slot)
|
||||
{
|
||||
if(GetPlayer()->StopSaveSpellEffects())
|
||||
return;
|
||||
|
||||
if(item) {
|
||||
LogWrite(MISC__INFO, 1, "Command", "ItemID: %u, ItemName: %s ItemCount: %i ", item->details.item_id, item->name.c_str(), item->details.count);
|
||||
if(item->GetItemScript() && lua_interface){
|
||||
|
|
|
@ -3326,6 +3326,21 @@ Client* ZoneServer::GetClientByName(char* name) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
Client* ZoneServer::GetClientByCharID(int32 charid) {
|
||||
Client* ret = 0;
|
||||
vector<Client*>::iterator itr;
|
||||
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
||||
if ((*itr)->GetCharacterID() == charid) {
|
||||
ret = *itr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ZoneServer::AddMovementNPC(Spawn* spawn){
|
||||
if (spawn)
|
||||
movement_spawns.Put(spawn->GetID(), 1);
|
||||
|
@ -4654,7 +4669,7 @@ void ZoneServer::SendSpellFailedPacket(Client* client, int16 error){
|
|||
}
|
||||
}
|
||||
|
||||
void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell){
|
||||
void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool fizzle){
|
||||
if(!interrupted || !spell)
|
||||
return;
|
||||
|
||||
|
@ -4668,7 +4683,7 @@ void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell){
|
|||
client = *client_itr;
|
||||
if(!client || !client->GetPlayer()->WasSentSpawn(interrupted->GetID()) || client->GetPlayer()->WasSpawnRemoved(interrupted))
|
||||
continue;
|
||||
packet = configReader.getStruct("WS_Interrupt", client->GetVersion());
|
||||
packet = configReader.getStruct(fizzle ? "WS_SpellFizzle" : "WS_Interrupt", client->GetVersion());
|
||||
if(packet){
|
||||
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(interrupted));
|
||||
packet->setArrayLengthByName("num_targets", spell->targets.size());
|
||||
|
@ -5691,9 +5706,9 @@ void ZoneServer::RemoveLocationGrids() {
|
|||
location_grids.clear(true);
|
||||
}
|
||||
|
||||
void ZoneServer::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast){
|
||||
void ZoneServer::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast, bool call_expire_function){
|
||||
if(spellProcess)
|
||||
spellProcess->RemoveSpellTimersFromSpawn(spawn, remove_all, delete_recast);
|
||||
spellProcess->RemoveSpellTimersFromSpawn(spawn, remove_all, delete_recast, call_expire_function);
|
||||
}
|
||||
|
||||
void ZoneServer::Interrupted(Entity* caster, Spawn* interruptor, int16 error_code, bool cancel, bool from_movement){
|
||||
|
@ -5730,8 +5745,7 @@ void ZoneServer::RemoveSpawnSupportFunctions(Spawn* spawn) {
|
|||
|
||||
LogWrite(ZONE__DEBUG, 7, "Zone", "Processing RemoveSpawnSupportFunctions...");
|
||||
|
||||
if(spawn->IsEntity())
|
||||
RemoveSpellTimersFromSpawn((Entity*)spawn, true);
|
||||
RemoveSpellTimersFromSpawn((Entity*)spawn, true);
|
||||
|
||||
RemoveDamagedSpawn(spawn);
|
||||
spawn->SendSpawnChanges(false);
|
||||
|
|
|
@ -313,7 +313,7 @@ public:
|
|||
bool CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn = 0, const char* message = 0, bool is_door_open = false);
|
||||
void SendSpawnVisualState(Spawn* spawn, int16 type);
|
||||
void SendSpellFailedPacket(Client* client, int16 error);
|
||||
void SendInterruptPacket(Spawn* interrupted, LuaSpell* spell);
|
||||
void SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool fizzle=false);
|
||||
void HandleEmote(Client* originator, string name);
|
||||
Client* GetClientBySpawn(Spawn* spawn);
|
||||
Spawn* GetSpawnByDatabaseID(int32 id);
|
||||
|
@ -392,7 +392,7 @@ public:
|
|||
void LoadSpellProcess();
|
||||
void LockAllSpells(Player* player);
|
||||
void UnlockAllSpells(Player* player);
|
||||
void RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast = true);
|
||||
void RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast = true, bool call_expire_function = true);
|
||||
void Interrupted(Entity* caster, Spawn* interruptor, int16 error_code, bool cancel = false, bool from_movement = false);
|
||||
Spell* GetSpell(Entity* caster);
|
||||
void ProcessSpell(Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false, LuaSpell* customSpell = 0, int16 custom_cast_time = 0, bool in_heroic_opp = false);
|
||||
|
@ -422,6 +422,7 @@ public:
|
|||
|
||||
void HidePrivateSpawn(Spawn* spawn);
|
||||
Client* GetClientByName(char* name);
|
||||
Client* GetClientByCharID(int32 charid);
|
||||
|
||||
/// <summary>Gets spawns for a true AoE spell</summary>
|
||||
vector<Spawn*> GetAttackableSpawnsByDistance(Spawn* spawn, float distance);
|
||||
|
|
Loading…
Add table
Reference in a new issue