mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-12-23 11:50:04 +00:00
Implemented static paths for mobs (like Don Doom and Bad Max).
* Mobs now retreat to where they were when they were roaming; not their spawn point * /toggleai still sends them back to their spawn point however * Fixed a bug where mobs would respawn where they were killed instead of their proper spawn point * Fixed mobs roaming despite simulatemobs being set to false * Updated tdata submodule
This commit is contained in:
parent
c792fb9d0d
commit
599bbedd8c
@ -34,6 +34,7 @@ std::string U16toU8(char16_t* src);
|
||||
size_t U8toU16(std::string src, char16_t* des, size_t max); // returns number of char16_t that was written at des
|
||||
time_t getTime();
|
||||
time_t getTimestamp();
|
||||
void terminate(int);
|
||||
|
||||
// The PROTOCOL_VERSION definition is defined by the build system.
|
||||
#if !defined(PROTOCOL_VERSION)
|
||||
|
@ -45,7 +45,6 @@ bool runCmd(std::string full, CNSocket* sock) {
|
||||
void helpCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
|
||||
ChatManager::sendServerMessage(sock, "Commands available to you");
|
||||
Player *plr = PlayerManager::getPlayer(sock);
|
||||
int i = 1;
|
||||
|
||||
for (auto& cmd : ChatManager::commands) {
|
||||
if (cmd.second.requiredAccLevel >= plr->accountId)
|
||||
@ -280,6 +279,10 @@ void toggleAiCommand(std::string full, std::vector<std::string>& args, CNSocket*
|
||||
for (auto& pair : MobManager::Mobs) {
|
||||
pair.second->state = MobState::RETREAT;
|
||||
pair.second->target = nullptr;
|
||||
|
||||
pair.second->roamX = pair.second->spawnX;
|
||||
pair.second->roamY = pair.second->spawnY;
|
||||
pair.second->roamZ = pair.second->spawnZ;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,6 +210,10 @@ int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) {
|
||||
mob->state = MobState::COMBAT;
|
||||
mob->nextMovement = getTime();
|
||||
mob->nextAttack = 0;
|
||||
|
||||
mob->roamX = mob->appearanceData.iX;
|
||||
mob->roamY = mob->appearanceData.iY;
|
||||
mob->roamZ = mob->appearanceData.iZ;
|
||||
}
|
||||
|
||||
mob->appearanceData.iHP -= damage;
|
||||
@ -254,7 +258,29 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
||||
}
|
||||
}
|
||||
|
||||
// delay the despawn animation
|
||||
mob->despawned = false;
|
||||
|
||||
auto it = TransportManager::NPCQueues.find(mob->appearanceData.iNPC_ID);
|
||||
if (it == TransportManager::NPCQueues.end() || it->second.empty())
|
||||
return;
|
||||
|
||||
// rewind or empty the movement queue
|
||||
if (mob->staticPath) {
|
||||
/*
|
||||
* This is inelegant, but we wind forward in the path until we find the point that
|
||||
* corresponds with the Mob's spawn point.
|
||||
*
|
||||
* IMPORTANT: The check in TableData::loadPaths() must pass or else this will loop forever.
|
||||
*/
|
||||
auto& queue = it->second;
|
||||
for (auto point = queue.front(); point.x != mob->spawnX || point.y != mob->spawnY; point = queue.front()) {
|
||||
queue.pop();
|
||||
queue.push(point);
|
||||
}
|
||||
} else {
|
||||
TransportManager::NPCQueues.erase(mob->appearanceData.iNPC_ID);
|
||||
}
|
||||
}
|
||||
|
||||
void MobManager::deadStep(Mob *mob, time_t currTime) {
|
||||
@ -284,6 +310,11 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
|
||||
mob->appearanceData.iHP = mob->maxHealth;
|
||||
mob->state = MobState::ROAMING;
|
||||
|
||||
// reset position
|
||||
mob->appearanceData.iX = mob->spawnX;
|
||||
mob->appearanceData.iY = mob->spawnY;
|
||||
mob->appearanceData.iZ = mob->spawnZ;
|
||||
|
||||
INITSTRUCT(sP_FE2CL_NPC_NEW, pkt);
|
||||
|
||||
pkt.NPCAppearanceData = mob->appearanceData;
|
||||
@ -360,7 +391,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
}
|
||||
|
||||
// retreat if the player leaves combat range
|
||||
distance = hypot(plr->x - mob->spawnX, plr->y - mob->spawnY);
|
||||
distance = hypot(plr->x - mob->roamX, plr->y - mob->roamY);
|
||||
if (distance >= mob->data["m_iCombatRange"]) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
@ -390,14 +421,18 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
||||
if (mob->idleRange == 0)
|
||||
return;
|
||||
|
||||
// no random roaming if the mob already has a set path
|
||||
if (mob->staticPath)
|
||||
return;
|
||||
|
||||
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
||||
return;
|
||||
incNextMovement(mob, currTime);
|
||||
|
||||
// only calculate a new route if we're not already on one
|
||||
auto it = TransportManager::NPCQueues.find(mob->appearanceData.iNPC_ID);
|
||||
if (it != TransportManager::NPCQueues.end() && it->second.empty())
|
||||
return;
|
||||
/*
|
||||
* mob->nextMovement is also updated whenever the path queue is traversed in
|
||||
* TransportManager::stepNPCPathing() (which ticks at a higher frequency than nextMovement),
|
||||
* so we don't have to check if there's already entries in the queue since we know there won't be.
|
||||
*/
|
||||
|
||||
int xStart = mob->spawnX - mob->idleRange/2;
|
||||
int yStart = mob->spawnY - mob->idleRange/2;
|
||||
@ -437,13 +472,13 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
||||
|
||||
mob->nextMovement = currTime + 500;
|
||||
|
||||
int distance = hypot(mob->appearanceData.iX - mob->spawnX, mob->appearanceData.iY - mob->spawnY);
|
||||
int distance = hypot(mob->appearanceData.iX - mob->roamX, mob->appearanceData.iY - mob->roamY);
|
||||
|
||||
//if (distance > mob->data["m_iIdleRange"]) {
|
||||
if (distance > 10) {
|
||||
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
|
||||
|
||||
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->spawnX, mob->spawnY, (int)mob->data["m_iRunSpeed"] * 3);
|
||||
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->roamX, mob->roamY, (int)mob->data["m_iRunSpeed"] * 3);
|
||||
|
||||
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||
pkt.iSpeed = (int)mob->data["m_iRunSpeed"] * 3;
|
||||
@ -720,10 +755,12 @@ std::pair<int,int> MobManager::getDamage(int attackPower, int defensePower, bool
|
||||
if (attackPower + defensePower * 2 == 0)
|
||||
return ret;
|
||||
|
||||
// base calculation
|
||||
int damage = attackPower * attackPower / (attackPower + defensePower);
|
||||
damage = std::max(std::max(29, attackPower / 7), damage - defensePower * (12 + difficulty) / 65);
|
||||
damage = damage * (rand() % 40 + 80) / 100;
|
||||
|
||||
// Adaptium/Blastons/Cosmix
|
||||
if (attackerStyle != -1 && defenderStyle != -1 && attackerStyle != defenderStyle) {
|
||||
if (attackerStyle < defenderStyle || attackerStyle - defenderStyle == 2)
|
||||
damage = damage * 5 / 4;
|
||||
@ -731,6 +768,7 @@ std::pair<int,int> MobManager::getDamage(int attackPower, int defensePower, bool
|
||||
damage = damage * 4 / 5;
|
||||
}
|
||||
|
||||
// weapon boosts
|
||||
if (batteryBoost)
|
||||
damage = damage * 5 / 4;
|
||||
|
||||
@ -913,6 +951,11 @@ bool MobManager::aggroCheck(Mob *mob, time_t currTime) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ struct Mob : public BaseNPC {
|
||||
// combat
|
||||
CNSocket *target = nullptr;
|
||||
time_t nextAttack = 0;
|
||||
int roamX, roamY, roamZ;
|
||||
|
||||
// temporary; until we're sure what's what
|
||||
nlohmann::json data;
|
||||
@ -57,9 +58,9 @@ struct Mob : public BaseNPC {
|
||||
if (regenTime >= 300000000)
|
||||
regenTime = 1500;
|
||||
|
||||
spawnX = appearanceData.iX;
|
||||
spawnY = appearanceData.iY;
|
||||
spawnZ = appearanceData.iZ;
|
||||
roamX = spawnX = appearanceData.iX;
|
||||
roamY = spawnY = appearanceData.iY;
|
||||
roamZ = spawnZ = appearanceData.iZ;
|
||||
|
||||
appearanceData.iConditionBitFlag = 0;
|
||||
|
||||
|
@ -44,8 +44,6 @@ void TableData::init() {
|
||||
std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl;
|
||||
}
|
||||
|
||||
loadPaths(&nextId); // load paths
|
||||
|
||||
// load everything else from xdttable
|
||||
std::cout << "[INFO] Parsing xdt.json..." << std::endl;
|
||||
std::ifstream infile(settings::XDTJSON);
|
||||
@ -198,6 +196,8 @@ void TableData::init() {
|
||||
std::cerr << "[WARN] Malformed mobs.json file! Reason:" << err.what() << std::endl;
|
||||
}
|
||||
|
||||
loadPaths(&nextId); // load paths
|
||||
|
||||
loadGruntwork(&nextId);
|
||||
|
||||
NPCManager::nextId = nextId;
|
||||
@ -264,6 +264,27 @@ void TableData::loadPaths(int* nextId) {
|
||||
for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) {
|
||||
constructPathNPC(npcPath);
|
||||
}
|
||||
|
||||
// mob paths
|
||||
pathDataNPC = pathData["mob"];
|
||||
for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) {
|
||||
for (auto& pair : MobManager::Mobs) {
|
||||
if (pair.second->appearanceData.iNPCType == npcPath.value()["iNPCType"]) {
|
||||
std::cout << "[INFO] Using static path for mob " << pair.second->appearanceData.iNPCType << " with ID " << pair.first << std::endl;
|
||||
|
||||
auto firstPoint = npcPath.value()["points"][0];
|
||||
if (firstPoint["iX"] != pair.second->spawnX || firstPoint["iY"] != pair.second->spawnY) {
|
||||
std::cout << "[FATAL] The first point of the route for mob " << pair.first <<
|
||||
" (type " << pair.second->appearanceData.iNPCType << ") does not correspond with its spawn point." << std::endl;
|
||||
terminate(0);
|
||||
}
|
||||
|
||||
constructPathNPC(npcPath, pair.first);
|
||||
pair.second->staticPath = true;
|
||||
break; // only one NPC per path
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "[INFO] Loaded " << TransportManager::NPCQueues.size() << " NPC paths" << std::endl;
|
||||
}
|
||||
catch (const std::exception& err) {
|
||||
@ -319,7 +340,7 @@ void TableData::constructPathSlider(nlohmann::json points, int rotations, int sl
|
||||
TransportManager::NPCQueues[sliderID] = route;
|
||||
}
|
||||
|
||||
void TableData::constructPathNPC(nlohmann::json::iterator _pathData) {
|
||||
void TableData::constructPathNPC(nlohmann::json::iterator _pathData, int32_t id) {
|
||||
auto pathData = _pathData.value();
|
||||
// Interpolate
|
||||
nlohmann::json pathPoints = pathData["points"];
|
||||
@ -337,7 +358,11 @@ void TableData::constructPathNPC(nlohmann::json::iterator _pathData) {
|
||||
from = to; // update point A
|
||||
stopTime = point["stop"];
|
||||
}
|
||||
TransportManager::NPCQueues[pathData["iNPCID"]] = points;
|
||||
|
||||
if (id == 0)
|
||||
id = pathData["iNPCID"];
|
||||
|
||||
TransportManager::NPCQueues[id] = points;
|
||||
}
|
||||
|
||||
// load gruntwork output; if it exists
|
||||
|
@ -18,5 +18,5 @@ namespace TableData {
|
||||
void loadPaths(int*);
|
||||
void constructPathSkyway(nlohmann::json::iterator);
|
||||
void constructPathSlider(nlohmann::json, int, int);
|
||||
void constructPathNPC(nlohmann::json::iterator);
|
||||
void constructPathNPC(nlohmann::json::iterator, int id=0);
|
||||
}
|
||||
|
@ -282,6 +282,12 @@ void TransportManager::stepNPCPathing() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip if not simulating mobs
|
||||
if (npc->npcClass == NPC_MOB && !MobManager::simulateMobs) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not roam if not roaming
|
||||
if (npc->npcClass == NPC_MOB && ((Mob*)npc)->state != MobState::ROAMING) {
|
||||
it++;
|
||||
|
2
tdata
2
tdata
@ -1 +1 @@
|
||||
Subproject commit 9c595541b3803664359a19379a2cef9b1c2cdc65
|
||||
Subproject commit f9430771644d21b2c7c350ab0cf46e27987e91f3
|
Loading…
Reference in New Issue
Block a user