mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 21:40:05 +00:00
Mobs fight back now.
There is still a lot of tuning, lerping and cleanup to do.
This commit is contained in:
parent
006d1000c7
commit
1f18104a6f
@ -4,6 +4,7 @@
|
|||||||
#include "ItemManager.hpp"
|
#include "ItemManager.hpp"
|
||||||
#include "MissionManager.hpp"
|
#include "MissionManager.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
std::map<int32_t, Mob*> MobManager::Mobs;
|
std::map<int32_t, Mob*> MobManager::Mobs;
|
||||||
@ -59,13 +60,30 @@ void MobManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
|||||||
}
|
}
|
||||||
Mob *mob = Mobs[pktdata[i]];
|
Mob *mob = Mobs[pktdata[i]];
|
||||||
|
|
||||||
|
// cannot kill mobs multiple times; cannot harm retreating mobs
|
||||||
|
if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) {
|
||||||
|
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
||||||
|
respdata[i].iHP = mob->appearanceData.iHP;
|
||||||
|
respdata[i].iHitFlag = 2; // probably still necessary
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mob->state == MobState::ROAMING) {
|
||||||
|
assert(mob->target == nullptr);
|
||||||
|
mob->target = sock;
|
||||||
|
mob->state = MobState::COMBAT;
|
||||||
|
mob->nextMovement = getTime();
|
||||||
|
std::cout << "combat state\n";
|
||||||
|
}
|
||||||
|
|
||||||
mob->appearanceData.iHP -= 100;
|
mob->appearanceData.iHP -= 100;
|
||||||
|
|
||||||
// wake up sleeping monster
|
// wake up sleeping monster
|
||||||
|
// TODO: remove client-side bit somehow
|
||||||
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
|
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
|
||||||
|
|
||||||
if (mob->appearanceData.iHP <= 0)
|
if (mob->appearanceData.iHP <= 0)
|
||||||
killMob(sock, mob);
|
killMob(mob->target, mob);
|
||||||
|
|
||||||
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
||||||
respdata[i].iDamage = 100;
|
respdata[i].iDamage = 100;
|
||||||
@ -85,6 +103,38 @@ void MobManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
|||||||
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_NPCs, resplen);
|
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_NPCs, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MobManager::npcAttackPc(Mob *mob) {
|
||||||
|
// player pointer has already been validated
|
||||||
|
Player *plr = PlayerManager::getPlayer(mob->target);
|
||||||
|
|
||||||
|
const size_t resplen = sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC) + sizeof(sAttackResult);
|
||||||
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
||||||
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
|
sP_FE2CL_NPC_ATTACK_PCs *pkt = (sP_FE2CL_NPC_ATTACK_PCs*)respbuf;
|
||||||
|
sAttackResult *atk = (sAttackResult*)(respbuf + sizeof(sP_FE2CL_NPC_ATTACK_PCs));
|
||||||
|
|
||||||
|
plr->HP += (int)mob->data["m_iPower"]; // already negative
|
||||||
|
|
||||||
|
pkt->iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||||
|
pkt->iPCCnt = 1;
|
||||||
|
|
||||||
|
atk->iID = plr->iID;
|
||||||
|
atk->iDamage = -(int)mob->data["m_iPower"];
|
||||||
|
atk->iHP = plr->HP;
|
||||||
|
atk->iHitFlag = 2;
|
||||||
|
|
||||||
|
mob->target->sendPacket((void*)respbuf, P_FE2CL_NPC_ATTACK_PCs, resplen);
|
||||||
|
PlayerManager::sendToViewable(mob->target, (void*)&pkt, P_FE2CL_NPC_ATTACK_PCs, resplen);
|
||||||
|
|
||||||
|
if (plr->HP <= 0) {
|
||||||
|
mob->target = nullptr;
|
||||||
|
mob->state = MobState::RETREAT;
|
||||||
|
std::cout << "retreat state\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MobManager::combatBegin(CNSocket *sock, CNPacketData *data) {} // stub
|
void MobManager::combatBegin(CNSocket *sock, CNPacketData *data) {} // stub
|
||||||
void MobManager::combatEnd(CNSocket *sock, CNPacketData *data) {} // stub
|
void MobManager::combatEnd(CNSocket *sock, CNPacketData *data) {} // stub
|
||||||
void MobManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {} // stub
|
void MobManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {} // stub
|
||||||
@ -136,10 +186,11 @@ void MobManager::giveReward(CNSocket *sock) {
|
|||||||
|
|
||||||
void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
||||||
mob->state = MobState::DEAD;
|
mob->state = MobState::DEAD;
|
||||||
|
mob->target = nullptr;
|
||||||
mob->appearanceData.iConditionBitFlag = 0;
|
mob->appearanceData.iConditionBitFlag = 0;
|
||||||
mob->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
|
mob->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
|
||||||
|
|
||||||
std::cout << "killed mob " << mob->appearanceData.iNPC_ID << std::endl;
|
std::cout << "dead state mob " << mob->appearanceData.iNPC_ID << std::endl;
|
||||||
|
|
||||||
giveReward(sock);
|
giveReward(sock);
|
||||||
MissionManager::mobKilled(sock, mob->appearanceData.iNPCType);
|
MissionManager::mobKilled(sock, mob->appearanceData.iNPCType);
|
||||||
@ -173,6 +224,7 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
|
|||||||
|
|
||||||
mob->appearanceData.iHP = mob->maxHealth;
|
mob->appearanceData.iHP = mob->maxHealth;
|
||||||
mob->state = MobState::ROAMING;
|
mob->state = MobState::ROAMING;
|
||||||
|
std::cout << "roaming state\n";
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_NPC_NEW, pkt);
|
INITSTRUCT(sP_FE2CL_NPC_NEW, pkt);
|
||||||
|
|
||||||
@ -186,6 +238,68 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||||
|
assert(mob->target != nullptr);
|
||||||
|
|
||||||
|
// sanity check: did the target player lose connection?
|
||||||
|
if (PlayerManager::players.find(mob->target) == PlayerManager::players.end()) {
|
||||||
|
mob->target = nullptr;
|
||||||
|
mob->state = MobState::RETREAT;
|
||||||
|
std::cout << "RETREAT state\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Player *plr = PlayerManager::getPlayer(mob->target);
|
||||||
|
|
||||||
|
// skip attack/move if stunned or asleep
|
||||||
|
if (mob->appearanceData.iConditionBitFlag & (CSB_BIT_STUN|CSB_BIT_MEZ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int distance = hypot(plr->x - mob->appearanceData.iX, plr->y - mob->appearanceData.iY);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the mob is close enough to attack, do so. If not, get closer.
|
||||||
|
* No, I'm not 100% sure this is how it's supposed to work.
|
||||||
|
*/
|
||||||
|
if (distance <= (int)mob->data["m_iAtkRange"]) {
|
||||||
|
std::cout << "attack logic\n";
|
||||||
|
// attack logic
|
||||||
|
if (mob->nextAttack == 0) {
|
||||||
|
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; // I *think* this is what this is
|
||||||
|
npcAttackPc(mob);
|
||||||
|
} else if (mob->nextAttack != 0 && currTime >= mob->nextAttack) {
|
||||||
|
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
||||||
|
npcAttackPc(mob);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cout << "movement logic\n";
|
||||||
|
std::cout << "distance: " << distance << " attack range: " << mob->data["m_iAtkRange"] << std::endl;
|
||||||
|
// movement logic
|
||||||
|
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
||||||
|
return;
|
||||||
|
mob->nextMovement = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
||||||
|
|
||||||
|
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
|
||||||
|
|
||||||
|
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||||
|
pkt.iSpeed = mob->data["m_iWalkSpeed"];
|
||||||
|
pkt.iToX = mob->appearanceData.iX = mob->target->plr->x; // FIXME: lerping
|
||||||
|
pkt.iToY = mob->appearanceData.iY = mob->target->plr->y;
|
||||||
|
pkt.iToZ = mob->appearanceData.iZ = mob->target->plr->z;
|
||||||
|
|
||||||
|
auto chunk = ChunkManager::grabChunk(mob->appearanceData.iX, mob->appearanceData.iY);
|
||||||
|
auto chunks = ChunkManager::grabChunks(chunk);
|
||||||
|
|
||||||
|
// notify all nearby players
|
||||||
|
for (Chunk *chunk : chunks) {
|
||||||
|
for (CNSocket *s : chunk->players) {
|
||||||
|
s->sendPacket(&pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: retreat if kited too far
|
||||||
|
}
|
||||||
|
|
||||||
void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
||||||
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
||||||
return;
|
return;
|
||||||
@ -225,6 +339,41 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
||||||
|
// distance between spawn point and current location
|
||||||
|
int distance = hypot(mob->appearanceData.iX - mob->spawnX, mob->appearanceData.iY - mob->spawnY);
|
||||||
|
|
||||||
|
if (distance > mob->data["m_iIdleRange"]) {
|
||||||
|
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
|
||||||
|
|
||||||
|
auto chunk = ChunkManager::grabChunk(mob->appearanceData.iX, mob->appearanceData.iY);
|
||||||
|
auto chunks = ChunkManager::grabChunks(chunk);
|
||||||
|
|
||||||
|
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||||
|
pkt.iSpeed = mob->data["m_iWalkSpeed"];
|
||||||
|
pkt.iToX = mob->appearanceData.iX = mob->spawnX;
|
||||||
|
pkt.iToY = mob->appearanceData.iY = mob->spawnY;
|
||||||
|
pkt.iToZ = mob->appearanceData.iZ;
|
||||||
|
|
||||||
|
// notify all nearby players
|
||||||
|
for (Chunk *chunk : chunks) {
|
||||||
|
for (CNSocket *s : chunk->players) {
|
||||||
|
s->sendPacket(&pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got there
|
||||||
|
if (distance <= mob->data["m_iIdleRange"]) {
|
||||||
|
mob->state = MobState::ROAMING;
|
||||||
|
std::cout << "roaming state\n";
|
||||||
|
mob->appearanceData.iHP = mob->maxHealth;
|
||||||
|
mob->killedTime = 0;
|
||||||
|
mob->nextAttack = 0;
|
||||||
|
mob->appearanceData.iConditionBitFlag = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MobManager::step(CNServer *serv, time_t currTime) {
|
void MobManager::step(CNServer *serv, time_t currTime) {
|
||||||
for (auto& pair : Mobs) {
|
for (auto& pair : Mobs) {
|
||||||
int x = pair.second->appearanceData.iX;
|
int x = pair.second->appearanceData.iX;
|
||||||
@ -242,6 +391,12 @@ void MobManager::step(CNServer *serv, time_t currTime) {
|
|||||||
case MobState::ROAMING:
|
case MobState::ROAMING:
|
||||||
roamingStep(pair.second, currTime);
|
roamingStep(pair.second, currTime);
|
||||||
break;
|
break;
|
||||||
|
case MobState::COMBAT:
|
||||||
|
combatStep(pair.second, currTime);
|
||||||
|
break;
|
||||||
|
case MobState::RETREAT:
|
||||||
|
retreatStep(pair.second, currTime);
|
||||||
|
break;
|
||||||
case MobState::DEAD:
|
case MobState::DEAD:
|
||||||
deadStep(pair.second, currTime);
|
deadStep(pair.second, currTime);
|
||||||
break;
|
break;
|
||||||
|
@ -18,27 +18,38 @@ enum class MobState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Mob : public BaseNPC {
|
struct Mob : public BaseNPC {
|
||||||
|
// general
|
||||||
MobState state;
|
MobState state;
|
||||||
const int maxHealth;
|
const int maxHealth;
|
||||||
|
|
||||||
time_t killedTime = 0;
|
|
||||||
const int regenTime;
|
|
||||||
bool despawned = false; // for the sake of death animations
|
|
||||||
|
|
||||||
int spawnX;
|
int spawnX;
|
||||||
int spawnY;
|
int spawnY;
|
||||||
int spawnZ;
|
int spawnZ;
|
||||||
|
|
||||||
const int idleRange;
|
// dead
|
||||||
|
time_t killedTime = 0;
|
||||||
|
int regenTime;
|
||||||
|
bool despawned = false; // for the sake of death animations
|
||||||
|
|
||||||
|
// roaming
|
||||||
|
int idleRange;
|
||||||
time_t nextMovement = 0;
|
time_t nextMovement = 0;
|
||||||
|
|
||||||
|
// combat
|
||||||
|
CNSocket *target = nullptr;
|
||||||
|
time_t nextAttack = 0;
|
||||||
|
|
||||||
// temporary; until we're sure what's what
|
// temporary; until we're sure what's what
|
||||||
nlohmann::json data;
|
nlohmann::json data;
|
||||||
|
|
||||||
Mob(int x, int y, int z, int type, int hp, int angle, nlohmann::json d)
|
Mob(int x, int y, int z, int type, int hp, int angle, nlohmann::json d)
|
||||||
: BaseNPC(x, y, z, type), maxHealth(hp), regenTime(d["m_iRegenTime"]), idleRange(d["m_iIdleRange"]), data(d) {
|
: BaseNPC(x, y, z, type), maxHealth(hp) {
|
||||||
state = MobState::ROAMING;
|
state = MobState::ROAMING;
|
||||||
|
|
||||||
|
data = d;
|
||||||
|
|
||||||
|
regenTime = data["m_iRegenTime"];
|
||||||
|
idleRange = data["m_iIdleRange"];
|
||||||
|
|
||||||
spawnX = appearanceData.iX;
|
spawnX = appearanceData.iX;
|
||||||
spawnY = appearanceData.iY;
|
spawnY = appearanceData.iY;
|
||||||
spawnZ = appearanceData.iZ;
|
spawnZ = appearanceData.iZ;
|
||||||
@ -62,6 +73,8 @@ namespace MobManager {
|
|||||||
void step(CNServer*, time_t);
|
void step(CNServer*, time_t);
|
||||||
|
|
||||||
void deadStep(Mob*, time_t);
|
void deadStep(Mob*, time_t);
|
||||||
|
void combatStep(Mob*, time_t);
|
||||||
|
void retreatStep(Mob*, time_t);
|
||||||
void roamingStep(Mob*, time_t);
|
void roamingStep(Mob*, time_t);
|
||||||
|
|
||||||
void pcAttackNpcs(CNSocket *sock, CNPacketData *data);
|
void pcAttackNpcs(CNSocket *sock, CNPacketData *data);
|
||||||
@ -69,6 +82,7 @@ namespace MobManager {
|
|||||||
void combatEnd(CNSocket *sock, CNPacketData *data);
|
void combatEnd(CNSocket *sock, CNPacketData *data);
|
||||||
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
|
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
|
||||||
|
|
||||||
|
void npcAttackPc(Mob *mob);
|
||||||
void killMob(CNSocket *sock, Mob *mob);
|
void killMob(CNSocket *sock, Mob *mob);
|
||||||
void giveReward(CNSocket *sock);
|
void giveReward(CNSocket *sock);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user