[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
This commit is contained in:
twincannon 2024-05-16 15:17:37 -04:00 committed by GitHub
parent 1be86edf20
commit c87aadbf0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 115 additions and 42 deletions

View File

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

View File

@ -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 {

View File

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

View File

@ -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<uint8_t>(EQ::skills::SkillHandtoHand);
e.sec_melee_type = static_cast<uint8_t>(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);

View File

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