From c87aadbf0c2fef846aed368e55a74ccd09aca184 Mon Sep 17 00:00:00 2001 From: twincannon Date: Thu, 16 May 2024 15:17:37 -0400 Subject: [PATCH] [Commands] #npcspawn Changes (#4311) * Changes to npcspawn create command so it takes more stats from the target npc * Add see invis stats to npccreate command * WIP npcspawn command changes * Add npcspawn clone and help args, fix some broken things with npcspawn * Cleanup comments and add apostraphes to zone shortname query * Make it so npcspawn remove only removes spawn2 row and optionally removes spawngroup and spawnentry, make create and add spawn the npc at client loc * Make npcspawn create use the same syntax for spawngroup naming as npcspawn add * Revert npcspawn create and add to use npc location rather than client, other misc tweaks --- zone/client.cpp | 16 +++++-- zone/gm_commands/npcedit.cpp | 9 ++-- zone/gm_commands/npcspawn.cpp | 47 ++++++++++++-------- zone/npc.cpp | 83 ++++++++++++++++++++++++++++------- zone/zonedb.h | 2 +- 5 files changed, 115 insertions(+), 42 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 171bb73c5..c95f995e1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6372,9 +6372,9 @@ void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra) bool is_delete = spawn_type.find("delete") != std::string::npos; bool is_remove = spawn_type.find("remove") != std::string::npos; bool is_update = spawn_type.find("update") != std::string::npos; + bool is_clone = spawn_type.find("clone") != std::string::npos; if (is_add || is_create) { - // Add: extra tries to create the NPC ID within the range for the current Zone (Zone ID * 1000) - // Create: extra sets the Respawn Timer for add + // extra sets the Respawn Timer for add/create content_db.NPCSpawnDB( is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, zone->GetShortName(), @@ -6398,7 +6398,17 @@ void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra) zone->GetShortName(), zone->GetInstanceVersion(), this, - target_npc->CastToNPC() + target_npc->CastToNPC(), + extra + ); + } else if (is_clone) { + content_db.NPCSpawnDB( + NPCSpawnTypes::AddSpawnFromSpawngroup, + zone->GetShortName(), + zone->GetInstanceVersion(), + this, + target_npc->CastToNPC(), + extra ); } } diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp index fe68ed771..cd6d00d23 100755 --- a/zone/gm_commands/npcedit.cpp +++ b/zone/gm_commands/npcedit.cpp @@ -1666,7 +1666,7 @@ void command_npcedit(Client *c, const Seperator *sep) } else if (!strcasecmp(sep->arg[1], "set_grid")) { if (sep->IsNumber(2)) { const uint32 grid_id = Strings::ToUnsignedInt(sep->arg[2]); - if (grid_id) { + if (grid_id >= 0) { d = fmt::format( "{} now has a Grid ID of {} on Spawn Group ID {}.", npc_id_string, @@ -1674,14 +1674,15 @@ void command_npcedit(Client *c, const Seperator *sep) Strings::Commify(std::to_string(t->GetSpawnGroupId())) ); auto query = fmt::format( - "UPDATE spawn2 SET pathgrid = {} WHERE spawngroupID = {} AND version = {}", + "UPDATE spawn2 SET pathgrid = {} WHERE spawngroupID = {} AND version = {} AND zone = '{}'", grid_id, t->GetSpawnGroupId(), - zone->GetInstanceVersion() + zone->GetInstanceVersion(), + zone->GetShortName() ); content_db.QueryDatabase(query); } else { - c->Message(Chat::White, "Grid ID must be greater than 0."); + c->Message(Chat::White, "Grid ID must be greater than or equal to 0."); return; } } else { diff --git a/zone/gm_commands/npcspawn.cpp b/zone/gm_commands/npcspawn.cpp index 8c1815214..4e97bdf38 100755 --- a/zone/gm_commands/npcspawn.cpp +++ b/zone/gm_commands/npcspawn.cpp @@ -9,44 +9,46 @@ void command_npcspawn(Client *c, const Seperator *sep) int arguments = sep->argnum; if (!arguments) { - c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update|Clone|Help]"); + return; + } + + if (!strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update|Clone|Help] [optional 3rd parameter]"); + c->Message(Chat::White, "Usage: #npcspawn add [respawntime] - Using the same targeted NPC ID, creates new spawn2 and spawngroup entries"); + c->Message(Chat::White, "Usage: #npcspawn clone [respawntime] - Copies targeted NPC and spawngroup, creating only a spawn2 entry at the current client location"); + c->Message(Chat::White, "Usage: #npcspawn create [respawntime] - Creates new NPC type copying the data from the targeted NPC, with new spawn2 and spawngroup entries"); + c->Message(Chat::White, "Usage: #npcspawn delete - Deletes the spawn2, spawngroup, spawnentry and npc_types rows for targeted NPC"); + c->Message(Chat::White, "Usage: #npcspawn remove [remove_spawngroups] - Deletes the spawn2 row for targeted NPC, also delete spawngroup and spawnentry rows if remove_spawngroups is > 0"); + c->Message(Chat::White, "Usage: #npcspawn update - Updates NPC appearance in database"); return; } auto target = c->GetTarget()->CastToNPC(); uint32 extra = 0; bool is_add = !strcasecmp(sep->arg[1], "add"); + bool is_clone = !strcasecmp(sep->arg[1], "clone"); bool is_create = !strcasecmp(sep->arg[1], "create"); bool is_delete = !strcasecmp(sep->arg[1], "delete"); bool is_remove = !strcasecmp(sep->arg[1], "remove"); bool is_update = !strcasecmp(sep->arg[1], "update"); if ( !is_add && + !is_clone && !is_create && !is_delete && !is_remove && !is_update ) { - c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update|Clone|Help]"); return; } - if (is_add || is_create) { - extra = ( - sep->IsNumber(2) ? - ( - is_add ? - Strings::ToInt(sep->arg[2]) : - 1 - ) : ( - is_add ? - 1200 : - 0 - ) - ); // Default to 1200 for Add, 0 for Create if not set + if (is_add || is_create || is_clone) { + extra = sep->IsNumber(2) ? Strings::ToInt(sep->arg[2]) : 1200; // Extra param is only used for respawn time in Add/Create/Clone, default to 1200 if not set content_db.NPCSpawnDB( - is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, + is_add ? NPCSpawnTypes::AddNewSpawngroup : (is_create ? NPCSpawnTypes::CreateNewSpawn : NPCSpawnTypes::AddSpawnFromSpawngroup), zone->GetShortName(), zone->GetInstanceVersion(), c, @@ -58,7 +60,13 @@ void command_npcspawn(Client *c, const Seperator *sep) Chat::White, fmt::format( "Spawn {} | Name: {}", - is_add ? "Added" : "Created", + is_add ? + "Added" : + ( + is_create ? + "Created" : + "Cloned" + ), c->GetTargetDescription(target) ).c_str() ); @@ -84,12 +92,15 @@ void command_npcspawn(Client *c, const Seperator *sep) ) ); + extra = sep->IsNumber(2) ? Strings::ToInt(sep->arg[2]) : 0; // Extra param is used in Remove as a flag to optionally remove spawngroup/spawnentry if 1 (always remove spawn2 entry) + content_db.NPCSpawnDB( spawn_update_type, zone->GetShortName(), zone->GetInstanceVersion(), c, - target + target, + extra ); c->Message( diff --git a/zone/npc.cpp b/zone/npc.cpp index 970c849a2..59f5d893d 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1209,6 +1209,7 @@ uint32 ZoneDatabase::CreateNewNPCCommand( e.race = n->GetRace(); e.class_ = n->GetClass(); e.hp = n->GetMaxHP(); + e.mana = n->GetMaxMana(); e.gender = n->GetGender(); e.texture = n->GetTexture(); e.helmtexture = n->GetHelmTexture(); @@ -1216,8 +1217,50 @@ uint32 ZoneDatabase::CreateNewNPCCommand( e.loottable_id = n->GetLoottableID(); e.merchant_id = n->MerchantType; e.runspeed = n->GetRunspeed(); - e.prim_melee_type = static_cast(EQ::skills::SkillHandtoHand); - e.sec_melee_type = static_cast(EQ::skills::SkillHandtoHand); + e.walkspeed = n->GetWalkspeed(); + e.prim_melee_type = n->GetPrimSkill(); + e.sec_melee_type = n->GetSecSkill(); + + e.bodytype = n->GetBodyType(); + e.npc_faction_id = n->GetNPCFactionID(); + e.aggroradius = n->GetAggroRange(); + e.assistradius = n->GetAssistRange(); + + e.AC = n->GetAC(); + e.ATK = n->GetATK(); + e.STR = n->GetSTR(); + e.STA = n->GetSTA(); + e.AGI = n->GetAGI(); + e.DEX = n->GetDEX(); + e.WIS = n->GetWIS(); + e._INT = n->GetINT(); + e.CHA = n->GetCHA(); + + e.PR = n->GetPR(); + e.MR = n->GetMR(); + e.DR = n->GetDR(); + e.FR = n->GetFR(); + e.CR = n->GetCR(); + e.Corrup = n->GetCorrup(); + e.PhR = n->GetPhR(); + + e.Accuracy = n->GetAccuracyRating(); + e.slow_mitigation = n->GetSlowMitigation(); + e.mindmg = n->GetMinDMG(); + e.maxdmg = n->GetMaxDMG(); + e.hp_regen_rate = n->GetHPRegen(); + e.hp_regen_per_second = n->GetHPRegenPerSecond(); + //e.attack_delay = n->GetAttackDelay(); // Attack delay isn't copying correctly, 3000 becomes 18,400 in the copied NPC? + e.spellscale = n->GetSpellScale(); + e.healscale = n->GetHealScale(); + e.Avoidance = n->GetAvoidanceRating(); + e.heroic_strikethrough = n->GetHeroicStrikethrough(); + + e.see_hide = n->SeeHide(); + e.see_improved_hide = n->SeeImprovedHide(); + e.see_invis = n->SeeInvisible(); + e.see_invis_undead = n->SeeInvisibleUndead(); + e = NpcTypesRepository::InsertOne(*this, e); @@ -1228,9 +1271,10 @@ uint32 ZoneDatabase::CreateNewNPCCommand( auto sg = SpawngroupRepository::NewEntity(); sg.name = fmt::format( - "{}-{}", + "{}_{}_{}", zone, - n->GetName() + Strings::Escape(n->GetName()), + Timer::GetCurrentTime() ); sg = SpawngroupRepository::InsertOne(*this, sg); @@ -1249,7 +1293,7 @@ uint32 ZoneDatabase::CreateNewNPCCommand( s2.x = n->GetX(); s2.y = n->GetY(); s2.z = n->GetZ(); - s2.respawntime = 1200; + s2.respawntime = extra > 0 ? extra : 1200; s2.heading = n->GetHeading(); s2.spawngroupID = sg.id; @@ -1361,12 +1405,17 @@ uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client* c, NPC* n) return updated; } -uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n) +uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n, uint32 remove_spawngroup_id) { + if (!n->respawn2) { + return 0; + } + const auto& l = Spawn2Repository::GetWhere( *this, fmt::format( - "`zone` = '{}' AND `spawngroupID` = {}", + "`id` = {} AND `zone` = '{}' AND `spawngroupID` = {}", + n->respawn2->GetID(), zone, n->GetSpawnGroupId() ) @@ -1382,12 +1431,14 @@ uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Cli return 0; } - if (!SpawngroupRepository::DeleteOne(*this, e.spawngroupID)) { - return 0; - } + if (remove_spawngroup_id > 0) { + if (!SpawngroupRepository::DeleteOne(*this, e.spawngroupID)) { + return 0; + } - if (!SpawnentryRepository::DeleteOne(*this, e.spawngroupID)) { - return 0; + if (!SpawnentryRepository::DeleteOne(*this, e.spawngroupID)) { + return 0; + } } return 1; @@ -1440,7 +1491,7 @@ uint32 ZoneDatabase::AddSpawnFromSpawnGroup( uint32 instance_version, Client* c, NPC* n, - uint32 spawngroup_id + uint32 extra ) { auto e = Spawn2Repository::NewEntity(); @@ -1451,8 +1502,8 @@ uint32 ZoneDatabase::AddSpawnFromSpawnGroup( e.y = c->GetY(); e.z = c->GetZ(); e.heading = c->GetHeading(); - e.respawntime = 120; - e.spawngroupID = spawngroup_id; + e.respawntime = extra > 0 ? extra : 1200; + e.spawngroupID = n->GetSpawnGroupId(); e = Spawn2Repository::InsertOne(*this, e); @@ -1529,7 +1580,7 @@ uint32 ZoneDatabase::NPCSpawnDB( return UpdateNPCTypeAppearance(c, n); } case NPCSpawnTypes::RemoveSpawn: { - return DeleteSpawnLeaveInNPCTypeTable(zone, c, n); + return DeleteSpawnLeaveInNPCTypeTable(zone, c, n, extra); } case NPCSpawnTypes::DeleteSpawn: { return DeleteSpawnRemoveFromNPCTypeTable(zone, instance_version, c, n); diff --git a/zone/zonedb.h b/zone/zonedb.h index 2c522607e..ed3bccd8b 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -552,7 +552,7 @@ public: uint32 NPCSpawnDB(uint8 command, const std::string& zone, uint32 instance_version, Client *c, NPC* n = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete uint32 CreateNewNPCCommand(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 extra); uint32 AddNewNPCSpawnGroupCommand(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 in_respawn_time); - uint32 DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n); + uint32 DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n, uint32 remove_spawngroup_id); uint32 DeleteSpawnRemoveFromNPCTypeTable(const std::string& zone, uint32 instance_version, Client* c, NPC* n); uint32 AddSpawnFromSpawnGroup(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 spawngroup_id); uint32 AddNPCTypes(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 spawngroup_id);