Minor tweaks.

* The server now refuses to start if any JSONs fail to load
* Mobs now take height into account when losing aggro on a player
* Mobs now aggro on the closest player in range, rather then on the
earliest one to connect to the server of the ones in range
* /summonW now works in IZs and Lairs as well
* Lowered the extra height that mobs spawn at with /summonW to prevent
glitching problems
This commit is contained in:
dongresource 2020-10-18 04:13:33 +02:00
parent 3ce8cf2129
commit bbd695cad1
3 changed files with 53 additions and 23 deletions

View File

@ -203,7 +203,7 @@ void mssCommand(std::string full, std::vector<std::string>& args, CNSocket* sock
void summonWCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) { void summonWCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
if (args.size() < 2) { if (args.size() < 2) {
ChatManager::sendServerMessage(sock, "/level: no mob type specified"); ChatManager::sendServerMessage(sock, "/summonW: no mob type specified");
return; return;
} }
Player* plr = PlayerManager::getPlayer(sock); Player* plr = PlayerManager::getPlayer(sock);
@ -225,7 +225,7 @@ void summonWCommand(std::string full, std::vector<std::string>& args, CNSocket*
BaseNPC *npc = nullptr; BaseNPC *npc = nullptr;
if (team == 2) { if (team == 2) {
npc = new Mob(plr->x, plr->y, plr->z + 1000, plr->instanceID, type, NPCManager::NPCData[type], NPCManager::nextId++); npc = new Mob(plr->x, plr->y, plr->z + 200, plr->instanceID, type, NPCManager::NPCData[type], NPCManager::nextId++);
npc->appearanceData.iAngle = (plr->angle + 180) % 360; npc->appearanceData.iAngle = (plr->angle + 180) % 360;
NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc;
@ -240,9 +240,22 @@ void summonWCommand(std::string full, std::vector<std::string>& args, CNSocket*
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z); NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z);
// if we're in a lair, we need to spawn the mob in both the private instance and the template
if ((plr->instanceID >> 32) != 0) {
npc = new Mob(plr->x, plr->y, plr->z + 200, plr->instanceID & 0xffffffff, type, NPCManager::NPCData[type], NPCManager::nextId++);
npc->appearanceData.iAngle = (plr->angle + 180) % 360;
NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc;
MobManager::Mobs[npc->appearanceData.iNPC_ID] = (Mob*)npc;
((Mob*)npc)->summoned = false;
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z);
}
ChatManager::sendServerMessage(sock, "/summonW: placed mob with type: " + std::to_string(type) + ChatManager::sendServerMessage(sock, "/summonW: placed mob with type: " + std::to_string(type) +
", id: " + std::to_string(npc->appearanceData.iNPC_ID)); ", id: " + std::to_string(npc->appearanceData.iNPC_ID));
TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; // only record the one in the template
} }
void unsummonWCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) { void unsummonWCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {

View File

@ -8,6 +8,7 @@
#include "TransportManager.hpp" #include "TransportManager.hpp"
#include <cmath> #include <cmath>
#include <limits.h>
#include <assert.h> #include <assert.h>
std::map<int32_t, Mob*> MobManager::Mobs; std::map<int32_t, Mob*> MobManager::Mobs;
@ -501,7 +502,8 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
} }
// retreat if the player leaves combat range // retreat if the player leaves combat range
distance = hypot(plr->x - mob->roamX, plr->y - mob->roamY); int xyDistance = hypot(plr->x - mob->roamX, plr->y - mob->roamY);
distance = hypot(xyDistance, plr->z - mob->roamZ);
if (distance >= mob->data["m_iCombatRange"]) { if (distance >= mob->data["m_iCombatRange"]) {
mob->target = nullptr; mob->target = nullptr;
mob->state = MobState::RETREAT; mob->state = MobState::RETREAT;
@ -1034,6 +1036,9 @@ void MobManager::resendMobHP(Mob *mob) {
* as the mob, since it might be near a chunk boundary. * as the mob, since it might be near a chunk boundary.
*/ */
bool MobManager::aggroCheck(Mob *mob, time_t currTime) { bool MobManager::aggroCheck(Mob *mob, time_t currTime) {
CNSocket *closest = nullptr;
int closestDistance = INT_MAX;
for (Chunk *chunk : mob->currentChunks) { for (Chunk *chunk : mob->currentChunks) {
for (CNSocket *s : chunk->players) { for (CNSocket *s : chunk->players) {
Player *plr = s->plr; Player *plr = s->plr;
@ -1053,22 +1058,28 @@ bool MobManager::aggroCheck(Mob *mob, time_t currTime) {
int xyDistance = hypot(mob->appearanceData.iX - plr->x, mob->appearanceData.iY - plr->y); int xyDistance = hypot(mob->appearanceData.iX - plr->x, mob->appearanceData.iY - plr->y);
int distance = hypot(xyDistance, mob->appearanceData.iZ - plr->z); int distance = hypot(xyDistance, mob->appearanceData.iZ - plr->z);
if (distance > mobRange) if (distance > mobRange || distance > closestDistance)
continue; continue;
// found player. engage. // found a player
mob->target = s; closest = s;
mob->state = MobState::COMBAT; closestDistance = distance;
mob->nextMovement = currTime;
mob->nextAttack = 0;
mob->roamX = mob->appearanceData.iX;
mob->roamY = mob->appearanceData.iY;
mob->roamZ = mob->appearanceData.iZ;
return true;
} }
} }
if (closest != nullptr) {
// found closest player. engage.
mob->target = closest;
mob->state = MobState::COMBAT;
mob->nextMovement = currTime;
mob->nextAttack = 0;
mob->roamX = mob->appearanceData.iX;
mob->roamY = mob->appearanceData.iY;
mob->roamZ = mob->appearanceData.iZ;
return true;
}
return false; return false;
} }

View File

@ -50,7 +50,8 @@ void TableData::init() {
} }
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl; std::cerr << "[FATAL] Malformed NPCs.json file! Reason:" << err.what() << std::endl;
terminate(0);
} }
// load everything else from xdttable // load everything else from xdttable
@ -176,7 +177,8 @@ void TableData::init() {
std::cout << "[INFO] Loaded " << NanoManager::NanoTable.size() << " nanos" << std::endl; std::cout << "[INFO] Loaded " << NanoManager::NanoTable.size() << " nanos" << std::endl;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[WARN] Malformed xdt.json file! Reason:" << err.what() << std::endl; std::cerr << "[FATAL] Malformed xdt.json file! Reason:" << err.what() << std::endl;
terminate(0);
} }
// load temporary mob dump // load temporary mob dump
@ -203,7 +205,8 @@ void TableData::init() {
std::cout << "[INFO] Populated " << NPCManager::NPCs.size() << " NPCs" << std::endl; std::cout << "[INFO] Populated " << NPCManager::NPCs.size() << " NPCs" << std::endl;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[WARN] Malformed mobs.json file! Reason:" << err.what() << std::endl; std::cerr << "[FATAL] Malformed mobs.json file! Reason:" << err.what() << std::endl;
terminate(0);
} }
loadDrops(); loadDrops();
@ -300,7 +303,8 @@ void TableData::loadPaths(int* nextId) {
std::cout << "[INFO] Loaded " << TransportManager::NPCQueues.size() << " NPC paths" << std::endl; std::cout << "[INFO] Loaded " << TransportManager::NPCQueues.size() << " NPC paths" << std::endl;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[WARN] Malformed paths.json file! Reason:" << err.what() << std::endl; std::cerr << "[FATAL] Malformed paths.json file! Reason:" << err.what() << std::endl;
terminate(0);
} }
} }
@ -413,7 +417,8 @@ void TableData::loadDrops() {
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[WARN] Malformed drops.json file! Reason:" << err.what() << std::endl; std::cerr << "[FATAL] Malformed drops.json file! Reason:" << err.what() << std::endl;
terminate(0);
} }
} }
@ -563,7 +568,8 @@ void TableData::loadGruntwork(int32_t *nextId) {
std::cout << "[INFO] Loaded gruntwork.json" << std::endl; std::cout << "[INFO] Loaded gruntwork.json" << std::endl;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[WARN] Malformed gruntwork.json file! Reason:" << err.what() << std::endl; std::cerr << "[FATAL] Malformed gruntwork.json file! Reason:" << err.what() << std::endl;
terminate(0);
} }
} }
@ -623,7 +629,7 @@ void TableData::flush() {
mob["iX"] = m->spawnX; mob["iX"] = m->spawnX;
mob["iY"] = m->spawnY; mob["iY"] = m->spawnY;
mob["iZ"] = m->spawnZ; mob["iZ"] = m->spawnZ;
mob["iMapNum"] = m->instanceID; mob["iMapNum"] = m->instanceID & 0xffffffff;
// this is a bit imperfect, since this is a live angle, not a spawn angle so it'll change often, but eh // this is a bit imperfect, since this is a live angle, not a spawn angle so it'll change often, but eh
mob["iAngle"] = m->appearanceData.iAngle; mob["iAngle"] = m->appearanceData.iAngle;