1
0

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.

This commit is contained in:
Emagi 2025-01-10 19:08:47 -05:00
parent c24f0c89fc
commit 3162106580
6 changed files with 133 additions and 46 deletions

View File

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

View File

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

View File

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

View File

@ -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<std::pair<int32, float>> 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<std::pair<int32, float>> 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());

View File

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

View File

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