- Fix #496 ITEM_STAT_ABILITYCASTINGSPEED (664) and ITEM_STAT_SPELLREUSESPEED (665) now supported
- Fix #109 Soulrend does not knock down target (finish spell cast, ZoneServer::SendCastSpellPacket spell_visual is disabled) when no damage applied * alter table character_spell_effects add column has_damaged tinyint(3) unsigned not null default 0 after resisted; - Fix #536, SpellDamage now can drain power. Also Fixed AoM and DoF client WS_HearSiphonSpellDamage - SpellDamage LUA Function now returns a boolean whether damage is dealt (or spell resisted) -- (true is damage/false is no damage or resisted). See Spells/Fighter/Crusader/Shadowknight/Soulrend.lua for a sample. - DamageSpawn LUA Function now returns a boolean whether damage is dealt, updated to allow take_power argument DamageSpawn(Attacker, Victim, victim, type, dmg_type, low_dmg, high_dmg, spell_name, crit_mod, is_tick, no_calcs, ignore_attacker, take_power) - new LUA Functions (both can be used in and outside of a LUA Spell): * SpellDamageExt(Target, type, min_damage, max_damage, crit_mod, no_calcs, override_packet_type, take_power, class_id_reqs...) -- extends support for take_power field (SpellDamage function does not have this and would break other potential spells) * SendHearCast(Spawn, spell_visual_id, cast_time, Caster, Target) -- lets the Spawn see a spell visual on Target. If Caster is not defined, we use Spawn, same goes for Target. - Fixed WS_HearHeal struct for DoF client (displays critically heal vs heal) and proper spell name. DoF does not support absorb or other types. - Support for translation of spell_visual (spells table) aka spellcast.dat from assets vpl. CREATE TABLE `spell_visuals` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(128) DEFAULT NULL, `alternate_spell_visual` varchar(128) DEFAULT NOT NULL '', `spell_visual_id` int(10) unsigned NOT NULL DEFAULT 0, `min_version_range` int(10) unsigned NOT NULL DEFAULT 0, `max_version_range` int(10) unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci; ** MAKE SURE TO GET spell_visuals sql included with update inserted! - Fixed right-click inventory examine (again?) - tested and it is working for main invetory, bags and items in bags! - DoF bags support up to 36 slots now instead of the restricted 20 for "classic" client - DoF and classic equipment restricts to 22 slots instead of trying to send client 25 slots (the additional do not exist) - Fix crash on signs due to lack of nullptr check on entity_command - SetInfoStructString / GetInfoStructString now supports combat_action_state -- can be used without overriding action_state outside of combat.
This commit is contained in:
parent
20795c5a6b
commit
3944d57579
30 changed files with 655 additions and 175 deletions
55
DB/updates/spell_visuals_nov12_2023.sql
Normal file
55
DB/updates/spell_visuals_nov12_2023.sql
Normal file
File diff suppressed because one or more lines are too long
|
@ -147,7 +147,7 @@ bool BotBrain::ProcessSpell(Entity* target, float distance) {
|
|||
Body->GetZone()->ProcessSpell(spell, Body, Body->GetTarget());
|
||||
m_spellRecovery = (int32)(Timer::GetCurrentTime2() + (spell->GetSpellData()->cast_time * 10) + (spell->GetSpellData()->recovery * 10) + 2000);
|
||||
// recast time
|
||||
int32 time = Timer::GetCurrentTime2() + (spell->GetSpellData()->recast * 1000);
|
||||
int32 time = Timer::GetCurrentTime2() + (spell->CalculateRecastTimer(Body));
|
||||
Body->SetRecast(spell, time);
|
||||
|
||||
string str = "I am casting ";
|
||||
|
@ -191,7 +191,7 @@ bool BotBrain::ProcessOutOfCombatSpells() {
|
|||
Body->GetZone()->ProcessSpell(spell, Body, Body->GetTarget());
|
||||
m_spellRecovery = (int32)(Timer::GetCurrentTime2() + (spell->GetSpellData()->cast_time * 10) + (spell->GetSpellData()->recovery * 10) + 2000);
|
||||
// recast time
|
||||
int32 time = Timer::GetCurrentTime2() + (spell->GetSpellData()->recast * 1000);
|
||||
int32 time = Timer::GetCurrentTime2() + (spell->CalculateRecastTimer(Body));
|
||||
Body->SetRecast(spell, time);
|
||||
|
||||
string str = "I am casting ";
|
||||
|
|
|
@ -390,13 +390,18 @@ void Entity::RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo
|
|||
SetAttackDelay(false, true);
|
||||
}
|
||||
|
||||
bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod, bool no_calcs){
|
||||
bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod, bool no_calcs, int8 override_packet_type, bool take_power){
|
||||
if(!victim || !luaspell || !luaspell->spell)
|
||||
return false;
|
||||
|
||||
Spell* spell = luaspell->spell;
|
||||
Skill* skill = nullptr;
|
||||
|
||||
int8 packet_type = DAMAGE_PACKET_TYPE_SPELL_DAMAGE;
|
||||
|
||||
if(override_packet_type) {
|
||||
packet_type = override_packet_type;
|
||||
}
|
||||
|
||||
int8 hit_result = 0;
|
||||
bool is_tick = false; // if spell is already active, this is a tick
|
||||
if (GetZone()->GetSpellProcess()->GetActiveSpells()->count(luaspell)){
|
||||
|
@ -404,14 +409,14 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
|
|||
is_tick = true;
|
||||
}
|
||||
else if(spell->GetSpellData()->type == SPELL_BOOK_TYPE_COMBAT_ART)
|
||||
hit_result = DetermineHit(victim, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, 0, false);
|
||||
hit_result = DetermineHit(victim, packet_type, damage_type, 0, false, luaspell);
|
||||
else
|
||||
hit_result = DetermineHit(victim, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, 0, true, luaspell);
|
||||
hit_result = DetermineHit(victim, packet_type, damage_type, 0, true, luaspell);
|
||||
|
||||
if(victim->IsEntity()) {
|
||||
CheckEncounterState((Entity*)victim);
|
||||
}
|
||||
|
||||
bool successful_hit = true;
|
||||
if(hit_result == DAMAGE_PACKET_RESULT_SUCCESSFUL) {
|
||||
luaspell->last_spellattack_hit = true;
|
||||
//If this spell is a tick and has already crit, force the tick to crit
|
||||
|
@ -421,8 +426,8 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
|
|||
else
|
||||
crit_mod = 2;
|
||||
}
|
||||
if(DamageSpawn((Entity*)victim, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, low_damage, high_damage, spell->GetName(), crit_mod, is_tick, no_calcs, luaspell) && !luaspell->crit)
|
||||
luaspell->crit = true;
|
||||
DamageSpawn((Entity*)victim, packet_type, damage_type, low_damage, high_damage, spell->GetName(), crit_mod, is_tick, no_calcs, false, take_power, luaspell);
|
||||
|
||||
CheckProcs(PROC_TYPE_OFFENSIVE, victim);
|
||||
CheckProcs(PROC_TYPE_MAGICAL_OFFENSIVE, victim);
|
||||
|
||||
|
@ -447,6 +452,7 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
|
|||
}
|
||||
}
|
||||
else {
|
||||
successful_hit = false;
|
||||
if(hit_result == DAMAGE_PACKET_RESULT_RESIST)
|
||||
luaspell->resisted = true;
|
||||
if(victim->IsNPC())
|
||||
|
@ -518,7 +524,7 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return successful_hit;
|
||||
}
|
||||
|
||||
bool Entity::ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32 high_damage, string name, string success_msg, string effect_msg) {
|
||||
|
@ -703,6 +709,9 @@ bool Entity::SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string
|
|||
}
|
||||
|
||||
int8 Entity::DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHitBonus, bool is_caster_spell, LuaSpell* lua_spell){
|
||||
if(lua_spell) {
|
||||
lua_spell->is_damage_spell = true;
|
||||
}
|
||||
if(!victim) {
|
||||
return DAMAGE_PACKET_RESULT_MISS;
|
||||
}
|
||||
|
@ -957,10 +966,16 @@ Skill* Entity::GetSkillByWeaponType(int8 type, int8 damage_type, bool update) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod, bool is_tick, bool no_calcs, bool ignore_attacker, LuaSpell* spell) {
|
||||
bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod, bool is_tick, bool no_calcs, bool ignore_attacker, bool take_power, LuaSpell* spell) {
|
||||
if(spell) {
|
||||
spell->is_damage_spell = true;
|
||||
}
|
||||
|
||||
bool has_damaged = false;
|
||||
|
||||
if(!victim || !victim->Alive() || victim->GetHP() == 0)
|
||||
return false;
|
||||
|
||||
|
||||
int8 hit_result = 0;
|
||||
int16 blow_type = 0;
|
||||
sint32 damage = 0;
|
||||
|
@ -1070,8 +1085,21 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
|
|||
|
||||
if (damage < (sint64)prevDmg)
|
||||
useWards = true;
|
||||
|
||||
victim->TakeDamage(damage);
|
||||
if(damage > 0 && spell) {
|
||||
has_damaged = true;
|
||||
spell->has_damaged = true;
|
||||
}
|
||||
if(take_power) {
|
||||
sint32 curPower = victim->GetPower();
|
||||
if(curPower < damage)
|
||||
curPower = 0;
|
||||
else
|
||||
curPower -= damage;
|
||||
victim->SetPower(curPower);
|
||||
}
|
||||
else {
|
||||
victim->TakeDamage(damage);
|
||||
}
|
||||
victim->CheckProcs(PROC_TYPE_DAMAGED, this);
|
||||
|
||||
if (IsPlayer()) {
|
||||
|
@ -1124,8 +1152,9 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
|
|||
else
|
||||
victim->CheckProcs(PROC_TYPE_PHYSICAL_DEFENSIVE, this);
|
||||
}
|
||||
|
||||
return crit;
|
||||
if(spell)
|
||||
spell->crit = crit;
|
||||
return has_damaged;
|
||||
}
|
||||
|
||||
float Entity::CalculateMitigation(int8 type, int8 damage_type, int16 effective_level_attacker, bool for_pvp) {
|
||||
|
|
|
@ -10628,26 +10628,8 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
|
|||
World::newValue = strtoull(sep->arg[1], NULL, 0);
|
||||
}
|
||||
else if (atoi(sep->arg[0]) == 29 && sep->IsNumber(1)) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
|
||||
if (packet) {
|
||||
int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer());
|
||||
int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer());
|
||||
|
||||
packet->setDataByName("spawn_id", caster_id);
|
||||
packet->setArrayLengthByName("num_targets", 1);
|
||||
packet->setArrayDataByName("target", target_id);
|
||||
packet->setDataByName("num_targets", 1);
|
||||
packet->setDataByName("spell_visual", strtoull(sep->arg[1], NULL, 0)); //result
|
||||
packet->setDataByName("cast_time", sep->IsNumber(2) ? strtof(sep->arg[2], NULL)*.01f : 2500); //delay
|
||||
packet->setDataByName("spell_id", 1);
|
||||
packet->setDataByName("spell_level", 1);
|
||||
packet->setDataByName("spell_tier", 1);
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
|
||||
DumpPacket(outapp);
|
||||
client->QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
client->SendHearCast(client->GetPlayer(), client->GetPlayer()->GetTarget() ? client->GetPlayer()->GetTarget() : client->GetPlayer(),
|
||||
strtoull(sep->arg[1], NULL, 0), atoul(sep->arg[2]));
|
||||
}
|
||||
else if (atoi(sep->arg[0]) == 30) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_UpdateSkillBook", client->GetVersion());
|
||||
|
@ -10677,6 +10659,10 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
|
|||
ClientPacketFunctions::SendServerControlFlags(client, param1, param2, paramval);
|
||||
}
|
||||
}
|
||||
else if (atoi(sep->arg[0]) == 33 && sep->IsNumber(1) && sep->IsNumber(2)) {
|
||||
client->GetCurrentZone()->SendHealPacket(client->GetPlayer(), client->GetPlayer()->GetTarget() ? client->GetPlayer()->GetTarget() : client->GetPlayer(),
|
||||
atoul(sep->arg[1]), atoul(sep->arg[2]), "TestSpell");
|
||||
}
|
||||
}
|
||||
else {
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_ExamineSpellInfo", client->GetVersion());
|
||||
|
|
|
@ -346,6 +346,7 @@ void Entity::MapInfoStruct()
|
|||
get_int8_funcs["reload_player_spells"] = l::bind(&InfoStruct::get_reload_player_spells, &info_struct);
|
||||
|
||||
get_string_funcs["action_state"] = l::bind(&InfoStruct::get_action_state, &info_struct);
|
||||
get_string_funcs["combat_action_state"] = l::bind(&InfoStruct::get_combat_action_state, &info_struct);
|
||||
|
||||
/** SETS **/
|
||||
set_string_funcs["name"] = l::bind(&InfoStruct::set_name, &info_struct, l::_1);
|
||||
|
@ -537,6 +538,7 @@ void Entity::MapInfoStruct()
|
|||
set_int8_funcs["reload_player_spells"] = l::bind(&InfoStruct::set_reload_player_spells, &info_struct, l::_1);
|
||||
|
||||
set_string_funcs["action_state"] = l::bind(&InfoStruct::set_action_state, &info_struct, l::_1);
|
||||
set_string_funcs["combat_action_state"] = l::bind(&InfoStruct::set_combat_action_state, &info_struct, l::_1);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1335,7 +1337,7 @@ void Entity::CalculateBonuses(){
|
|||
info->set_potency(0);
|
||||
info->set_hate_mod(0);
|
||||
info->set_reuse_speed(0);
|
||||
info->set_casting_speed(0);
|
||||
// info->set_casting_speed(0);
|
||||
info->set_recovery_speed(0);
|
||||
info->set_spell_reuse_speed(0);
|
||||
info->set_spell_multi_attack(0);
|
||||
|
@ -2161,7 +2163,7 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) {
|
|||
}
|
||||
|
||||
if (attacker && spell->caster)
|
||||
attacker->DamageSpawn(spell->caster, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, redirectDamage, redirectDamage, 0, 0, false, false, false, spell);
|
||||
attacker->DamageSpawn(spell->caster, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, redirectDamage, redirectDamage, 0, 0, false, false, false, false, spell);
|
||||
}
|
||||
|
||||
bool shouldRemoveSpell = false;
|
||||
|
|
|
@ -471,6 +471,7 @@ struct InfoStruct{
|
|||
reload_player_spells_ = oldStruct->get_reload_player_spells();
|
||||
|
||||
action_state_ = oldStruct->get_action_state();
|
||||
combat_action_state_ = oldStruct->get_combat_action_state();
|
||||
|
||||
}
|
||||
//mutable std::shared_mutex mutex_;
|
||||
|
@ -681,6 +682,8 @@ struct InfoStruct{
|
|||
|
||||
std::string get_action_state() { std::lock_guard<std::mutex> lk(classMutex); return action_state_; }
|
||||
|
||||
std::string get_combat_action_state() { std::lock_guard<std::mutex> lk(classMutex); return combat_action_state_; }
|
||||
|
||||
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; }
|
||||
|
@ -883,11 +886,11 @@ struct InfoStruct{
|
|||
void add_crit_bonus(float value) { std::lock_guard<std::mutex> lk(classMutex); if(crit_bonus_ + value < 0.0f) crit_bonus_ = 0.0f; else crit_bonus_ += value; }
|
||||
void add_potency(float value) { std::lock_guard<std::mutex> lk(classMutex); if(potency_ + value < 0.0f) potency_ = 0.0f; else potency_ += value; }
|
||||
void add_hate_mod(float value) { std::lock_guard<std::mutex> lk(classMutex); if(hate_mod_ + value < 0.0f) hate_mod_ = 0.0f; else hate_mod_ += value; }
|
||||
void add_reuse_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); if(reuse_speed_ + value < 0.0f) reuse_speed_ = 0.0f; else reuse_speed_ += value; }
|
||||
void add_casting_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); if(casting_speed_ + value < 0.0f) casting_speed_ = 0.0f; else casting_speed_ += value; }
|
||||
void add_recovery_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); if(recovery_speed_ + value < 0.0f) recovery_speed_ = 0.0f; else recovery_speed_ += value; }
|
||||
void add_spell_reuse_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); if(spell_reuse_speed_ + value < 0.0f) spell_reuse_speed_ = 0.0f; else spell_reuse_speed_ += value; }
|
||||
void add_spell_multi_attack(float value) { std::lock_guard<std::mutex> lk(classMutex); if(spell_multi_attack_ + value < 0.0f) spell_multi_attack_ = 0.0f; else spell_multi_attack_ += value; }
|
||||
void add_reuse_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); reuse_speed_ += value; }
|
||||
void add_casting_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); casting_speed_ += value; }
|
||||
void add_recovery_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); recovery_speed_ += value; }
|
||||
void add_spell_reuse_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); spell_reuse_speed_ += value; }
|
||||
void add_spell_multi_attack(float value) { std::lock_guard<std::mutex> lk(classMutex); spell_multi_attack_ += value; }
|
||||
void add_dps(float value) { std::lock_guard<std::mutex> lk(classMutex); if(dps_ + value < 0.0f) dps_ = 0.0f; else dps_ += value; }
|
||||
void add_dps_multiplier(float value) { std::lock_guard<std::mutex> lk(classMutex); if(dps_multiplier_ + value < 0.0f) dps_multiplier_ = 0.0f; else dps_multiplier_ += value; }
|
||||
void add_attackspeed(float value) { std::lock_guard<std::mutex> lk(classMutex); if(attackspeed_ + value < 0.0f) attackspeed_ = 0.0f; else attackspeed_ += value; }
|
||||
|
@ -975,6 +978,8 @@ struct InfoStruct{
|
|||
|
||||
void set_action_state(std::string value) { std::lock_guard<std::mutex> lk(classMutex); action_state_ = value; }
|
||||
|
||||
void set_combat_action_state(std::string value) { std::lock_guard<std::mutex> lk(classMutex); combat_action_state_ = value; }
|
||||
|
||||
void ResetEffects(Spawn* spawn)
|
||||
{
|
||||
for(int i=0;i<45;i++){
|
||||
|
@ -1183,6 +1188,7 @@ private:
|
|||
int8 reload_player_spells_;
|
||||
|
||||
std::string action_state_;
|
||||
std::string combat_action_state_;
|
||||
|
||||
// when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock
|
||||
std::mutex classMutex;
|
||||
|
@ -1424,13 +1430,13 @@ public:
|
|||
bool RangeWeaponReady();
|
||||
void MeleeAttack(Spawn* victim, float distance, bool primary, bool multi_attack = false);
|
||||
void RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo, bool multi_attack = false);
|
||||
bool SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod = 0, bool no_calcs = false);
|
||||
bool SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod = 0, bool no_calcs = false, int8 override_packet_type = 0, bool take_power = false);
|
||||
bool ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32 high_damage, string name, string success_msg, string effect_msg);
|
||||
bool SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string heal_type, int32 low_heal, int32 high_heal, int8 crit_mod = 0, bool no_calcs = false, string custom_spell_name="");
|
||||
int8 DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHitBonus, bool is_caster_spell, LuaSpell* lua_spell = nullptr);
|
||||
float GetDamageTypeResistPercentage(int8 damage_type);
|
||||
Skill* GetSkillByWeaponType(int8 type, int8 damage_type, bool update);
|
||||
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, LuaSpell* spell = 0);
|
||||
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, bool take_power = false, LuaSpell* spell = 0);
|
||||
float CalculateMitigation(int8 type = DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, int8 damage_type = 0, int16 attacker_level = 0, bool for_pvp = false);
|
||||
void AddHate(Entity* attacker, sint32 hate);
|
||||
bool CheckInterruptSpell(Entity* attacker);
|
||||
|
|
|
@ -2098,7 +2098,6 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
tradeskill_class_levels[i] = tmp_level;
|
||||
}
|
||||
}
|
||||
bool simplified_display = false;
|
||||
if (client->GetVersion() <= 546) { //simplify display (if possible)
|
||||
map<int8, int16> new_adv_class_levels;
|
||||
for (int i = 1; i <= 31; i += 10) {
|
||||
|
@ -2115,7 +2114,6 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
}
|
||||
}
|
||||
if (new_adv_class_levels.size() > 0) {
|
||||
simplified_display = true;
|
||||
int8 i = 0;
|
||||
for (itr = new_adv_class_levels.begin(); itr != new_adv_class_levels.end(); itr++) {
|
||||
i = itr->first;
|
||||
|
@ -2302,9 +2300,11 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
}
|
||||
case ITEM_TYPE_BAG:{
|
||||
if(bag_info){
|
||||
|
||||
int8 max_slots = player->GetMaxBagSlots(client->GetVersion());
|
||||
if (bag_info->num_slots > max_slots)
|
||||
bag_info->num_slots = max_slots;
|
||||
if (client->GetVersion() <= 546) {
|
||||
if (bag_info->num_slots > CLASSIC_EQ_MAX_BAG_SLOTS)
|
||||
bag_info->num_slots = CLASSIC_EQ_MAX_BAG_SLOTS;
|
||||
packet->setSubstructDataByName("details", "num_slots", bag_info->num_slots);
|
||||
packet->setSubstructDataByName("details", "weight_reduction", bag_info->weight_reduction);
|
||||
}
|
||||
|
@ -2375,12 +2375,14 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
packet->setSubstructDataByName("header_info", "footer_type", 0);
|
||||
|
||||
spell->SetPacketInformation(packet, player->GetZone()->GetClientBySpawn(player));
|
||||
if (player->HasSpell(skill_info->spell_id, skill_info->spell_tier, true))
|
||||
if (player->HasSpell(skill_info->spell_id, skill_info->spell_tier, true)) {
|
||||
packet->setDataByName("scribed", 1);
|
||||
}
|
||||
|
||||
if (packet->GetVersion() >= 927){
|
||||
if (player->HasSpell(skill_info->spell_id, skill_info->spell_tier, true))
|
||||
if (player->HasSpell(skill_info->spell_id, skill_info->spell_tier, true)) {
|
||||
packet->setAddToPacketByName("scribed_better_version", 1);// need to confirm
|
||||
}
|
||||
}
|
||||
else
|
||||
packet->setAddToPacketByName("scribed_better_version", 0); //if not scribed
|
||||
|
@ -2576,7 +2578,6 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
packet->setSubstructDataByName("footer", "description", description.c_str());
|
||||
|
||||
LogWrite(ITEM__PACKET, 0, "Items", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
|
||||
|
||||
#if EQDEBUG >= 9
|
||||
packet->PrintPacket();
|
||||
#endif
|
||||
|
@ -3547,9 +3548,12 @@ void PlayerItemList::AddItemToPacket(PacketStruct* packet, Player* player, Item*
|
|||
menu_data -= ITEM_MENU_TYPE_GENERIC;
|
||||
|
||||
if (item->details.num_slots > 0) {
|
||||
if (packet->GetVersion() <= 546 && item->details.num_slots > CLASSIC_EQ_MAX_BAG_SLOTS)
|
||||
item->details.num_slots = CLASSIC_EQ_MAX_BAG_SLOTS;
|
||||
int8 max_slots = player->GetMaxBagSlots(client->GetVersion());
|
||||
if (item->details.num_slots > max_slots)
|
||||
item->details.num_slots = max_slots;
|
||||
|
||||
menu_data += ITEM_MENU_TYPE_BAG;
|
||||
|
||||
if (item->details.num_free_slots == item->details.num_slots)
|
||||
menu_data += ITEM_MENU_TYPE_EMPTY_BAG;
|
||||
}
|
||||
|
@ -3653,24 +3657,24 @@ void PlayerItemList::AddItemToPacket(PacketStruct* packet, Player* player, Item*
|
|||
packet->setSubstructArrayDataByName("items", "unique_id", item->details.item_id, 0, i);
|
||||
else
|
||||
packet->setSubstructArrayDataByName("items", "unique_id", item->details.unique_id, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "bag_id", item->details.bag_id, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "inv_slot_id", item->details.inv_slot_id, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "menu_type", menu_data, 0, i);
|
||||
if (overflow) {
|
||||
if (overflow)
|
||||
packet->setSubstructArrayDataByName("items", "index", 0xFFFF, 0, i);
|
||||
}
|
||||
else {
|
||||
|
||||
if(i < 6) {
|
||||
packet->setSubstructArrayDataByName("items", "bag_id", item->details.bag_id ? item->details.bag_id : i, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "index", 0xFF, 0, i);
|
||||
|
||||
if(packet->GetVersion() <= 546) {
|
||||
/* DoF client and earlier side automatically assigns indexes
|
||||
** we have to send 0xFF or else all index is set to 255 on client
|
||||
** and then examine inventory won't work */
|
||||
packet->setSubstructArrayDataByName("items", "index", 0xFF, 0, i);
|
||||
}
|
||||
else {
|
||||
packet->setSubstructArrayDataByName("items", "bag_id", item->details.bag_id, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "index", i, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "index", i, 0, i);
|
||||
}
|
||||
item->details.index = i;
|
||||
}
|
||||
item->details.index = i;
|
||||
|
||||
packet->setSubstructArrayDataByName("items", "icon", item->details.icon, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "slot_id", item->details.slot_id, 0, i); // inventory doesn't convert slots
|
||||
if (client->GetVersion() <= 1208) {
|
||||
|
@ -3692,11 +3696,6 @@ void PlayerItemList::AddItemToPacket(PacketStruct* packet, Player* player, Item*
|
|||
packet->setSubstructArrayDataByName("items", "item_id", item->details.item_id, 0, i);
|
||||
//need broker id
|
||||
packet->setSubstructArrayDataByName("items", "name", item->name.c_str(), 0, i);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -4147,7 +4146,7 @@ EQ2Packet* EquipmentItemList::serialize(int16 version, Player* player){
|
|||
PacketStruct* packet2 = configReader.getStruct("Substruct_Item", version);
|
||||
packet_size = packet2->GetTotalPacketSize();
|
||||
safe_delete(packet2);
|
||||
int8 num_slots = NUM_SLOTS;
|
||||
int8 num_slots = player->GetNumSlotsEquip(version);
|
||||
packet->setArrayLengthByName("item_count", num_slots);
|
||||
if(!orig_packet){
|
||||
xor_packet = new uchar[packet_size* num_slots];
|
||||
|
@ -4161,7 +4160,7 @@ EQ2Packet* EquipmentItemList::serialize(int16 version, Player* player){
|
|||
|
||||
int32 levelsLowered = (effective_level > 0 && effective_level < player->GetLevel()) ? player->GetLevel() - effective_level : 0;
|
||||
|
||||
for(int16 i=0;i<NUM_SLOTS;i++){
|
||||
for(int16 i=0;i<num_slots;i++){
|
||||
// override the item slot we currently check as the client has different ordering, we need to match it
|
||||
int16 itemIdx = player->ConvertSlotFromClient(i, version);
|
||||
|
||||
|
@ -4171,9 +4170,12 @@ EQ2Packet* EquipmentItemList::serialize(int16 version, Player* player){
|
|||
if(item->slot_data.size() > 0)
|
||||
menu_data -= ITEM_MENU_TYPE_GENERIC;
|
||||
if (item->details.num_slots > 0) {
|
||||
if (packet->GetVersion() <= 546 && item->details.num_slots > CLASSIC_EQ_MAX_BAG_SLOTS)
|
||||
item->details.num_slots = CLASSIC_EQ_MAX_BAG_SLOTS;
|
||||
int8 max_slots = player->GetMaxBagSlots(version);
|
||||
if (item->details.num_slots > max_slots)
|
||||
item->details.num_slots = max_slots;
|
||||
|
||||
menu_data += ITEM_MENU_TYPE_BAG;
|
||||
|
||||
if (item->details.num_free_slots == item->details.num_slots)
|
||||
menu_data += ITEM_MENU_TYPE_EMPTY_BAG;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <ctime>
|
||||
#include "../../common/types.h"
|
||||
#include "../../common/DataBuffer.h"
|
||||
#include "../../common/MiscFunctions.h"
|
||||
#include "../Commands/Commands.h"
|
||||
#include "../../common/ConfigReader.h"
|
||||
|
||||
|
@ -111,8 +110,10 @@ extern MasterItemList master_item_list;
|
|||
#define DOF_DRINK_SLOT 2097152
|
||||
|
||||
#define CLASSIC_EQ_MAX_BAG_SLOTS 20
|
||||
#define DOF_EQ_MAX_BAG_SLOTS 36
|
||||
#define NUM_BANK_SLOTS 12
|
||||
#define NUM_SHARED_BANK_SLOTS 8
|
||||
#define CLASSIC_NUM_SLOTS 22
|
||||
#define NUM_SLOTS 25
|
||||
#define NUM_INV_SLOTS 6
|
||||
#define INV_SLOT1 0
|
||||
|
@ -204,8 +205,8 @@ extern MasterItemList master_item_list;
|
|||
|
||||
|
||||
|
||||
#define ITEM_MENU_TYPE_GENERIC 1 //0
|
||||
#define ITEM_MENU_TYPE_EQUIP 2 //1
|
||||
#define ITEM_MENU_TYPE_GENERIC 1 //0 (NON_EQUIPABLE)
|
||||
#define ITEM_MENU_TYPE_EQUIP 2 //1 (This is SLOT_FULL for classic)
|
||||
#define ITEM_MENU_TYPE_BAG 4//2
|
||||
#define ITEM_MENU_TYPE_HOUSE 8 //3 Place
|
||||
#define ITEM_MENU_TYPE_EMPTY_BAG 16 //4
|
||||
|
|
|
@ -1854,7 +1854,8 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) {
|
|||
LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
|
||||
if(!luaspell || luaspell->resisted) {
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
return 0;
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
}
|
||||
Spawn* caster = luaspell->caster;
|
||||
sint32 type = lua_interface->GetSInt32Value(state, 2);
|
||||
|
@ -1936,6 +1937,7 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) {
|
|||
success = true;
|
||||
}
|
||||
}
|
||||
lua_interface->SetBooleanValue(state, luaspell->has_damaged);
|
||||
if (success) {
|
||||
Spell* spell = luaspell->spell;
|
||||
if (caster->IsPlayer() && spell && spell->GetSpellData()->target_type == 1 && spell->GetSpellData()->spell_book_type == 1) { //offense combat art
|
||||
|
@ -1945,7 +1947,118 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
else {
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_SpellDamageExt(lua_State* state) {
|
||||
if (!lua_interface)
|
||||
return 0;
|
||||
Spawn* target = lua_interface->GetSpawn(state);
|
||||
LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
|
||||
if(!luaspell || luaspell->resisted) {
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
}
|
||||
Spawn* caster = luaspell->caster;
|
||||
sint32 type = lua_interface->GetSInt32Value(state, 2);
|
||||
int32 min_damage = lua_interface->GetInt32Value(state, 3);
|
||||
int32 max_damage = lua_interface->GetInt32Value(state, 4);
|
||||
int8 crit_mod = lua_interface->GetInt32Value(state, 5);
|
||||
bool no_calcs = lua_interface->GetInt32Value(state, 6) == 1;
|
||||
int32 override_packet_type = lua_interface->GetInt32Value(state, 7);
|
||||
bool take_power = lua_interface->GetInt32Value(state, 8) == 1;
|
||||
//lua_interface->ResetFunctionStack(state);
|
||||
int32 class_id = lua_interface->GetInt32Value(state, 9);
|
||||
vector<int16> faction_req;
|
||||
vector<int16> race_req;
|
||||
int32 class_req = 0;
|
||||
int32 i = 0;
|
||||
int8 f = 0;
|
||||
int8 r = 0;
|
||||
while ((class_id = lua_interface->GetInt32Value(state, 9 + i))) {
|
||||
if (class_id < 100) {
|
||||
class_req += pow(2.0, double(class_id - 1));
|
||||
}
|
||||
else if (class_id > 100 && class_id < 1000) {
|
||||
race_req.push_back(class_id);
|
||||
r++;
|
||||
}
|
||||
else {
|
||||
faction_req.push_back(class_id);
|
||||
f++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (caster && caster->IsEntity()) {
|
||||
bool race_match = false;
|
||||
bool success = false;
|
||||
luaspell->resisted = false;
|
||||
if (luaspell->targets.size() > 0) {
|
||||
ZoneServer* zone = luaspell->caster->GetZone();
|
||||
Spawn* target = 0;
|
||||
luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
|
||||
for (int32 i = 0; i < luaspell->targets.size(); i++) {
|
||||
if ((target = zone->GetSpawnByID(luaspell->targets[i]))) {
|
||||
|
||||
if (race_req.size() > 0) {
|
||||
for (int8 i = 0; i < race_req.size(); i++) {
|
||||
if(race_req[i] == target->GetRace() ||
|
||||
race_req[i] == race_types_list.GetRaceType(target->GetModelType()) ||
|
||||
race_req[i] == race_types_list.GetRaceBaseType(target->GetModelType())) {
|
||||
race_match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
race_match = true; // if the race_req.size = 0 then there is no race requirement and the race_match will be true
|
||||
if (race_match == true) {
|
||||
float distance = caster->GetDistance(target, true);
|
||||
((Entity*)caster)->SpellAttack(target, distance, luaspell, type, min_damage, max_damage, crit_mod, no_calcs, override_packet_type, take_power);
|
||||
}
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
else if (target) {
|
||||
|
||||
//check class and race/faction here
|
||||
if (race_req.size() > 0) {
|
||||
for (int8 i = 0; i < race_req.size(); i++) {
|
||||
if(race_req[i] == target->GetRace() ||
|
||||
race_req[i] == race_types_list.GetRaceType(target->GetModelType()) ||
|
||||
race_req[i] == race_types_list.GetRaceBaseType(target->GetModelType())) {
|
||||
race_match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
race_match = true; // if the race_req.size = 0 then there is no race requirement and the race_match will be true
|
||||
if (race_match == true) {
|
||||
float distance = caster->GetDistance(target, true);
|
||||
if (((Entity*)caster)->SpellAttack(target, distance, luaspell, type, min_damage, max_damage, crit_mod, no_calcs, override_packet_type, take_power))
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
lua_interface->SetBooleanValue(state, luaspell->has_damaged);
|
||||
if (success) {
|
||||
Spell* spell = luaspell->spell;
|
||||
if (caster->IsPlayer() && spell && spell->GetSpellData()->target_type == 1 && spell->GetSpellData()->spell_book_type == 1) { //offense combat art
|
||||
((Player*)caster)->InCombat(true);
|
||||
if (caster->GetZone())
|
||||
caster->GetZone()->TriggerCharSheetTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int EQ2Emu_lua_ModifyPower(lua_State* state) {
|
||||
if (!lua_interface)
|
||||
|
@ -12424,30 +12537,39 @@ int EQ2Emu_lua_DamageSpawn(lua_State* state) {
|
|||
bool is_tick = (lua_interface->GetInt8Value(state, 9) == 1);
|
||||
bool no_calcs = (lua_interface->GetInt8Value(state, 10) == 1);
|
||||
bool ignore_attacker = (lua_interface->GetInt8Value(state, 11) == 1);
|
||||
bool take_power = (lua_interface->GetInt8Value(state, 12) == 1);
|
||||
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (!attacker) {
|
||||
lua_interface->LogError("%s: LUA ProcDamage command error: caster is not a valid spawn", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
if (!attacker->IsEntity()) {
|
||||
lua_interface->LogError("%s: LUA ProcDamage command error: caster is not an entity", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
if (!victim) {
|
||||
lua_interface->LogError("%s: LUA ProcDamage command error: target is not a valid spawn", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
if (!victim->IsEntity()) {
|
||||
lua_interface->LogError("%s: LUA ProcDamage command error: target is not an entity", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
((Entity*)attacker)->DamageSpawn((Entity*)victim, type, dmg_type, low_damage, high_damage, spell_name.c_str(), crit_mod, is_tick, no_calcs, ignore_attacker);
|
||||
return 0;
|
||||
bool has_damaged = ((Entity*)attacker)->DamageSpawn((Entity*)victim, type, dmg_type, low_damage, high_damage, spell_name.c_str(), crit_mod, is_tick, no_calcs, ignore_attacker, take_power);
|
||||
lua_interface->SetBooleanValue(state, has_damaged);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_IsInvulnerable(lua_State* state) {
|
||||
|
@ -13878,3 +14000,30 @@ int EQ2Emu_lua_RemoveWidgetFromZoneMap(lua_State* state) {
|
|||
lua_interface->SetBooleanValue(state, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_SendHearCast(lua_State* state) {
|
||||
if (!lua_interface)
|
||||
return 0;
|
||||
LuaSpell* spell = lua_interface->GetCurrentSpell(state);
|
||||
Spawn* spawn = lua_interface->GetSpawn(state);
|
||||
int32 spell_visual_id = lua_interface->GetInt32Value(state, 2);
|
||||
int16 cast_time = lua_interface->GetInt16Value(state, 3);
|
||||
Spawn* caster = lua_interface->GetSpawn(state, 4);
|
||||
Spawn* target = lua_interface->GetSpawn(state, 5);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if(spell && spawn && spawn->IsEntity()) {
|
||||
ZoneServer* zone = spawn->GetZone();
|
||||
if(zone) {
|
||||
zone->SendCastSpellPacket(spell, caster && caster->IsEntity() ? (Entity*)caster : (Entity*)spawn, spell_visual_id, cast_time > 0 ? cast_time : 0xFFFF);
|
||||
}
|
||||
}
|
||||
else if (spawn) {
|
||||
if (spawn->IsPlayer()) {
|
||||
Client* client = ((Player*)spawn)->GetClient();
|
||||
if (client) {
|
||||
client->SendHearCast(caster ? caster : client->GetPlayer(), target ? target : client->GetPlayer(), spell_visual_id, cast_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -183,6 +183,7 @@ int EQ2Emu_lua_Spawn(lua_State* state);
|
|||
int EQ2Emu_lua_AddSpawnAccess(lua_State* state);
|
||||
int EQ2Emu_lua_CastSpell(lua_State* state);
|
||||
int EQ2Emu_lua_SpellDamage(lua_State* state);
|
||||
int EQ2Emu_lua_SpellDamageExt(lua_State* state);
|
||||
int EQ2Emu_lua_FaceTarget(lua_State* state);
|
||||
int EQ2Emu_lua_MoveToLocation(lua_State* state);
|
||||
int EQ2Emu_lua_ClearRunningLocations(lua_State* state);
|
||||
|
@ -646,4 +647,6 @@ int EQ2Emu_lua_RemoveRecipeFromPlayer(lua_State* state);
|
|||
int EQ2Emu_lua_ReplaceWidgetFromClient(lua_State* state);
|
||||
int EQ2Emu_lua_RemoveWidgetFromSpawnMap(lua_State* state);
|
||||
int EQ2Emu_lua_RemoveWidgetFromZoneMap(lua_State* state);
|
||||
|
||||
int EQ2Emu_lua_SendHearCast(lua_State* state);
|
||||
#endif
|
|
@ -267,6 +267,8 @@ bool LuaInterface::LoadLuaSpell(const char* name) {
|
|||
spell->caster = 0;
|
||||
spell->initial_target = 0;
|
||||
spell->resisted = false;
|
||||
spell->has_damaged = false;
|
||||
spell->is_damage_spell = false;
|
||||
spell->interrupted = false;
|
||||
spell->last_spellattack_hit = false;
|
||||
spell->crit = false;
|
||||
|
@ -987,6 +989,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
|
|||
lua_register(state, "GetSpeed", EQ2Emu_lua_GetSpeed);
|
||||
lua_register(state, "HasMoved", EQ2Emu_lua_HasMoved);
|
||||
lua_register(state, "SpellDamage", EQ2Emu_lua_SpellDamage);
|
||||
lua_register(state, "SpellDamageExt", EQ2Emu_lua_SpellDamageExt);
|
||||
lua_register(state, "CastSpell", EQ2Emu_lua_CastSpell);
|
||||
lua_register(state, "SpellHeal", EQ2Emu_lua_SpellHeal);
|
||||
lua_register(state, "SpellHealPct", EQ2Emu_lua_SpellHealPct);
|
||||
|
@ -1516,6 +1519,8 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
|
|||
lua_register(state, "ReplaceWidgetFromClient", EQ2Emu_lua_ReplaceWidgetFromClient);
|
||||
lua_register(state, "RemoveWidgetFromSpawnMap", EQ2Emu_lua_RemoveWidgetFromSpawnMap);
|
||||
lua_register(state, "RemoveWidgetFromZoneMap", EQ2Emu_lua_RemoveWidgetFromZoneMap);
|
||||
|
||||
lua_register(state, "SendHearCast", EQ2Emu_lua_SendHearCast);
|
||||
}
|
||||
|
||||
void LuaInterface::LogError(const char* error, ...) {
|
||||
|
@ -1980,6 +1985,8 @@ LuaSpell* LuaInterface::GetSpell(const char* name) {
|
|||
new_spell->timer = spell->timer;
|
||||
new_spell->timer.Disable();
|
||||
new_spell->resisted = false;
|
||||
new_spell->is_damage_spell = false;
|
||||
new_spell->has_damaged = false;
|
||||
new_spell->interrupted = false;
|
||||
new_spell->crit = false;
|
||||
new_spell->last_spellattack_hit = false;
|
||||
|
|
|
@ -91,6 +91,8 @@ struct LuaSpell{
|
|||
int8 slot_pos;
|
||||
int32 damage_remaining;
|
||||
bool resisted;
|
||||
bool has_damaged;
|
||||
bool is_damage_spell;
|
||||
bool interrupted;
|
||||
bool crit;
|
||||
bool last_spellattack_hit;
|
||||
|
|
|
@ -1396,6 +1396,25 @@ int16 Player::ConvertSlotFromClient(int8 slot, int16 version) {
|
|||
return slot;
|
||||
}
|
||||
|
||||
int16 Player::GetNumSlotsEquip(int16 version) {
|
||||
if(version <= 546) {
|
||||
return CLASSIC_NUM_SLOTS;
|
||||
}
|
||||
|
||||
return NUM_SLOTS;
|
||||
}
|
||||
|
||||
int8 Player::GetMaxBagSlots(int16 version) {
|
||||
if(version <= 283) {
|
||||
return CLASSIC_EQ_MAX_BAG_SLOTS;
|
||||
}
|
||||
else if(version = 546) {
|
||||
return DOF_EQ_MAX_BAG_SLOTS;
|
||||
}
|
||||
|
||||
return 255;
|
||||
}
|
||||
|
||||
vector<EQ2Packet*> Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, int16 version, int8 appearance_type, bool send_item_updates) {
|
||||
vector<EQ2Packet*> packets;
|
||||
EquipmentItemList* equipList = &equipment_list;
|
||||
|
@ -1403,7 +1422,7 @@ vector<EQ2Packet*> Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
|
|||
if(appearance_type)
|
||||
equipList = &appearance_equipment_list;
|
||||
|
||||
if(index >= NUM_SLOTS) {
|
||||
if(index >= GetNumSlotsEquip(version)) {
|
||||
LogWrite(PLAYER__ERROR, 0, "Player", "%u index is out of range for equip items, bag_id: %i, slot: %u, version: %u, appearance: %u", index, bag_id, slot, version, appearance_type);
|
||||
return packets;
|
||||
}
|
||||
|
@ -2653,33 +2672,26 @@ vector<Spell*> Player::GetSpellBookSpellsByTimer(int32 timerID) {
|
|||
}
|
||||
|
||||
void Player::ModifySpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast, int16 recast) {
|
||||
if (modify_recast) {
|
||||
spell->recast = recast;
|
||||
spell->recast_available = Timer::GetCurrentTime2() + (recast * 100);
|
||||
}
|
||||
SetSpellEntryRecast(spell, modify_recast, recast);
|
||||
if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) {
|
||||
spell->status += value; // use set/remove spell status now
|
||||
}
|
||||
}
|
||||
|
||||
void Player::AddSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast, int16 recast) {
|
||||
if (modify_recast) {
|
||||
spell->recast = recast;
|
||||
spell->recast_available = Timer::GetCurrentTime2() + (recast * 100);
|
||||
}
|
||||
SetSpellEntryRecast(spell, modify_recast, recast);
|
||||
if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) {
|
||||
spell->status = spell->status | value;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::RemoveSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast, int16 recast) {
|
||||
if (modify_recast) {
|
||||
spell->recast = recast;
|
||||
spell->recast_available = Timer::GetCurrentTime2() + (recast * 100);
|
||||
}
|
||||
SetSpellEntryRecast(spell, modify_recast, recast);
|
||||
if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) {
|
||||
spell->status = spell->status & ~value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Player::SetSpellStatus(Spell* spell, int8 status){
|
||||
MSpellsBook.lock();
|
||||
vector<SpellBookEntry*>::iterator itr;
|
||||
|
@ -2694,6 +2706,21 @@ void Player::SetSpellStatus(Spell* spell, int8 status){
|
|||
MSpellsBook.unlock();
|
||||
}
|
||||
|
||||
void Player::SetSpellEntryRecast(SpellBookEntry* spell, bool modify_recast, int16 recast) {
|
||||
if (modify_recast) {
|
||||
spell->recast = recast;
|
||||
float override_recast = static_cast<float>(recast);
|
||||
Spell* spell_ = master_spell_list.GetSpell(spell->spell_id, spell->tier);
|
||||
if(spell_) {
|
||||
int32 recast_time = spell_->CalculateRecastTimer(this, override_recast);
|
||||
spell->recast_available = Timer::GetCurrentTime2() + (recast_time / 10);
|
||||
}
|
||||
else {
|
||||
spell->recast_available = Timer::GetCurrentTime2() + (recast * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<SpellBookEntry*>* Player::GetSpellsSaveNeeded(){
|
||||
vector<SpellBookEntry*>* ret = 0;
|
||||
vector<SpellBookEntry*>::iterator itr;
|
||||
|
@ -6947,13 +6974,13 @@ void Player::SaveSpellEffects()
|
|||
continue;
|
||||
|
||||
savedEffects.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')",
|
||||
"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, has_damaged, 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, %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->spell->IsCopiedSpell() ? info->spell_effects[i].spell->spell->GetSpellData()->inherited_spell_id : 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->crit, info->spell_effects[i].spell->last_spellattack_hit, info->spell_effects[i].spell->interrupted, info->spell_effects[i].spell->resisted, info->spell_effects[i].spell->has_damaged, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->spell_effects[i].spell).c_str()).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);
|
||||
|
@ -6975,12 +7002,12 @@ void Player::SaveSpellEffects()
|
|||
if(info->maintained_effects[i].spell->spell->GetSpellData() && !info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
|
||||
timestamp = info->maintained_effects[i].expire_timestamp - Timer::GetCurrentTime2();
|
||||
savedEffects.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')",
|
||||
"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, has_damaged, 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, %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->spell->IsCopiedSpell() ? info->maintained_effects[i].spell->spell->GetSpellData()->inherited_spell_id : 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->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].spell->has_damaged, (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 ");
|
||||
|
|
|
@ -513,6 +513,8 @@ public:
|
|||
vector<EQ2Packet*> UnequipItem(int16 index, sint32 bag_id, int8 slot, int16 version, int8 appearance_type = 0, bool send_item_updates = true);
|
||||
int16 ConvertSlotToClient(int8 slot, int16 version);
|
||||
int16 ConvertSlotFromClient(int8 slot, int16 version);
|
||||
int16 GetNumSlotsEquip(int16 version);
|
||||
int8 GetMaxBagSlots(int16 version);
|
||||
EQ2Packet* SwapEquippedItems(int8 slot1, int8 slot2, int16 version, int16 equiptype);
|
||||
EQ2Packet* RemoveInventoryItem(int8 bag_slot, int8 slot);
|
||||
EQ2Packet* SendInventoryUpdate(int16 version);
|
||||
|
@ -1181,6 +1183,7 @@ private:
|
|||
void ModifySpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0);
|
||||
void AddSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0);
|
||||
void RemoveSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0);
|
||||
void SetSpellEntryRecast(SpellBookEntry* spell, bool modify_recast, int16 recast);
|
||||
void InitXPTable();
|
||||
map<int8, int32> m_levelXPReq;
|
||||
|
||||
|
|
|
@ -301,7 +301,7 @@ void Sign::HandleUse(Client* client, string command)
|
|||
|
||||
|
||||
//devn00b: Add support for marking objects
|
||||
if (strcmp(entity_command->command.c_str(), "mark") == 0) {
|
||||
if (entity_command && strcmp(entity_command->command.c_str(), "mark") == 0) {
|
||||
LogWrite(SIGN__DEBUG, 0, "Sign", "ActivateMarkReqested Sign - Command: '%s' (Should read mark)", entity_command->command.c_str());
|
||||
int32 char_id = client->GetCharacterID();
|
||||
database.SaveSignMark(char_id, GetWidgetID(), database.GetCharacterName(char_id), client);
|
||||
|
|
|
@ -2392,21 +2392,27 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
|
|||
else
|
||||
packet->setDataByName("soga_model_type", sogaModelType);
|
||||
|
||||
if (GetTempActionState() >= 0)
|
||||
packet->setDataByName("action_state", GetTempActionState());
|
||||
else {
|
||||
int16 action_state = appearance.action_state;
|
||||
if(IsEntity()) {
|
||||
std::string actionState = "";
|
||||
if (GetTempActionState() >= 0) {
|
||||
action_state = GetTempActionState();
|
||||
actionState = ((Entity*)this)->GetInfoStruct()->get_combat_action_state();
|
||||
}
|
||||
else {
|
||||
actionState = ((Entity*)this)->GetInfoStruct()->get_action_state();
|
||||
}
|
||||
|
||||
Client* client = spawn->GetClient();
|
||||
int16 action_state = appearance.action_state;
|
||||
if(IsEntity() && client) {
|
||||
std::string actionState = ((Entity*)this)->GetInfoStruct()->get_action_state();
|
||||
if(actionState.size() > 0) {
|
||||
Emote* emote = visual_states.FindEmote(actionState, client->GetVersion());
|
||||
if(emote != NULL)
|
||||
action_state = emote->GetVisualState();
|
||||
}
|
||||
}
|
||||
packet->setDataByName("action_state", action_state);
|
||||
}
|
||||
packet->setDataByName("action_state", action_state);
|
||||
|
||||
bool scaredOfPlayer = false;
|
||||
|
||||
|
|
|
@ -1091,11 +1091,13 @@ public:
|
|||
bool HasMovementLoop(){ return movement_loop.size() > 0; }
|
||||
bool HasMovementLocations() {
|
||||
bool hasLocations = false;
|
||||
if (MMovementLocations)
|
||||
if (MMovementLocations) {
|
||||
MMovementLocations->readlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
hasLocations = movement_locations ? movement_locations->size() > 0 : false;
|
||||
if (MMovementLocations)
|
||||
if (MMovementLocations) {
|
||||
MMovementLocations->releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
return hasLocations;
|
||||
}
|
||||
|
||||
|
|
|
@ -307,10 +307,15 @@ void SpellProcess::CheckRecast(Spell* spell, Entity* caster, float timer_overrid
|
|||
else
|
||||
timer->client = 0;
|
||||
timer->spell = spell;
|
||||
if(timer_override == 0)
|
||||
timer->timer = new Timer((int32)(spell->GetSpellData()->recast*1000));
|
||||
else
|
||||
timer->timer = new Timer((int32)(timer_override*1000));
|
||||
int32 recast_time = spell->GetSpellData()->recast * 1000;
|
||||
if(timer_override == 0) {
|
||||
recast_time = spell->CalculateRecastTimer(caster);
|
||||
timer->timer = new Timer(recast_time);
|
||||
}
|
||||
else {
|
||||
recast_time = spell->CalculateRecastTimer(caster, timer_override);
|
||||
timer->timer = new Timer(recast_time);
|
||||
}
|
||||
|
||||
timer->type_group_spell_id = spell->GetSpellData()->type_group_spell_id;
|
||||
timer->linked_timer = spell->GetSpellData()->linked_timer;
|
||||
|
@ -318,11 +323,7 @@ void SpellProcess::CheckRecast(Spell* spell, Entity* caster, float timer_overrid
|
|||
|
||||
recast_timers.Add(timer);
|
||||
if(caster->IsPlayer()){
|
||||
if(timer_override == 0)
|
||||
((Player*)caster)->LockSpell(spell, (int16)(spell->GetSpellData()->recast * 10));
|
||||
else
|
||||
((Player*)caster)->LockSpell(spell, timer_override * 10);
|
||||
|
||||
((Player*)caster)->LockSpell(spell, (int16)(recast_time / 100));
|
||||
if (check_linked_timers && spell->GetSpellData()->linked_timer > 0) {
|
||||
vector<Spell*> linkedSpells = ((Player*)caster)->GetSpellBookSpellsByTimer(spell->GetSpellData()->linked_timer);
|
||||
for (int8 i = 0; i < linkedSpells.size(); i++) {
|
||||
|
@ -620,11 +621,13 @@ bool SpellProcess::CastInstant(Spell* spell, Entity* caster, Entity* target, boo
|
|||
lua_interface->ResetFunctionStack(lua_spell->state);
|
||||
}
|
||||
|
||||
bool result = CastProcessedSpell(lua_spell, passive);
|
||||
|
||||
caster->GetZone()->SendCastSpellPacket(lua_spell, caster);
|
||||
|
||||
if (!remove)
|
||||
return CastProcessedSpell(lua_spell, passive);
|
||||
|
||||
|
||||
if(!remove)
|
||||
return result;
|
||||
|
||||
lua_interface->RemoveSpell(lua_spell, true, SpellScriptTimersHasSpell(lua_spell));
|
||||
return true;
|
||||
}
|
||||
|
@ -651,12 +654,12 @@ void SpellProcess::SendFinishedCast(LuaSpell* spell, Client* client){
|
|||
UnlockAllSpells(client);
|
||||
|
||||
if(spell->resisted && spell->spell->GetSpellData()->recast > 0)
|
||||
CheckRecast(spell->spell, client->GetPlayer(), 0.5); // half sec recast on resisted spells
|
||||
CheckRecast(spell->spell, client->GetPlayer(), 0.5f); // half sec recast on resisted spells
|
||||
else if (!spell->interrupted)
|
||||
CheckRecast(spell->spell, client->GetPlayer());
|
||||
else if(spell->caster && spell->caster->IsPlayer())
|
||||
{
|
||||
((Player*)spell->caster)->LockSpell(spell->spell, (int16)(spell->spell->GetSpellData()->recast * 10));
|
||||
((Player*)spell->caster)->LockSpell(spell->spell, (int16)(spell->spell->CalculateRecastTimer(spell->caster) / 100));
|
||||
}
|
||||
PacketStruct* packet = configReader.getStruct("WS_FinishCastSpell", client->GetVersion());
|
||||
if(packet){
|
||||
|
|
|
@ -810,7 +810,7 @@ void Spell::SetPacketInformation(PacketStruct* packet, Client* client, bool disp
|
|||
else {
|
||||
packet->setSubstructDataByName("spell_info", "cast_time", spell->cast_time);
|
||||
}
|
||||
packet->setSubstructDataByName("spell_info", "recast", spell->recast);
|
||||
packet->setSubstructDataByName("spell_info", "recast", CalculateRecastTimer(client->GetPlayer())/1000);
|
||||
packet->setSubstructDataByName("spell_info", "radius", spell->radius);
|
||||
packet->setSubstructDataByName("spell_info", "req_concentration", spell->req_concentration);
|
||||
//packet->setSubstructDataByName("spell_info","req_concentration2", 2);
|
||||
|
@ -2220,14 +2220,42 @@ bool Spell::IsCopiedSpell() {
|
|||
void Spell::ModifyCastTime(Entity* caster){
|
||||
int16 cast_time = spell->cast_time;
|
||||
float cast_speed = caster->GetInfoStruct()->get_casting_speed();
|
||||
if (cast_time > 0){
|
||||
if (cast_speed > 0) // casting speed can only reduce up to half a cast time
|
||||
spell->cast_time *= max((float) 0.5, (float) (1 / (1 + (cast_speed * .01))));
|
||||
else if (cast_speed < 0) // not sure if casting speed debuff is capped on live or not, capping at 1.5 * the normal rate for now
|
||||
spell->cast_time *= min((float) 1.5, (float) (1 + (1 - (1 / (1 + (cast_speed * -.01))))));
|
||||
if (cast_speed > 0.0f){
|
||||
bool modifiedSpeed = false;
|
||||
if (cast_speed > 0.0f && (modifiedSpeed = true)) // casting speed can only reduce up to half a cast time
|
||||
spell->cast_time *= max(0.5f, (float) (1 / (1 + (cast_speed * .01))));
|
||||
else if (cast_speed < 0.0f && (modifiedSpeed = true)) // not sure if casting speed debuff is capped on live or not, capping at 1.5 * the normal rate for now
|
||||
spell->cast_time *= min(1.5f, (float) (1 + (1 - (1 / (1 + (cast_speed * -.01))))));
|
||||
|
||||
if(modifiedSpeed) {
|
||||
LogWrite(SPELL__DEBUG, 9, "Spells", "%s: spell %s cast time %u to %u based on cast_speed %f", GetName(), caster->GetName(), cast_time, spell->cast_time, cast_speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 Spell::CalculateRecastTimer(Entity* caster, float override_timer) {
|
||||
int32 original_recast = static_cast<int>(GetSpellData()->recast) * 1000;
|
||||
|
||||
if(override_timer > 0.0f) {
|
||||
original_recast = static_cast<int>(override_timer) * 1000;
|
||||
}
|
||||
|
||||
int32 recast_time = original_recast;
|
||||
float cast_speed = caster->GetInfoStruct()->get_spell_reuse_speed();
|
||||
if (cast_speed > 0.0f){
|
||||
bool modifiedSpeed = false;
|
||||
if (cast_speed > 0.0f && (modifiedSpeed = true)) // casting speed can only reduce up to half a cast time
|
||||
recast_time *= max(0.5f, (float) (1 / (1 + (cast_speed * .01))));
|
||||
else if (cast_speed < 0.0f && (modifiedSpeed = true)) // not sure if casting speed debuff is capped on live or not, capping at 1.5 * the normal rate for now
|
||||
recast_time *= min(1.5f, (float) (1 + (1 - (1 / (1 + (cast_speed * -.01))))));
|
||||
|
||||
if(modifiedSpeed) {
|
||||
LogWrite(SPELL__DEBUG, 9, "Spells", "%s: spell %s recast time %u to %u based on spell_reuse_time %f", GetName(), caster->GetName(), original_recast, recast_time, cast_speed);
|
||||
}
|
||||
}
|
||||
return recast_time;
|
||||
}
|
||||
|
||||
vector <SpellDisplayEffect*>* Spell::GetSpellEffects(){
|
||||
std::shared_lock lock(MSpellInfo);
|
||||
vector <SpellDisplayEffect*>* ret = &effects;
|
||||
|
|
|
@ -364,6 +364,7 @@ public:
|
|||
bool IsOffenseSpell();
|
||||
bool IsCopiedSpell();
|
||||
void ModifyCastTime(Entity* caster);
|
||||
int32 CalculateRecastTimer(Entity* caster, float override_timer = 0.0f);
|
||||
bool CastWhileStunned();
|
||||
bool CastWhileMezzed();
|
||||
bool CastWhileStifled();
|
||||
|
|
|
@ -61,7 +61,7 @@ public:
|
|||
if(in_targeted_message)
|
||||
targeted_message = string(in_targeted_message);
|
||||
}
|
||||
int GetVisualState() { return visual_state; }
|
||||
int32 GetVisualState() { return visual_state; }
|
||||
const char* GetName() { return name.c_str(); }
|
||||
const char* GetMessage() { return message.c_str(); }
|
||||
const char* GetTargetedMessage() { return targeted_message.c_str(); }
|
||||
|
@ -70,7 +70,7 @@ public:
|
|||
string GetMessageString() { return message; }
|
||||
string GetTargetedMessageString() { return targeted_message; }
|
||||
private:
|
||||
int visual_state;
|
||||
int32 visual_state;
|
||||
string name;
|
||||
string message;
|
||||
string targeted_message;
|
||||
|
@ -98,7 +98,7 @@ public:
|
|||
}
|
||||
|
||||
void AddVersionRange(int32 min_version, int32 max_version,
|
||||
char* in_name, int in_visual_state, char* in_message, char* in_targeted_message)
|
||||
char* in_name, int in_visual_state, char* in_message = nullptr, char* in_targeted_message = nullptr)
|
||||
{
|
||||
map<VersionRange*, Emote*>::iterator itr = FindVersionRange(min_version, max_version);
|
||||
if (itr != version_map.end())
|
||||
|
@ -164,8 +164,9 @@ public:
|
|||
void Reset(){
|
||||
ClearVisualStates();
|
||||
ClearEmotes();
|
||||
ClearSpellVisuals();
|
||||
}
|
||||
|
||||
|
||||
void ClearEmotes(){
|
||||
map<string, EmoteVersionRange*>::iterator map_list;
|
||||
for(map_list = emoteMap.begin(); map_list != emoteMap.end(); map_list++ )
|
||||
|
@ -215,8 +216,67 @@ public:
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InsertSpellVisualRange(EmoteVersionRange* emote, int32 spell_visual_id) {
|
||||
spellMap[emote->GetName()] = emote;
|
||||
spellMapID[spell_visual_id] = emote;
|
||||
}
|
||||
|
||||
EmoteVersionRange* FindSpellVisualRange(string var) {
|
||||
if (spellMap.count(var) > 0)
|
||||
{
|
||||
return spellMap[var];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EmoteVersionRange* FindSpellVisualRangeByID(int32 id) {
|
||||
if (spellMapID.count(id) > 0)
|
||||
{
|
||||
return spellMapID[id];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Emote* FindSpellVisual(string var, int32 version){
|
||||
if (spellMap.count(var) > 0)
|
||||
{
|
||||
map<VersionRange*,Emote*>::iterator itr = spellMap[var]->FindEmoteVersion(version);
|
||||
|
||||
if (itr != spellMap[var]->GetRangeEnd())
|
||||
{
|
||||
Emote* emote = itr->second;
|
||||
return emote;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Emote* FindSpellVisualByID(int32 visual_id, int32 version){
|
||||
if (spellMapID.count(visual_id) > 0)
|
||||
{
|
||||
map<VersionRange*,Emote*>::iterator itr = spellMapID[visual_id]->FindEmoteVersion(version);
|
||||
|
||||
if (itr != spellMapID[visual_id]->GetRangeEnd())
|
||||
{
|
||||
Emote* emote = itr->second;
|
||||
return emote;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ClearSpellVisuals(){
|
||||
map<string, EmoteVersionRange*>::iterator map_list;
|
||||
for(map_list = spellMap.begin(); map_list != spellMap.end(); map_list++ )
|
||||
safe_delete(map_list->second);
|
||||
spellMap.clear();
|
||||
spellMapID.clear();
|
||||
}
|
||||
private:
|
||||
map<string,VisualState*> visualStateMap;
|
||||
map<string,EmoteVersionRange*> emoteMap;
|
||||
map<string,EmoteVersionRange*> spellMap;
|
||||
map<int32,EmoteVersionRange*> spellMapID;
|
||||
};
|
||||
|
||||
|
|
|
@ -459,11 +459,29 @@ void WorldDatabase::LoadVisualStates()
|
|||
visual_states.InsertEmoteRange(range);
|
||||
}
|
||||
|
||||
range->AddVersionRange(atoul(row[4]),atoul(row[5]), row[0], atoi(row[1]), row[2], row[3]);
|
||||
range->AddVersionRange(atoul(row[4]),atoul(row[5]), row[0], atoul(row[1]), row[2], row[3]);
|
||||
total++;
|
||||
LogWrite(WORLD__DEBUG, 5, "World", "---Loading emote state: '%s' (%i)", row[1], atoi(row[0]));
|
||||
LogWrite(WORLD__DEBUG, 5, "World", "---Loading emote state: '%s' (%i)", row[0], atoul(row[0]));
|
||||
}
|
||||
LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u emote state(s)", total);
|
||||
|
||||
|
||||
total = 0;
|
||||
result = query2.RunQuery2(Q_SELECT, "SELECT name, spell_visual_id, alternate_spell_visual, min_version_range, max_version_range FROM spell_visuals");
|
||||
while(result && (row = mysql_fetch_row(result)))
|
||||
{
|
||||
EmoteVersionRange* range = 0;
|
||||
if ((range = visual_states.FindSpellVisualRange(string(row[0]))) == NULL)
|
||||
{
|
||||
range = new EmoteVersionRange(row[0]);
|
||||
visual_states.InsertSpellVisualRange(range, atoul(row[1]));
|
||||
}
|
||||
|
||||
range->AddVersionRange(atoul(row[3]),atoul(row[4]), row[0], atoul(row[1]), row[2]);
|
||||
total++;
|
||||
LogWrite(WORLD__DEBUG, 5, "World", "---Loading spell visual state: '%s' (%u)", row[1], atoul(row[0]));
|
||||
}
|
||||
LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u spell visual state(s)", total);
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadSubCommandList()
|
||||
|
@ -7632,7 +7650,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
|
|||
|
||||
multimap<LuaSpell*, Entity*> restoreSpells;
|
||||
// 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)) {
|
||||
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, has_damaged, 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;
|
||||
}
|
||||
|
@ -7668,6 +7686,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
|
|||
int8 last_spellattack_hit = result.GetInt32Str("last_spellattack_hit");
|
||||
int8 interrupted = result.GetInt32Str("interrupted");
|
||||
int8 resisted = result.GetInt32Str("resisted");
|
||||
int8 has_damaged = result.GetInt32Str("has_damaged");
|
||||
std::string custom_function = std::string(result.GetStringStr("custom_function"));
|
||||
LuaSpell* lua_spell = 0;
|
||||
if(custom_spell)
|
||||
|
@ -7788,6 +7807,8 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
|
|||
lua_spell->interrupted = interrupted;
|
||||
lua_spell->last_spellattack_hit = last_spellattack_hit;
|
||||
lua_spell->num_triggers = num_triggers;
|
||||
lua_spell->has_damaged = has_damaged;
|
||||
lua_spell->is_damage_spell = has_damaged;
|
||||
}
|
||||
|
||||
if(lua_spell->initial_target == 0 && target_char_id == 0xFFFFFFFF && player->HasPet())
|
||||
|
|
|
@ -86,6 +86,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "Tradeskills/Tradeskills.h"
|
||||
#include "AltAdvancement/AltAdvancement.h"
|
||||
#include "Bots/Bot.h"
|
||||
#include "VisualStates.h"
|
||||
|
||||
extern WorldDatabase database;
|
||||
extern const char* ZONE_NAME;
|
||||
|
@ -119,6 +120,7 @@ extern MasterAAList master_aa_list;
|
|||
extern MasterAAList master_tree_nodes;
|
||||
extern ChestTrapList chest_trap_list;
|
||||
extern MasterRecipeBookList master_recipebook_list;
|
||||
extern VisualStates visual_states;
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -3036,7 +3038,7 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
|||
sent_item_details[id] = true;
|
||||
MItemDetails.releasewritelock(__FUNCTION__, __LINE__);
|
||||
EQ2Packet* app = item->serialize(GetVersion(), false, GetPlayer());
|
||||
//DumpPacket(app);
|
||||
|
||||
QueuePacket(app);
|
||||
if (wasSpawn)
|
||||
delete item;
|
||||
|
@ -3070,7 +3072,6 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
|||
sent_item_details[id] = true;
|
||||
MItemDetails.releasewritelock(__FUNCTION__, __LINE__);
|
||||
EQ2Packet* app = item->serialize(GetVersion(), false, GetPlayer());
|
||||
//DumpPacket(app);
|
||||
QueuePacket(app);
|
||||
}
|
||||
else {
|
||||
|
@ -3099,7 +3100,7 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
|||
if (item) {
|
||||
//only display popup for non merchant links
|
||||
EQ2Packet* app = item->serialize(GetVersion(), (request->getType_int8_ByName("show_popup") != 0), GetPlayer(), true, 0, 0, GetVersion() > 546 ? true : false);
|
||||
//DumpPacket(app);
|
||||
|
||||
QueuePacket(app);
|
||||
}
|
||||
else {
|
||||
|
@ -7965,9 +7966,7 @@ void Client::SendBuyMerchantList(bool sell) {
|
|||
else
|
||||
packet->setDataByName("type", 2);
|
||||
}
|
||||
packet->PrintPacket();
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
// DumpPacket(outapp);
|
||||
QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
|
@ -8541,7 +8540,6 @@ void Client::SendMailList() {
|
|||
p->setArrayDataByName("player_to_id", mail->player_to_id, i);
|
||||
p->setArrayDataByName("player_from", mail->player_from.c_str(), i);
|
||||
p->setArrayDataByName("subject", mail->subject.c_str(), i);
|
||||
p->setArrayDataByName("unknown1", 0x0000, i);
|
||||
p->setArrayDataByName("already_read", mail->already_read, i);
|
||||
if(mail->expire_time)
|
||||
p->setArrayDataByName("mail_deletion", mail->expire_time - mail->time_sent, i);
|
||||
|
@ -8598,6 +8596,8 @@ void Client::SendMailList() {
|
|||
p->setDataByName("unknown3", 0x01F4);
|
||||
p->setDataByName("unknown4", 0x01000000);
|
||||
EQ2Packet* pack = p->serialize();
|
||||
//DumpPacket(pack);
|
||||
//p->PrintPacket();
|
||||
QueuePacket(pack);
|
||||
safe_delete(p);
|
||||
}
|
||||
|
@ -12415,4 +12415,45 @@ ZoneServer* Client::GetHouseZoneServer(int32 spawn_id, int64 house_id) {
|
|||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Client::SendHearCast(Spawn* caster, Spawn* target, int32 spell_visual, int16 cast_time) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", GetVersion());
|
||||
if (packet) {
|
||||
int32 caster_id = GetPlayer()->GetIDWithPlayerSpawn(caster);
|
||||
int32 target_id = GetPlayer()->GetIDWithPlayerSpawn(target);
|
||||
|
||||
packet->setDataByName("spawn_id", caster_id);
|
||||
packet->setArrayLengthByName("num_targets", 1);
|
||||
packet->setArrayDataByName("target", target_id);
|
||||
packet->setDataByName("num_targets", 1);
|
||||
|
||||
int32 visual = GetSpellVisualOverride(spell_visual);
|
||||
|
||||
packet->setDataByName("spell_visual", visual); //result
|
||||
packet->setDataByName("cast_time", cast_time*.01f); //delay
|
||||
packet->setDataByName("spell_id", 1);
|
||||
packet->setDataByName("spell_level", 1);
|
||||
packet->setDataByName("spell_tier", 1);
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
|
||||
DumpPacket(outapp);
|
||||
QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
|
||||
int32 Client::GetSpellVisualOverride(int32 spell_visual) {
|
||||
int32 visual = spell_visual;
|
||||
if(GetVersion() <= 546) { // spell's spell_visual field is based on newer clients, DoF has to convert
|
||||
Emote* spellVisualEmote = visual_states.FindSpellVisualByID(visual, 60085);
|
||||
if(spellVisualEmote != nullptr && spellVisualEmote->GetMessageString().size() > 0) {
|
||||
spellVisualEmote = visual_states.FindSpellVisual(spellVisualEmote->GetMessageString(), GetVersion());
|
||||
if(spellVisualEmote) {
|
||||
visual = (int32)spellVisualEmote->GetVisualState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visual;
|
||||
}
|
|
@ -585,6 +585,9 @@ public:
|
|||
|
||||
void SendReplaceWidget(int32 widget_id, bool delete_widget, float x=0.0f, float y=0.0f, float z=0.0f, int32 grid_id=0);
|
||||
void ProcessZoneIgnoreWidgets();
|
||||
|
||||
void SendHearCast(Spawn* caster, Spawn* target, int32 spell_visual, int16 cast_time);
|
||||
int32 GetSpellVisualOverride(int32 spell_visual);
|
||||
private:
|
||||
void AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i);
|
||||
void SavePlayerImages();
|
||||
|
|
|
@ -5249,13 +5249,18 @@ void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool f
|
|||
safe_delete(packet);
|
||||
}
|
||||
|
||||
void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster){
|
||||
void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster, int32 spell_visual_override, int16 casttime_override){
|
||||
EQ2Packet* outapp = 0;
|
||||
PacketStruct* packet = 0;
|
||||
Client* client = 0;
|
||||
if(!caster || !spell || !spell->spell || spell->interrupted)
|
||||
return;
|
||||
|
||||
if(spell->is_damage_spell && (!spell->has_damaged || spell->resisted)) {
|
||||
// we did not successfully hit target, so we should not send the visual
|
||||
return;
|
||||
}
|
||||
|
||||
vector<Client*>::iterator client_itr;
|
||||
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
|
@ -5284,8 +5289,17 @@ void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster){
|
|||
packet->setArrayDataByName("target", 0xFFFFFFFF, i);
|
||||
}
|
||||
}
|
||||
packet->setDataByName("spell_visual", spell->spell->GetSpellData()->spell_visual); //result
|
||||
packet->setDataByName("cast_time", spell->spell->GetSpellData()->cast_time*.01f); //delay
|
||||
|
||||
int32 visual_to_use = spell_visual_override > 0 ? spell_visual_override : spell->spell->GetSpellData()->spell_visual;
|
||||
int32 visual = client->GetSpellVisualOverride(visual_to_use);
|
||||
|
||||
packet->setDataByName("spell_visual", visual); //result
|
||||
if(casttime_override != 0xFFFF) {
|
||||
packet->setDataByName("cast_time", casttime_override*.01f); //delay
|
||||
}
|
||||
else {
|
||||
packet->setDataByName("cast_time", spell->spell->GetSpellData()->cast_time*.01f); //delay
|
||||
}
|
||||
packet->setDataByName("spell_id", spell->spell->GetSpellID());
|
||||
packet->setDataByName("spell_level", 1);
|
||||
packet->setDataByName("spell_tier", spell->spell->GetSpellData()->tier);
|
||||
|
@ -5330,7 +5344,10 @@ void ZoneServer::SendCastSpellPacket(int32 spell_visual, Spawn* target, Spawn* c
|
|||
}
|
||||
packet->setArrayLengthByName("num_targets", 1);
|
||||
packet->setArrayDataByName("target", target_id);
|
||||
packet->setDataByName("spell_visual", spell_visual);
|
||||
|
||||
int32 visual = client->GetSpellVisualOverride(spell_visual);
|
||||
|
||||
packet->setDataByName("spell_visual", visual);
|
||||
packet->setDataByName("cast_time", 0);
|
||||
packet->setDataByName("spell_id", 0);
|
||||
packet->setDataByName("spell_level", 0);
|
||||
|
|
|
@ -355,7 +355,7 @@ public:
|
|||
void SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, int8 type2, int8 damage_type, int16 damage, const char* spell_name);
|
||||
void SendHealPacket(Spawn* caster, Spawn* target, int16 type, int32 heal_amt, const char* spell_name);
|
||||
|
||||
void SendCastSpellPacket(LuaSpell* spell, Entity* caster);
|
||||
void SendCastSpellPacket(LuaSpell* spell, Entity* caster, int32 spell_visual_override = 0, int16 casttime_override = 0xFFFF);
|
||||
void SendCastSpellPacket(int32 spell_visual, Spawn* target, Spawn* caster = 0);
|
||||
void SendCastEntityCommandPacket(EntityCommand* entity_command, int32 spawn_id, int32 target_id);
|
||||
void TriggerCharSheetTimer();
|
||||
|
|
|
@ -279,7 +279,7 @@ bool EQStream::HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset, int16 len
|
|||
if(valid)
|
||||
return true;
|
||||
}
|
||||
else if(p->pBuffer[offset] != 0xff && p->pBuffer[offset+1] == 0xff) {
|
||||
else if(p->pBuffer[offset] != 0xff && p->pBuffer[offset+1] == 0xff && p->size > (1+offset)) {
|
||||
uint8 new_length = 0;
|
||||
|
||||
memcpy(&new_length, p->pBuffer+offset, sizeof(int8));
|
||||
|
@ -291,7 +291,7 @@ bool EQStream::HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset, int16 len
|
|||
delete subp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1467,8 +1467,9 @@ void EQStream::Write(int eq_fd)
|
|||
//no more data to send
|
||||
if (GetState() == CLOSING) {
|
||||
MOutboundQueue.lock();
|
||||
if (SequencedQueue.size() > 0 )
|
||||
LogWrite(PACKET__DEBUG, 9, "Packet", "All outgoing data flushed, client should be disconnecting, awaiting acknowledgement of SequencedQueue.");
|
||||
if (SequencedQueue.size() > 0 ) {
|
||||
// retransmission attempts
|
||||
}
|
||||
else
|
||||
{
|
||||
LogWrite(PACKET__DEBUG, 9, "Packet", "All outgoing data flushed, disconnecting client.");
|
||||
|
|
|
@ -1029,6 +1029,7 @@
|
|||
<Data ElementName="index" Type="int16" Size="1" />
|
||||
<Data ElementName="icon" Type="int16" Size="1" />
|
||||
<Data ElementName="count" Type="int8" Size="1" />
|
||||
<Data ElementName="unknown" Type="int8" Size="1" />
|
||||
<Data ElementName="level" Type="int8" Size="1" />
|
||||
<Data ElementName="tier" Type="int8" Size="1" />
|
||||
<Data ElementName="num_slots" Type="int8" Size="1" />
|
||||
|
@ -1036,19 +1037,20 @@
|
|||
<Data ElementName="name" Type="char" Size="81" />
|
||||
</Struct>
|
||||
<Struct Name="Substruct_Item" ClientVersion="546" >
|
||||
<Data ElementName="unique_id" Type="int32" Size="1" />
|
||||
<Data ElementName="bag_id" Type="int32" Size="1" />
|
||||
<Data ElementName="inv_slot_id" Type="int32" Size="1" />
|
||||
<Data ElementName="menu_type" Type="int32" Size="1" />
|
||||
<Data ElementName="slot_id" Type="int8" Size="1" />
|
||||
<Data ElementName="index" Type="int16" Size="1" />
|
||||
<Data ElementName="icon" Type="int16" Size="1" />
|
||||
<Data ElementName="count" Type="int8" Size="1" />
|
||||
<Data ElementName="level" Type="int8" Size="1" />
|
||||
<Data ElementName="tier" Type="int8" Size="1" />
|
||||
<Data ElementName="num_slots" Type="int8" Size="1" />
|
||||
<Data ElementName="item_id" Type="sint32" Size="1" />
|
||||
<Data ElementName="name" Type="char" Size="81" />
|
||||
<Data ElementName="unique_id" Type="int32" Size="1" /><!-- 4 -->
|
||||
<Data ElementName="bag_id" Type="int32" Size="1" /><!-- 8 -->
|
||||
<Data ElementName="inv_slot_id" Type="int32" Size="1" /><!-- 12 -->
|
||||
<Data ElementName="menu_type" Type="int32" Size="1" /><!-- 16 -->
|
||||
<Data ElementName="slot_id" Type="int8" Size="1" /><!-- 17 -->
|
||||
<Data ElementName="index" Type="int16" Size="1" /><!-- 19 -->
|
||||
<Data ElementName="icon" Type="int16" Size="1" /> <!-- 21 -->
|
||||
<Data ElementName="count" Type="int8" Size="1" /> <!-- 23 -->
|
||||
<Data ElementName="level" Type="int8" Size="1" /> <!-- 24 -->
|
||||
<Data ElementName="tier" Type="int8" Size="1" /> <!-- 25 -->
|
||||
<Data ElementName="num_slots" Type="int8" Size="1" /> <!-- 26 -->
|
||||
<Data ElementName="item_id" Type="sint32" Size="1" /> <!-- 27 -->
|
||||
<Data ElementName="name" Type="char" Size="64" />
|
||||
<Data ElementName="unknown6" Type="int8" Size="17" />
|
||||
</Struct>
|
||||
<Struct Name="Substruct_Item" ClientVersion="547" >
|
||||
<Data ElementName="unique_id" Type="int32" Size="1" />
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
|
||||
function cast(Caster, Target, DmgType, MinVal, MaxVal)
|
||||
|
||||
local damageInflicted = false;
|
||||
-- Inflicts 12 - 20 piercing damage on target
|
||||
if MaxVal ~= nil and MinVal < MaxVal then
|
||||
SpellDamage(Target, DmgType, math.random(MinVal, MaxVal))
|
||||
damageInflicted = SpellDamage(Target, DmgType, math.random(MinVal, MaxVal))
|
||||
else
|
||||
SpellDamage(Target, DmgType, MinVal)
|
||||
damageInflicted = SpellDamage(Target, DmgType, MinVal)
|
||||
end
|
||||
|
||||
-- Applies Knockdown on termination. Lasts for 1.5 seconds.
|
||||
|
@ -20,8 +21,7 @@ function cast(Caster, Target, DmgType, MinVal, MaxVal)
|
|||
-- Stuns target
|
||||
-- Blurs vision of target
|
||||
-- Does not affect Epic targets
|
||||
if not IsEpic(Target) then
|
||||
Knockback(Caster, Target, 1500)
|
||||
if damageInflicted and not IsEpic(Target) then
|
||||
AddControlEffect(Target, 4)
|
||||
BlurVision(Target, 1.0)
|
||||
AddSpellTimer(1500, "RemoveStunBlur")
|
||||
|
|
|
@ -2938,6 +2938,21 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="spell" Type="int8" />
|
||||
<Data ElementName="spell_name" Type="EQ2_16Bit_String" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_HearSiphonSpellDamage" ClientVersion="546" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd">
|
||||
<Data ElementName="header" Substruct="WS_HearDamage_Header" Size="1" />
|
||||
<Data ElementName="num_dmg" Type="int8" />
|
||||
<Data ElementName="siphon_type" Type="int8" />
|
||||
<Data ElementName="dmg_array" Type="Array" ArraySizeVariable="num_dmg">
|
||||
<Data ElementName="damage_type" Type="int8" />
|
||||
<Data ElementName="damage" Type="int16" />
|
||||
<Data ElementName="unknown1" Type="int8" />
|
||||
<Data ElementName="unknown2" Type="int8" />
|
||||
<Data ElementName="crit_flag" Type="int8" /> <!-- 4==crit -->
|
||||
<Data ElementName="unknown4" Type="int8" />
|
||||
</Data>
|
||||
<Data ElementName="spell" Type="int8" />
|
||||
<Data ElementName="spell_name" Type="EQ2_16Bit_String" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_HearSiphonSpellDamage" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd">
|
||||
<Data ElementName="header" Substruct="WS_HearDamage_Header" Size="1" />
|
||||
<Data ElementName="siphon_type" Type="int8" />
|
||||
|
@ -3094,8 +3109,8 @@ to zero and treated like placeholders." />
|
|||
<Data ElementName="header" Substruct="WS_HearDamage_Header" Size="1" />
|
||||
<Data ElementName="siphon_type" Type="int8" />
|
||||
<Data ElementName="siphon_subtype" Type="int8" />
|
||||
<Data ElementName="damage" Type="int16" />
|
||||
<Data ElementName="unknown1" Type="int8" Size="2" />
|
||||
<Data ElementName="damage" Type="int32" />
|
||||
<Data ElementName="unknown1" Type="int8" Size="5" />
|
||||
<Data ElementName="spell_name" Type="EQ2_8Bit_String" Size="1" />
|
||||
<!-- All Hear spell damages so far seem to have new bytes at the end (who knows for how long) -->
|
||||
<Data ElementName="unknown2" Type="int8" Size="5" />
|
||||
|
@ -3132,12 +3147,20 @@ to zero and treated like placeholders." />
|
|||
<!-- All Hear spell damages so far seem to have new bytes at the end (who knows for how long) -->
|
||||
<Data ElementName="unknown2" Type="int8" Size="5" />
|
||||
</Struct>
|
||||
<!-- WS_HearHeal may be innaccurate, copied from DoF -->
|
||||
<Struct Name="WS_HearHeal" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearHealCmd">
|
||||
<Data ElementName="caster" Type="int32" />
|
||||
<Data ElementName="target" Type="int32" />
|
||||
<Data ElementName="heal_amt" Type="int32" />
|
||||
<Data ElementName="heal_amt" Type="int16" />
|
||||
<Data ElementName="spellname" Type="EQ2_16Bit_String" Size="1"/>
|
||||
<Data ElementName="type" Type="int16" />
|
||||
<Data ElementName="type" Type="int8" />
|
||||
</Struct>
|
||||
<Struct Name="WS_HearHeal" ClientVersion="546" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearHealCmd">
|
||||
<Data ElementName="caster" Type="int32" />
|
||||
<Data ElementName="target" Type="int32" />
|
||||
<Data ElementName="heal_amt" Type="int16" />
|
||||
<Data ElementName="spellname" Type="EQ2_16Bit_String" Size="1"/>
|
||||
<Data ElementName="type" Type="int8" />
|
||||
</Struct>
|
||||
<Struct Name="WS_HearHeal" ClientVersion="57048" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearHealCmd">
|
||||
<Data ElementName="caster" Type="int32" />
|
||||
|
|
Loading…
Reference in a new issue