diff --git a/src/ChatManager.cpp b/src/ChatManager.cpp index d75ada8..21286e6 100644 --- a/src/ChatManager.cpp +++ b/src/ChatManager.cpp @@ -523,6 +523,119 @@ void playersCommand(std::string full, std::vector& args, CNSocket* ChatManager::sendServerMessage(sock, PlayerManager::getPlayerName(pair.second)); } +void summonGroupWCommand(std::string full, std::vector& args, CNSocket* sock) { + if (args.size() < 4) { + ChatManager::sendServerMessage(sock, "/summonGroupW [distance]"); + return; + } + Player* plr = PlayerManager::getPlayer(sock); + + char *rest; + int type = std::strtol(args[1].c_str(), &rest, 10); + int type2 = std::strtol(args[2].c_str(), &rest, 10); + int count = std::strtol(args[3].c_str(), &rest, 10); + int distance = 150; + if (args.size() > 4) + distance = std::strtol(args[4].c_str(), &rest, 10); + + if (*rest) { + ChatManager::sendServerMessage(sock, "Invalid NPC number: " + args[1]); + return; + } + + // permission & sanity check + if (plr == nullptr || type >= 3314 || type2 >= 3314 || count > 5) + return; + + Mob* leadNpc = nullptr; + + for (int i = 0; i < count; i++) { + int team = NPCManager::NPCData[type]["m_iTeam"]; + assert(NPCManager::nextId < INT32_MAX); + + +#define EXTRA_HEIGHT 200 + BaseNPC *npc = nullptr; + int id = NPCManager::nextId++; + + int x = plr->x; + int y = plr->y; + int z = plr->z; + if (i > 0) { + int angle = 360.0f / (count-1) * (i-1); + if (count == 3) + angle = 90 + 60 * i; + + angle += (plr->angle + 180) % 360; + + x += -1.0f * sin(angle / 180.0f * M_PI) * distance; + y += -1.0f * cos(angle / 180.0f * M_PI) * distance; + z = plr->z; + } + + if (team == 2) { + npc = new Mob(x, y, z + EXTRA_HEIGHT, plr->instanceID, type, NPCManager::NPCData[type], id); + MobManager::Mobs[npc->appearanceData.iNPC_ID] = (Mob*)npc; + + if (i > 0) { + leadNpc->groupMember[i-1] = npc->appearanceData.iNPC_ID; + Mob* mob = MobManager::Mobs[npc->appearanceData.iNPC_ID]; + mob->groupLeader = leadNpc->appearanceData.iNPC_ID; + mob->offsetX = x - plr->x; + mob->offsetY = y - plr->y; + } + + // re-enable respawning + ((Mob*)npc)->summoned = false; + } else { + npc = new BaseNPC(x, y, z + EXTRA_HEIGHT, 0, plr->instanceID, type, id); + } + + npc->appearanceData.iAngle = (plr->angle + 180) % 360; + NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; + + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, x, y, z); + + // if we're in a lair, we need to spawn the NPC in both the private instance and the template + if (PLAYERID(plr->instanceID) != 0) { + id = NPCManager::nextId++; + + if (team == 2) { + npc = new Mob(x, y, z + EXTRA_HEIGHT, MAPNUM(plr->instanceID), type, NPCManager::NPCData[type], id); + + MobManager::Mobs[npc->appearanceData.iNPC_ID] = (Mob*)npc; + + if (i > 0) { + leadNpc->groupMember[i-1] = npc->appearanceData.iNPC_ID; + Mob* mob = MobManager::Mobs[npc->appearanceData.iNPC_ID]; + mob->groupLeader = leadNpc->appearanceData.iNPC_ID; + mob->offsetX = x - plr->x; + mob->offsetY = y - plr->y; + } + + ((Mob*)npc)->summoned = false; + } else { + npc = new BaseNPC(x, y, z + EXTRA_HEIGHT, 0, MAPNUM(plr->instanceID), type, id); + } + + npc->appearanceData.iAngle = (plr->angle + 180) % 360; + NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; + + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, x, y, z); + } + + ChatManager::sendServerMessage(sock, "/summonGroupW: placed mob with type: " + std::to_string(type) + + ", id: " + std::to_string(npc->appearanceData.iNPC_ID)); + TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; // only record the one in the template + + if (i == 0 && team == 2) { + type = type2; + leadNpc = MobManager::Mobs[npc->appearanceData.iNPC_ID]; + leadNpc->groupLeader = leadNpc->appearanceData.iNPC_ID; + } + } +} + void flushCommand(std::string full, std::vector& args, CNSocket* sock) { TableData::flush(); ChatManager::sendServerMessage(sock, "Wrote gruntwork to " + settings::GRUNTWORKJSON); @@ -553,6 +666,7 @@ void ChatManager::init() { registerCommand("tasks", 30, tasksCommand, "list all active missions and their respective task ids."); registerCommand("notify", 30, notifyCommand, "receive a message whenever a player joins the server"); registerCommand("players", 30, playersCommand, "print all players on the server"); + registerCommand("summonGroupW", 30, summonGroupWCommand, "permanently summon group NPCs"); } void ChatManager::registerCommand(std::string cmd, int requiredLevel, CommandHandler handlr, std::string help) { diff --git a/src/TableData.cpp b/src/TableData.cpp index f80ed3c..a492804 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -641,6 +641,8 @@ void TableData::loadGruntwork(int32_t *nextId) { // mobs auto mobs = gruntwork["mobs"]; + int leaderMob = -1; + int leaderMobFollowers = 0; for (auto _mob = mobs.begin(); _mob != mobs.end(); _mob++) { auto mob = _mob.value(); BaseNPC *npc; @@ -655,6 +657,37 @@ void TableData::loadGruntwork(int32_t *nextId) { ((Mob*)npc)->summoned = false; MobManager::Mobs[npc->appearanceData.iNPC_ID] = (Mob*)npc; + + // handling groups + if (mob.find("iOffsetX") != mob.end() && MobManager::Mobs.find(id) != MobManager::Mobs.end()) { + Mob* currNpc = MobManager::Mobs[id]; + + if (leaderMob == -1) { + if (MobManager::Mobs.find(id-1) != MobManager::Mobs.end()) { + Mob* leadNpc = MobManager::Mobs[id-1]; + leaderMob = id-1; + leadNpc->groupMember[leaderMobFollowers] = id; + leaderMobFollowers++; + currNpc->groupLeader = id-1; + leadNpc->groupLeader = id-1; + } + } else { + if (MobManager::Mobs.find(leaderMob) != MobManager::Mobs.end()) { + Mob* leadNpc = MobManager::Mobs[leaderMob]; + leaderMob = leaderMob; + leadNpc->groupMember[leaderMobFollowers] = id; + leaderMobFollowers++; + currNpc->groupLeader = leaderMob; + } + } + + currNpc->offsetX = (int)mob["iOffsetX"]; + currNpc->offsetY = mob.find("iOffsetY") == mob.end() ? 0 : (int)mob["iOffsetY"]; + } else { + leaderMob = -1; + leaderMobFollowers = 0; + } + } else { npc = new BaseNPC(mob["iX"], mob["iY"], mob["iZ"], mob["iAngle"], instanceID, mob["iNPCType"], id); } @@ -737,12 +770,19 @@ void TableData::flush() { continue; int x, y, z, hp; + int offsetX = 0; + int offsetY = 0; if (npc->npcClass == NPC_MOB) { Mob *m = (Mob*)npc; x = m->spawnX; y = m->spawnY; z = m->spawnZ; hp = m->maxHealth; + // handling groups + if (m->groupLeader != 0 && m->groupLeader != m->appearanceData.iNPC_ID) { + offsetX = m->offsetX; + offsetY = m->offsetY; + } } else { x = npc->appearanceData.iX; y = npc->appearanceData.iY; @@ -760,6 +800,12 @@ void TableData::flush() { // this is a bit imperfect, since this is a live angle, not a spawn angle so it'll change often, but eh mob["iAngle"] = npc->appearanceData.iAngle; + // there is an assumption that group mobs will never have an offset of (0,0) + if (offsetX != 0 || offsetY != 0) { + mob["iOffsetX"] = offsetX; + mob["iOffsetY"] = offsetY; + } + // it's called mobs, but really it's everything gruntwork["mobs"].push_back(mob); }