mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-04 22:40:05 +00:00
Mobs roam proper distances now.
* Mob roaming is now integrated into the TransportManager * Doubled the roaming distance, since it was clearly too small * Tripled retreat speed * Made use of NPCManager::sendToViewable() in TransportManager
This commit is contained in:
parent
a53f38b87d
commit
901e011740
@ -5,6 +5,7 @@
|
||||
#include "ItemManager.hpp"
|
||||
#include "MissionManager.hpp"
|
||||
#include "GroupManager.hpp"
|
||||
#include "TransportManager.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <assert.h>
|
||||
@ -366,12 +367,14 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: precalculate a path, lerp through it, repeat.
|
||||
* The way it works right now, mobs only move around a little bit. This will result
|
||||
* in more natural movement, and will mesh well with pre-calculated paths (for Don Doom,
|
||||
* Bad Max, etc.) once those have been made.
|
||||
*/
|
||||
inline void MobManager::incNextMovement(Mob *mob, time_t currTime) {
|
||||
if (currTime == 0)
|
||||
currTime = getTime();
|
||||
|
||||
int delay = (int)mob->data["m_iDelayTime"] * 1000;
|
||||
mob->nextMovement = currTime + delay/2 + rand() % (delay/2);
|
||||
}
|
||||
|
||||
void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
||||
/*
|
||||
* We reuse nextAttack to avoid scanning for players all the time, but to still
|
||||
@ -389,34 +392,44 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
||||
|
||||
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
||||
return;
|
||||
incNextMovement(mob, currTime);
|
||||
|
||||
int delay = (int)mob->data["m_iDelayTime"] * 1000;
|
||||
mob->nextMovement = currTime + delay/2 + rand() % (delay/2);
|
||||
// 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;
|
||||
|
||||
std::cout << "charting new path for Mob " << (int)mob->appearanceData.iNPC_ID << std::endl;
|
||||
|
||||
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
|
||||
int xStart = mob->spawnX - mob->idleRange/2;
|
||||
int yStart = mob->spawnY - mob->idleRange/2;
|
||||
|
||||
int farX = xStart + rand() % mob->idleRange;
|
||||
int farY = yStart + rand() % mob->idleRange;
|
||||
int speed = mob->data["m_iWalkSpeed"];
|
||||
|
||||
int farX, farY;
|
||||
int distance; // for short walk detection
|
||||
|
||||
/*
|
||||
* We don't want the mob to just take one step and stop, so we make sure
|
||||
* it has walked a half-decent distance.
|
||||
*/
|
||||
do {
|
||||
farX = xStart + rand() % mob->idleRange;
|
||||
farY = yStart + rand() % mob->idleRange;
|
||||
|
||||
distance = std::hypot(mob->appearanceData.iX - farX, mob->appearanceData.iY - farY);
|
||||
} while (distance < 500);
|
||||
|
||||
// halve movement speed if snared
|
||||
if (mob->appearanceData.iConditionBitFlag & CSB_BIT_DN_MOVE_SPEED)
|
||||
speed /= 2;
|
||||
|
||||
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, farX, farY, speed);
|
||||
std::queue<WarpLocation> queue;
|
||||
WarpLocation from = { mob->appearanceData.iX, mob->appearanceData.iY, mob->appearanceData.iZ };
|
||||
WarpLocation to = { farX, farY, mob->appearanceData.iZ };
|
||||
|
||||
NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ);
|
||||
|
||||
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||
pkt.iSpeed = speed;
|
||||
pkt.iToX = mob->appearanceData.iX = targ.first;
|
||||
pkt.iToY = mob->appearanceData.iY = targ.second;
|
||||
pkt.iToZ = mob->appearanceData.iZ;
|
||||
|
||||
// notify all nearby players
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
// add a route to the queue; to be processed in TransportManager::stepNPCPathing()
|
||||
TransportManager::lerp(&queue, from, to, speed);
|
||||
TransportManager::NPCQueues[mob->appearanceData.iNPC_ID] = queue;
|
||||
}
|
||||
|
||||
void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
||||
@ -432,10 +445,10 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
||||
if (distance > 10) {
|
||||
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
|
||||
|
||||
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->spawnX, mob->spawnY, mob->data["m_iRunSpeed"]);
|
||||
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->spawnX, mob->spawnY, (int)mob->data["m_iRunSpeed"] * 3);
|
||||
|
||||
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||
pkt.iSpeed = mob->data["m_iRunSpeed"];
|
||||
pkt.iSpeed = (int)mob->data["m_iRunSpeed"] * 3;
|
||||
pkt.iToX = mob->appearanceData.iX = targ.first;
|
||||
pkt.iToY = mob->appearanceData.iY = targ.second;
|
||||
pkt.iToZ = mob->appearanceData.iZ;
|
||||
@ -702,7 +715,9 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) {
|
||||
lastHealTime = currTime;
|
||||
}
|
||||
|
||||
std::pair<int,int> MobManager::getDamage(int attackPower, int defensePower, bool shouldCrit, bool batteryBoost, int attackerStyle, int defenderStyle, int difficulty) {
|
||||
std::pair<int,int> MobManager::getDamage(int attackPower, int defensePower, bool shouldCrit,
|
||||
bool batteryBoost, int attackerStyle,
|
||||
int defenderStyle, int difficulty) {
|
||||
std::pair<int,int> ret = {0, 1};
|
||||
if (attackPower + defensePower * 2 == 0)
|
||||
return ret;
|
||||
@ -816,7 +831,8 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) {
|
||||
|
||||
int difficulty = (int)mob->data["m_iNpcLevel"];
|
||||
|
||||
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW >= 11 + difficulty), NanoManager::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty);
|
||||
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW >= 11 + difficulty),
|
||||
NanoManager::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty);
|
||||
|
||||
if (plr->batteryW >= 11 + difficulty)
|
||||
plr->batteryW -= 11 + difficulty;
|
||||
|
@ -35,6 +35,7 @@ struct Mob : public BaseNPC {
|
||||
// roaming
|
||||
int idleRange;
|
||||
time_t nextMovement = 0;
|
||||
bool staticPath = false;
|
||||
|
||||
// combat
|
||||
CNSocket *target = nullptr;
|
||||
@ -50,7 +51,7 @@ struct Mob : public BaseNPC {
|
||||
data = d;
|
||||
|
||||
regenTime = data["m_iRegenTime"];
|
||||
idleRange = data["m_iIdleRange"];
|
||||
idleRange = (int)data["m_iIdleRange"] * 2; // TODO: tuning?
|
||||
|
||||
// XXX: temporarily force respawns for Fusions until we implement instancing
|
||||
if (regenTime >= 300000000)
|
||||
@ -111,5 +112,6 @@ namespace MobManager {
|
||||
|
||||
void pcAttackChars(CNSocket *sock, CNPacketData *data);
|
||||
void resendMobHP(Mob *mob);
|
||||
void incNextMovement(Mob *mob, time_t currTime=0);
|
||||
bool aggroCheck(Mob *mob, time_t currTime);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "NanoManager.hpp"
|
||||
#include "TransportManager.hpp"
|
||||
#include "TableData.hpp"
|
||||
#include "MobManager.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <cmath>
|
||||
@ -275,12 +276,18 @@ void TransportManager::stepNPCPathing() {
|
||||
if (NPCManager::NPCs.find(it->first) != NPCManager::NPCs.end())
|
||||
npc = NPCManager::NPCs[it->first];
|
||||
|
||||
if (npc == nullptr) {
|
||||
if (npc == nullptr || queue->empty()) {
|
||||
// pluck out dead path + update iterator
|
||||
it = NPCQueues.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not roam if not roaming
|
||||
if (npc->npcClass == NPC_MOB && ((Mob*)npc)->state != MobState::ROAMING) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
||||
WarpLocation point = queue->front(); // get point
|
||||
queue->pop(); // remove point from front of queue
|
||||
|
||||
@ -291,10 +298,6 @@ void TransportManager::stepNPCPathing() {
|
||||
// update NPC location to update viewables
|
||||
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, point.x, point.y, point.z);
|
||||
|
||||
// get chunks in view
|
||||
auto chunk = ChunkManager::grabChunk(npc->appearanceData.iX, npc->appearanceData.iY, npc->instanceID);
|
||||
auto chunks = ChunkManager::grabChunks(chunk);
|
||||
|
||||
switch (npc->npcClass) {
|
||||
case NPC_BUS:
|
||||
INITSTRUCT(sP_FE2CL_TRANSPORTATION_MOVE, busMove);
|
||||
@ -306,13 +309,11 @@ void TransportManager::stepNPCPathing() {
|
||||
busMove.iToZ = point.z;
|
||||
busMove.iSpeed = distanceBetween; // set to distance to match how monkeys work
|
||||
|
||||
// send packet to players in view
|
||||
for (Chunk* chunk : chunks) {
|
||||
for (CNSocket* s : chunk->players) {
|
||||
s->sendPacket(&busMove, P_FE2CL_TRANSPORTATION_MOVE, sizeof(sP_FE2CL_TRANSPORTATION_MOVE));
|
||||
}
|
||||
}
|
||||
NPCManager::sendToViewable(npc, &busMove, P_FE2CL_TRANSPORTATION_MOVE, sizeof(sP_FE2CL_TRANSPORTATION_MOVE));
|
||||
break;
|
||||
case NPC_MOB:
|
||||
MobManager::incNextMovement((Mob*)npc);
|
||||
/* fallthrough */
|
||||
default:
|
||||
INITSTRUCT(sP_FE2CL_NPC_MOVE, move);
|
||||
move.iNPC_ID = npc->appearanceData.iNPC_ID;
|
||||
@ -322,16 +323,17 @@ void TransportManager::stepNPCPathing() {
|
||||
move.iToZ = point.z;
|
||||
move.iSpeed = distanceBetween;
|
||||
|
||||
// send packet to players in view
|
||||
for (Chunk* chunk : chunks) {
|
||||
for (CNSocket* s : chunk->players) {
|
||||
s->sendPacket(&move, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
}
|
||||
NPCManager::sendToViewable(npc, &move, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
break;
|
||||
}
|
||||
|
||||
queue->push(point); // move processed point to the back to maintain cycle
|
||||
/*
|
||||
* Move processed point to the back to maintain cycle, unless this is a
|
||||
* dynamically calculated mob route.
|
||||
*/
|
||||
if (!(npc->npcClass == NPC_MOB && !((Mob*)npc)->staticPath))
|
||||
queue->push(point);
|
||||
|
||||
it++; // go to next entry in map
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user