From 31621065807b3342f480704756774848235d7086 Mon Sep 17 00:00:00 2001 From: Emagi Date: Fri, 10 Jan 2025 19:08:47 -0500 Subject: [PATCH] Added Lua Functions PlayAnimationString(Spawn: Spawn, String: EmoteStringCommand, Spawn: OptTarget, Boolean: SetNoTarget, Boolean: UseAllSpellTargets, Boolean: IgnoreSelf) and GetSpellTargets(Optional_Spell) if no argument must be in spell script. Fixed spells with no range that are group based to apply to all in group. --- source/WorldServer/LuaFunctions.cpp | 71 +++++++++++++++++++++++ source/WorldServer/LuaFunctions.h | 2 + source/WorldServer/LuaInterface.cpp | 2 + source/WorldServer/SpellProcess.cpp | 89 +++++++++++++++-------------- source/WorldServer/zoneserver.cpp | 13 ++++- source/WorldServer/zoneserver.h | 2 +- 6 files changed, 133 insertions(+), 46 deletions(-) diff --git a/source/WorldServer/LuaFunctions.cpp b/source/WorldServer/LuaFunctions.cpp index 91911ed..9b6e2d5 100644 --- a/source/WorldServer/LuaFunctions.cpp +++ b/source/WorldServer/LuaFunctions.cpp @@ -7134,6 +7134,39 @@ int EQ2Emu_lua_PlayAnimation(lua_State* state) { return 0; } +int EQ2Emu_lua_PlayAnimationString(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* spawn = lua_interface->GetSpawn(state); + std::string name = lua_interface->GetStringValue(state, 2); + Spawn* opt_target = lua_interface->GetSpawn(state, 3); + bool set_no_target = lua_interface->GetBooleanValue(state, 4); + bool use_all_spelltargets = lua_interface->GetBooleanValue(state, 5); + bool ignore_self = lua_interface->GetBooleanValue(state, 6); + LuaSpell* spell = lua_interface->GetCurrentSpell(state); + + if (!spawn) { + lua_interface->LogError("%s: LUA PlayAnimationString command error: spawn is not valid", lua_interface->GetScriptName(state)); + return 0; + } + + if (spell && spell->caster && spell->caster->GetZone() && use_all_spelltargets) { + Spawn* target; + spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); + for (int8 i = 0; i < spell->targets.size(); i++) { + target = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + if(target && (!ignore_self || spawn != target)) { + spell->caster->GetZone()->HandleEmote(target, name, opt_target, set_no_target); + } + } + spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); + } + else { + spawn->GetZone()->HandleEmote(spawn, name, opt_target, set_no_target); + } + return 0; +} + int EQ2Emu_lua_IsPet(lua_State* state) { if (!lua_interface) return 0; @@ -14254,6 +14287,44 @@ int EQ2Emu_lua_GetSpellInitialTarget(lua_State* state) { return 0; } +int EQ2Emu_lua_GetSpellTargets(lua_State* state) { + if (!lua_interface) + return 0; + LuaSpell* spell = lua_interface->GetSpell(state); + + if(!spell) { + spell = lua_interface->GetCurrentSpell(state); + } + + lua_interface->ResetFunctionStack(state); + + if (spell) { + if(!spell->caster) { + lua_interface->LogError("%s: LUA GetSpellTargets command error, caster does not exist.", lua_interface->GetScriptName(state)); + return 0; + } + if(!spell->caster->GetZone()) { + lua_interface->LogError("%s: LUA GetSpellTargets command error, zone does not exist.", lua_interface->GetScriptName(state)); + return 0; + } + + if (spell && spell->caster) { + ZoneServer* zone = spell->caster->GetZone(); + spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); + lua_createtable(state, spell->targets.size(), 0); + int newTable = lua_gettop(state); + for (int32 i = 0; i < spell->targets.size(); i++) { + Spawn* spawn = zone->GetSpawnByID(spell->targets.at(i)); + lua_interface->SetSpawnValue(state, spawn); + lua_rawseti(state, newTable, i + 1); + } + spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); + return 1; + } + } + return 0; +} + int EQ2Emu_lua_DespawnByLocationID(lua_State* state) { ZoneServer* zone = lua_interface->GetZone(state); int32 location_id = lua_interface->GetInt32Value(state, 2); diff --git a/source/WorldServer/LuaFunctions.h b/source/WorldServer/LuaFunctions.h index 79d0103..2e9fa65 100644 --- a/source/WorldServer/LuaFunctions.h +++ b/source/WorldServer/LuaFunctions.h @@ -202,6 +202,7 @@ int EQ2Emu_lua_PlayFlavorID(lua_State* state); int EQ2Emu_lua_PlaySound(lua_State* state); int EQ2Emu_lua_PlayVoice(lua_State* state); int EQ2Emu_lua_PlayAnimation(lua_State* state); +int EQ2Emu_lua_PlayAnimationString(lua_State* state); int EQ2Emu_lua_AddLootItem(lua_State* state); int EQ2Emu_lua_HasLootItem(lua_State* state); int EQ2Emu_lua_RemoveLootItem(lua_State* state); @@ -658,6 +659,7 @@ int EQ2Emu_lua_GetCharacterFlag(lua_State* state); int EQ2Emu_lua_ToggleCharacterFlag(lua_State* state); int EQ2Emu_lua_GetSpellInitialTarget(lua_State* state); +int EQ2Emu_lua_GetSpellTargets(lua_State* state); int EQ2Emu_lua_DespawnByLocationID(lua_State* state); diff --git a/source/WorldServer/LuaInterface.cpp b/source/WorldServer/LuaInterface.cpp index 0e26420..5ec08f8 100644 --- a/source/WorldServer/LuaInterface.cpp +++ b/source/WorldServer/LuaInterface.cpp @@ -1114,6 +1114,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "PlaySound", EQ2Emu_lua_PlaySound); lua_register(state, "PlayVoice", EQ2Emu_lua_PlayVoice); lua_register(state, "PlayAnimation", EQ2Emu_lua_PlayAnimation); + lua_register(state, "PlayAnimationString", EQ2Emu_lua_PlayAnimationString); lua_register(state, "AddLootItem", EQ2Emu_lua_AddLootItem); lua_register(state, "HasLootItem", EQ2Emu_lua_HasLootItem); lua_register(state, "RemoveLootItem", EQ2Emu_lua_RemoveLootItem); @@ -1563,6 +1564,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "ToggleCharacterFlag", EQ2Emu_lua_ToggleCharacterFlag); lua_register(state, "GetSpellInitialTarget", EQ2Emu_lua_GetSpellInitialTarget); + lua_register(state, "GetSpellTargets", EQ2Emu_lua_GetSpellTargets); lua_register(state,"DespawnByLocationID", EQ2Emu_lua_DespawnByLocationID); diff --git a/source/WorldServer/SpellProcess.cpp b/source/WorldServer/SpellProcess.cpp index dd2a721..af8dfea 100644 --- a/source/WorldServer/SpellProcess.cpp +++ b/source/WorldServer/SpellProcess.cpp @@ -2583,7 +2583,7 @@ bool SpellProcess::GetPlayerGroupTargets(Player* target, Spawn* caster, LuaSpell continue; else if (info && info->client && info->client->GetPlayer()->GetZone() == ((Player*)target)->GetZone() && info->client->GetPlayer()->Alive() - && (bypassRangeChecks || caster->GetDistance((Entity*)info->client->GetPlayer()) <= luaspell->spell->GetSpellData()->range)) + && (bypassRangeChecks || luaspell->spell->GetSpellData()->range == 0 || (luaspell->spell->GetSpellData()->range > 0 && caster->GetDistance((Entity*)info->client->GetPlayer()) <= luaspell->spell->GetSpellData()->range))) { AddSelfAndPet(luaspell, info->client->GetPlayer()); } @@ -2600,53 +2600,58 @@ bool SpellProcess::GetPlayerGroupTargets(Player* target, Spawn* caster, LuaSpell void SpellProcess::GetSpellTargetsTrueAOE(LuaSpell* luaspell) { if (luaspell && luaspell->caster && luaspell->spell && luaspell->spell->GetSpellData()->max_aoe_targets > 0) { - if (luaspell->caster->HasTarget() && luaspell->caster->GetTarget() != luaspell->caster){ - //Check if the caster has an implied target - if (luaspell->caster->GetDistance(luaspell->caster->GetTarget()) <= luaspell->spell->GetSpellData()->radius) - { - luaspell->initial_target = luaspell->caster->GetTarget()->GetID(); - luaspell->initial_target_char_id = (luaspell->caster->GetTarget() && luaspell->caster->GetTarget()->IsPlayer()) ? ((Player*)luaspell->caster->GetTarget())->GetCharacterID() : 0; - } + if(luaspell->caster->IsPlayer() && luaspell->spell->GetSpellData()->affect_only_group_members) { + GetPlayerGroupTargets((Player*)luaspell->caster, luaspell->caster, luaspell); } - int32 ignore_target = 0; - std::vector> spawns = luaspell->caster->GetZone()->GetAttackableSpawnsByDistance(luaspell->caster, luaspell->spell->GetSpellData()->radius); - luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - int32 i = 0; - for (const auto& pair : spawns) { - if (i == 0){ - Spawn* spawn = luaspell->caster->GetZone()->GetSpawnByID(luaspell->initial_target); - if (spawn && luaspell->initial_target && luaspell->caster->GetID() != luaspell->initial_target && luaspell->caster->AttackAllowed((Entity*)spawn)){ - //this is the "Direct" target and aoe can't be avoided - AddLuaSpellTarget(luaspell, luaspell->initial_target, false); - ignore_target = luaspell->initial_target; + else { + if (luaspell->caster->HasTarget() && luaspell->caster->GetTarget() != luaspell->caster){ + //Check if the caster has an implied target + if (luaspell->caster->GetDistance(luaspell->caster->GetTarget()) <= luaspell->spell->GetSpellData()->radius) + { + luaspell->initial_target = luaspell->caster->GetTarget()->GetID(); + luaspell->initial_target_char_id = (luaspell->caster->GetTarget() && luaspell->caster->GetTarget()->IsPlayer()) ? ((Player*)luaspell->caster->GetTarget())->GetCharacterID() : 0; } } - - i++; - - if (luaspell->targets.size() >= luaspell->spell->GetSpellData()->max_aoe_targets) - break; - - int32 target_id = pair.first; - Spawn* spawn = luaspell->caster->GetZone()->GetSpawnByID(target_id); - if(!spawn) { - LogWrite(SPELL__ERROR, 0, "Spell", "Error: Spell target is NULL! SpellProcess::ProcessSpell for Spell '%s' target id %u", (luaspell->spell != nullptr) ? luaspell->spell->GetName() : "Unknown", target_id); - } - //If we have already added this spawn, check the next spawn in the list - if (spawn && spawn->GetID() == ignore_target || (spawn->IsEntity() && !luaspell->caster->AttackAllowed((Entity*)spawn))){ - continue; - } - if (spawn){ - //If this spawn is immune to aoe, continue - if (((Entity*)spawn)->IsAOEImmune() || ((Entity*)spawn)->IsMezzed()) + int32 ignore_target = 0; + std::vector> spawns = luaspell->caster->GetZone()->GetAttackableSpawnsByDistance(luaspell->caster, luaspell->spell->GetSpellData()->radius); + luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__); + int32 i = 0; + for (const auto& pair : spawns) { + if (i == 0){ + Spawn* spawn = luaspell->caster->GetZone()->GetSpawnByID(luaspell->initial_target); + if (spawn && luaspell->initial_target && luaspell->caster->GetID() != luaspell->initial_target && luaspell->caster->AttackAllowed((Entity*)spawn)){ + //this is the "Direct" target and aoe can't be avoided + AddLuaSpellTarget(luaspell, luaspell->initial_target, false); + ignore_target = luaspell->initial_target; + } + } + + i++; + + if (luaspell->targets.size() >= luaspell->spell->GetSpellData()->max_aoe_targets) + break; + + int32 target_id = pair.first; + Spawn* spawn = luaspell->caster->GetZone()->GetSpawnByID(target_id); + if(!spawn) { + LogWrite(SPELL__ERROR, 0, "Spell", "Error: Spell target is NULL! SpellProcess::ProcessSpell for Spell '%s' target id %u", (luaspell->spell != nullptr) ? luaspell->spell->GetName() : "Unknown", target_id); + } + //If we have already added this spawn, check the next spawn in the list + if (spawn && spawn->GetID() == ignore_target || (spawn->IsEntity() && !luaspell->caster->AttackAllowed((Entity*)spawn))){ continue; - AddLuaSpellTarget(luaspell, spawn->GetID(), false); - } + } + if (spawn){ + //If this spawn is immune to aoe, continue + if (((Entity*)spawn)->IsAOEImmune() || ((Entity*)spawn)->IsMezzed()) + continue; + AddLuaSpellTarget(luaspell, spawn->GetID(), false); + } - if (luaspell->targets.size() >= luaspell->spell->GetSpellData()->max_aoe_targets) - break; + if (luaspell->targets.size() >= luaspell->spell->GetSpellData()->max_aoe_targets) + break; + } + luaspell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); } - luaspell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); } if (luaspell->targets.size() > 20) LogWrite(SPELL__DEBUG, 0, "Spell", "Warning in SpellProcess::GetSpellTargetsTrueAOE Size of targets array is %u", luaspell->targets.size()); diff --git a/source/WorldServer/zoneserver.cpp b/source/WorldServer/zoneserver.cpp index c8bd6a3..28d3722 100644 --- a/source/WorldServer/zoneserver.cpp +++ b/source/WorldServer/zoneserver.cpp @@ -6822,12 +6822,19 @@ void ZoneServer::RemoveSpawnSupportFunctions(Spawn* spawn, bool lock_spell_proce movement_spawns.erase(spawn->GetID()); } -void ZoneServer::HandleEmote(Spawn* originator, string name) { +void ZoneServer::HandleEmote(Spawn* originator, string name, Spawn* opt_target, bool no_target) { if (!originator) { LogWrite(ZONE__ERROR, 0, "Zone", "HandleEmote called with an invalid client"); return; } + Spawn* target = originator->GetTarget(); + if(opt_target) + target = opt_target; + + if(no_target) // override having a target + target = nullptr; + Client* orig_client = (originator->IsPlayer() && ((Player*)originator)->GetClient()) ? ((Player*)originator)->GetClient() : nullptr; Client* client = 0; int32 cur_client_version = orig_client ? orig_client->GetVersion() : 546; @@ -6875,10 +6882,10 @@ void ZoneServer::HandleEmote(Spawn* originator, string name) { packet->setDataByName("spawn_id" , client->GetPlayer()->GetIDWithPlayerSpawn(originator)); if(!emoteResponse){ string message; - if(originator->GetTarget() && originator->GetTarget()->GetID() != originator->GetID()){ + if(target && target->GetID() != originator->GetID()){ message = emote->GetTargetedMessageString(); if(message.find("%t") < 0xFFFFFFFF) - message.replace(message.find("%t"), 2, originator->GetTarget()->GetName()); + message.replace(message.find("%t"), 2, target->GetName()); } if(message.length() == 0) message = emote->GetMessageString(); diff --git a/source/WorldServer/zoneserver.h b/source/WorldServer/zoneserver.h index 25edddb..691b200 100644 --- a/source/WorldServer/zoneserver.h +++ b/source/WorldServer/zoneserver.h @@ -335,7 +335,7 @@ public: void SendSpawnVisualState(Spawn* spawn, int16 type); void SendSpellFailedPacket(Client* client, int16 error); void SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool fizzle=false); - void HandleEmote(Spawn* originator, string name); + void HandleEmote(Spawn* originator, string name, Spawn* opt_target = nullptr, bool no_target = false); Spawn* GetSpawnByDatabaseID(int32 id); Spawn* GetSpawnByID(int32 id, bool spawnListLocked=false);