diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index f33759d1a..d18796a4e 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -5978,6 +5978,16 @@ bool Perl__aretaskscompleted(perl::array task_ids) return quest_manager.aretaskscompleted(v); } +void Perl__SpawnCircle(uint32 npc_id, float x, float y, float z, float heading, float radius, uint32 points) +{ + quest_manager.SpawnCircle(npc_id, glm::vec4(x, y, z, heading), radius, points); +} + +void Perl__SpawnGrid(uint32 npc_id, float x, float y, float z, float heading, float spacing, uint32 spawn_count) +{ + quest_manager.SpawnGrid(npc_id, glm::vec4(x, y, z, heading), spacing, spawn_count); +} + void perl_register_quest() { perl::interpreter perl(PERL_GET_THX); @@ -6287,6 +6297,8 @@ void perl_register_quest() package.add("SendMail", &Perl__SendMail); package.add("SetAutoLoginCharacterNameByAccountID", &Perl__SetAutoLoginCharacterNameByAccountID); package.add("SetRunning", &Perl__SetRunning); + package.add("SpawnCircle", &Perl__SpawnCircle); + package.add("SpawnGrid", &Perl__SpawnGrid); package.add("activespeakactivity", &Perl__activespeakactivity); package.add("activespeaktask", &Perl__activespeaktask); package.add("activetasksinset", &Perl__activetasksinset); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index f8a8a4ee4..6567d2476 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -5635,6 +5635,16 @@ int lua_are_tasks_completed(luabind::object task_ids) return quest_manager.aretaskscompleted(v); } +void lua_spawn_circle(uint32 npc_id, float x, float y, float z, float heading, float radius, uint32 points) +{ + quest_manager.SpawnCircle(npc_id, glm::vec4(x, y, z, heading), radius, points); +} + +void lua_spawn_grid(uint32 npc_id, float x, float y, float z, float heading, float spacing, uint32 spawn_count) +{ + quest_manager.SpawnGrid(npc_id, glm::vec4(x, y, z, heading), spacing, spawn_count); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -6442,6 +6452,8 @@ luabind::scope lua_register_general() { luabind::def("send_parcel", &lua_send_parcel), luabind::def("get_zone_uptime", &lua_get_zone_uptime), luabind::def("are_tasks_completed", &lua_are_tasks_completed), + luabind::def("spawn_circle", &lua_spawn_circle), + luabind::def("spawn_grid", &lua_spawn_grid), /* Cross Zone */ diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index cf77e503e..68bd859af 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4623,3 +4623,78 @@ bool QuestManager::SetAutoLoginCharacterNameByAccountID(uint32 account_id, const { return AccountRepository::SetAutoLoginCharacterNameByAccountID(database, account_id, character_name); } + +void QuestManager::SpawnCircle(uint32 npc_id, glm::vec4 position, float radius, uint32 points) +{ + const NPCType* t = content_db.LoadNPCTypesData(npc_id); + if (!t) { + return; + } + + glm::vec4 npc_position = position; + + for (uint32 i = 0; i < points; i++) { + float angle = 2 * M_PI * i / points; + + npc_position.x = position.x + radius * std::cos(angle); + npc_position.y = position.y + radius * std::sin(angle); + + NPC* n = new NPC(t, nullptr, npc_position, GravityBehavior::Water); + + n->FixZ(); + + n->AddLootTable(); + + if (n->DropsGlobalLoot()) { + n->CheckGlobalLootTables(); + } + + entity_list.AddNPC(n, true, true); + } +} + +void QuestManager::SpawnGrid(uint32 npc_id, glm::vec4 position, float spacing, uint32 spawn_count) +{ + const NPCType* t = content_db.LoadNPCTypesData(npc_id); + if (!t) { + return; + } + + glm::vec4 npc_position = position; + + uint32 columns = std::ceil(std::sqrt(spawn_count)); + uint32 rows = std::ceil(spawn_count / columns); + + float total_width = ((columns - 1) * spacing); + float total_height = ((rows - 1) * spacing); + + float start_x = position.x - total_width / 2; + float start_y = position.y - total_height / 2; + + uint32 spawned = 0; + + for (uint32 row = 0; row < rows; row++) { + for (uint32 column = 0; column < columns; column++) { + if (spawned >= spawn_count) { + break; + } + + npc_position.x = start_x + column * spacing; + npc_position.y = start_y + row * spacing; + + NPC* n = new NPC(t, nullptr, npc_position, GravityBehavior::Water); + + n->FixZ(); + + n->AddLootTable(); + + if (n->DropsGlobalLoot()) { + n->CheckGlobalLootTables(); + } + + entity_list.AddNPC(n, true, true); + + spawned++; + } + } +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 463a3baa6..1f872aaf5 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -357,6 +357,8 @@ public: void SendChannelMessage(Client* from, const char* to, uint8 channel_number, uint32 guild_id, uint8 language_id, uint8 language_skill, const char* message); std::string GetAutoLoginCharacterNameByAccountID(uint32 account_id); bool SetAutoLoginCharacterNameByAccountID(uint32 account_id, const std::string& character_name); + void SpawnCircle(uint32 npc_id, glm::vec4 position, float radius, uint32 points); + void SpawnGrid(uint32 npc_id, glm::vec4 position, float spacing, uint32 spawn_count); Bot *GetBot() const; Client *GetInitiator() const;