mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 13:30:06 +00:00
(WIP) Point 2: Generalization
This commit is contained in:
parent
82bc94c01c
commit
f0cf6326e5
@ -153,7 +153,7 @@ bool doDebuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t target
|
||||
respdata[i].eCT = 4;
|
||||
respdata[i].iID = mob->id;
|
||||
respdata[i].bProtected = 1;
|
||||
if (mob->skillStyle < 0 && mob->state != MobState::RETREAT
|
||||
if (mob->skillStyle < 0 && mob->state != AIState::RETREAT
|
||||
&& !(mob->cbf & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom
|
||||
mob->cbf |= bitFlag;
|
||||
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
|
||||
@ -227,7 +227,7 @@ bool doDamageNDebuff(CNSocket *sock, sSkillResult_Damage_N_Debuff *respdata, int
|
||||
respdata[i].iID = mob->id;
|
||||
respdata[i].iHP = mob->hp;
|
||||
respdata[i].bProtected = 1;
|
||||
if (mob->skillStyle < 0 && mob->state != MobState::RETREAT
|
||||
if (mob->skillStyle < 0 && mob->state != AIState::RETREAT
|
||||
&& !(mob->cbf & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom
|
||||
mob->cbf |= bitFlag;
|
||||
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
|
||||
@ -450,7 +450,7 @@ bool doDamageNDebuff(Mob* mob, sSkillResult_Damage_N_Debuff* respdata, int i, in
|
||||
|
||||
if (plr->HP <= 0) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
mob->state = AIState::RETREAT;
|
||||
if (!MobAI::aggroCheck(mob, getTime())) {
|
||||
MobAI::clearDebuff(mob);
|
||||
if (mob->groupLeader != 0)
|
||||
@ -530,7 +530,7 @@ bool doDamage(Mob* mob, sSkillResult_Damage* respdata, int i, int32_t targetID,
|
||||
|
||||
if (plr->HP <= 0) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
mob->state = AIState::RETREAT;
|
||||
if (!MobAI::aggroCheck(mob, getTime())) {
|
||||
MobAI::clearDebuff(mob);
|
||||
if (mob->groupLeader != 0)
|
||||
@ -588,7 +588,7 @@ bool doLeech(Mob* mob, sSkillResult_Heal_HP* healdata, int i, int32_t targetID,
|
||||
|
||||
if (plr->HP <= 0) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
mob->state = AIState::RETREAT;
|
||||
if (!MobAI::aggroCheck(mob, getTime())) {
|
||||
MobAI::clearDebuff(mob);
|
||||
if (mob->groupLeader != 0)
|
||||
|
@ -50,14 +50,14 @@ int CombatNPC::takeDamage(EntityRef src, int amt) {
|
||||
Mob* mob = (Mob*)this;
|
||||
|
||||
// cannot kill mobs multiple times; cannot harm retreating mobs
|
||||
if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) {
|
||||
if (mob->state != AIState::ROAMING && mob->state != AIState::COMBAT) {
|
||||
return 0; // no damage
|
||||
}
|
||||
|
||||
if (mob->skillStyle >= 0)
|
||||
return 0; // don't hurt a mob casting corruption
|
||||
|
||||
if (mob->state == MobState::ROAMING) {
|
||||
if (mob->state == AIState::ROAMING) {
|
||||
assert(mob->target == nullptr && src.type == EntityType::PLAYER); // players only for now
|
||||
MobAI::enterCombat(src.sock, mob);
|
||||
|
||||
@ -101,7 +101,31 @@ int32_t CombatNPC::getID() {
|
||||
}
|
||||
|
||||
void CombatNPC::step(time_t currTime) {
|
||||
// stubbed
|
||||
if (playersInView < 0)
|
||||
std::cout << "[WARN] Weird playerview value " << playersInView << std::endl;
|
||||
|
||||
// skip movement and combat if disabled or not in view
|
||||
if ((!MobAI::simulateMobs || playersInView == 0) && state != AIState::DEAD
|
||||
&& state != AIState::RETREAT)
|
||||
return;
|
||||
|
||||
switch (state) {
|
||||
case AIState::INACTIVE:
|
||||
// no-op
|
||||
break;
|
||||
case AIState::ROAMING:
|
||||
roamingStep(currTime);
|
||||
break;
|
||||
case AIState::COMBAT:
|
||||
combatStep(currTime);
|
||||
break;
|
||||
case AIState::RETREAT:
|
||||
retreatStep(currTime);
|
||||
break;
|
||||
case AIState::DEAD:
|
||||
deadStep(currTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shouldCrit,
|
||||
@ -259,7 +283,7 @@ void Combat::npcAttackPc(Mob *mob, time_t currTime) {
|
||||
|
||||
if (plr->HP <= 0) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
mob->state = AIState::RETREAT;
|
||||
if (!MobAI::aggroCheck(mob, currTime)) {
|
||||
MobAI::clearDebuff(mob);
|
||||
if (mob->groupLeader != 0)
|
||||
@ -293,7 +317,7 @@ static void genQItemRolls(Player *leader, std::map<int, int>& rolls) {
|
||||
}
|
||||
|
||||
void Combat::killMob(CNSocket *sock, Mob *mob) {
|
||||
mob->state = MobState::DEAD;
|
||||
mob->state = AIState::DEAD;
|
||||
mob->target = nullptr;
|
||||
mob->cbf = 0;
|
||||
mob->skillStyle = -1;
|
||||
|
@ -328,7 +328,7 @@ static void toggleAiCommand(std::string full, std::vector<std::string>& args, CN
|
||||
continue;
|
||||
|
||||
Mob* mob = (Mob*)pair.second;
|
||||
mob->state = MobState::RETREAT;
|
||||
mob->state = AIState::RETREAT;
|
||||
mob->target = nullptr;
|
||||
mob->nextMovement = getTime();
|
||||
|
||||
|
@ -16,6 +16,16 @@ enum class EntityType : uint8_t {
|
||||
BUS
|
||||
};
|
||||
|
||||
enum class AIState {
|
||||
INACTIVE,
|
||||
ROAMING,
|
||||
COMBAT,
|
||||
RETREAT,
|
||||
DEAD
|
||||
};
|
||||
|
||||
class Chunk;
|
||||
|
||||
struct Entity {
|
||||
EntityType kind = EntityType::INVALID;
|
||||
int x = 0, y = 0, z = 0;
|
||||
@ -123,6 +133,8 @@ struct CombatNPC : public BaseNPC, public ICombatant {
|
||||
int spawnZ = 0;
|
||||
int level = 0;
|
||||
int speed = 300;
|
||||
AIState state = AIState::INACTIVE;
|
||||
int playersInView = 0; // for optimizing away AI in empty chunks
|
||||
|
||||
CombatNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, int maxHP)
|
||||
: BaseNPC(angle, iID, t, id), maxHealth(maxHP) {
|
||||
@ -138,12 +150,16 @@ struct CombatNPC : public BaseNPC, public ICombatant {
|
||||
virtual bool isAlive() override;
|
||||
virtual int getCurrentHP() override;
|
||||
virtual int32_t getID() override;
|
||||
|
||||
virtual void step(time_t currTime) override;
|
||||
virtual void roamingStep(time_t currTime) {} // no-op by default
|
||||
virtual void combatStep(time_t currTime) {}
|
||||
virtual void retreatStep(time_t currTime) {}
|
||||
virtual void deadStep(time_t currTime) {}
|
||||
};
|
||||
|
||||
// Mob is in MobAI.hpp, Player is in Player.hpp
|
||||
|
||||
// TODO: decouple from BaseNPC
|
||||
struct Egg : public BaseNPC {
|
||||
bool summoned = false;
|
||||
bool dead = false;
|
||||
@ -161,7 +177,6 @@ struct Egg : public BaseNPC {
|
||||
virtual void disappearFromViewOf(CNSocket *sock) override;
|
||||
};
|
||||
|
||||
// TODO: decouple from BaseNPC
|
||||
struct Bus : public BaseNPC {
|
||||
Bus(int angle, uint64_t iID, int t, int id) :
|
||||
BaseNPC(angle, iID, t, id) {
|
||||
|
@ -68,13 +68,13 @@ void MobAI::followToCombat(Mob *mob) {
|
||||
}
|
||||
Mob* followerMob = (Mob*)NPCManager::NPCs[leadMob->groupMember[i]];
|
||||
|
||||
if (followerMob->state != MobState::ROAMING) // only roaming mobs should transition to combat
|
||||
if (followerMob->state != AIState::ROAMING) // only roaming mobs should transition to combat
|
||||
continue;
|
||||
|
||||
enterCombat(mob->target, followerMob);
|
||||
}
|
||||
|
||||
if (leadMob->state != MobState::ROAMING)
|
||||
if (leadMob->state != AIState::ROAMING)
|
||||
return;
|
||||
|
||||
enterCombat(mob->target, leadMob);
|
||||
@ -96,19 +96,19 @@ void MobAI::groupRetreat(Mob *mob) {
|
||||
}
|
||||
Mob* followerMob = (Mob*)NPCManager::NPCs[leadMob->groupMember[i]];
|
||||
|
||||
if (followerMob->state != MobState::COMBAT)
|
||||
if (followerMob->state != AIState::COMBAT)
|
||||
continue;
|
||||
|
||||
followerMob->target = nullptr;
|
||||
followerMob->state = MobState::RETREAT;
|
||||
followerMob->state = AIState::RETREAT;
|
||||
clearDebuff(followerMob);
|
||||
}
|
||||
|
||||
if (leadMob->state != MobState::COMBAT)
|
||||
if (leadMob->state != AIState::COMBAT)
|
||||
return;
|
||||
|
||||
leadMob->target = nullptr;
|
||||
leadMob->state = MobState::RETREAT;
|
||||
leadMob->state = AIState::RETREAT;
|
||||
clearDebuff(leadMob);
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ bool MobAI::aggroCheck(Mob *mob, time_t currTime) {
|
||||
if (levelDifference > -10)
|
||||
mobRange = levelDifference < 10 ? mobRange - (levelDifference * mobRange / 15) : mobRange / 3;
|
||||
|
||||
if (mob->state != MobState::ROAMING && plr->inCombat) // freshly out of aggro mobs
|
||||
if (mob->state != AIState::ROAMING && plr->inCombat) // freshly out of aggro mobs
|
||||
mobRange = mob->sightRange * 2; // should not be impacted by the above
|
||||
|
||||
if (plr->iSpecialState & (CN_SPECIAL_STATE_FLAG__INVISIBLE|CN_SPECIAL_STATE_FLAG__INVULNERABLE))
|
||||
@ -267,7 +267,7 @@ static void dealCorruption(Mob *mob, std::vector<int> targetData, int skillID, i
|
||||
|
||||
if (plr->HP <= 0) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
mob->state = AIState::RETREAT;
|
||||
if (!aggroCheck(mob, getTime())) {
|
||||
clearDebuff(mob);
|
||||
if (mob->groupLeader != 0)
|
||||
@ -395,7 +395,7 @@ static void useAbilities(Mob *mob, time_t currTime) {
|
||||
|
||||
void MobAI::enterCombat(CNSocket *sock, Mob *mob) {
|
||||
mob->target = sock;
|
||||
mob->state = MobState::COMBAT;
|
||||
mob->state = AIState::COMBAT;
|
||||
mob->nextMovement = getTime();
|
||||
mob->nextAttack = 0;
|
||||
|
||||
@ -473,7 +473,7 @@ void Mob::deadStep(time_t currTime) {
|
||||
std::cout << "respawning mob " << id << " with HP = " << maxHealth << std::endl;
|
||||
|
||||
hp = maxHealth;
|
||||
state = MobState::ROAMING;
|
||||
state = AIState::ROAMING;
|
||||
|
||||
// if mob is a group leader/follower, spawn where the group is.
|
||||
if (groupLeader != 0) {
|
||||
@ -501,7 +501,7 @@ void Mob::combatStep(time_t currTime) {
|
||||
// lose aggro if the player lost connection
|
||||
if (PlayerManager::players.find(target) == PlayerManager::players.end()) {
|
||||
target = nullptr;
|
||||
state = MobState::RETREAT;
|
||||
state = AIState::RETREAT;
|
||||
if (!aggroCheck(this, currTime)) {
|
||||
clearDebuff(this);
|
||||
if (groupLeader != 0)
|
||||
@ -516,7 +516,7 @@ void Mob::combatStep(time_t currTime) {
|
||||
if (plr->HP <= 0
|
||||
|| (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE)) {
|
||||
target = nullptr;
|
||||
state = MobState::RETREAT;
|
||||
state = AIState::RETREAT;
|
||||
if (!aggroCheck(this, currTime)) {
|
||||
clearDebuff(this);
|
||||
if (groupLeader != 0)
|
||||
@ -572,7 +572,6 @@ void Mob::combatStep(time_t currTime) {
|
||||
}
|
||||
|
||||
int distanceToTravel = INT_MAX;
|
||||
int speed = speed;
|
||||
// movement logic: move when out of range but don't move while casting a skill
|
||||
if (distance > mobRange && skillStyle == -1) {
|
||||
if (nextMovement != 0 && currTime < nextMovement)
|
||||
@ -628,7 +627,7 @@ void Mob::combatStep(time_t currTime) {
|
||||
distance = hypot(xyDistance, plr->z - roamZ);
|
||||
if (distance >= data["m_iCombatRange"]) {
|
||||
target = nullptr;
|
||||
state = MobState::RETREAT;
|
||||
state = AIState::RETREAT;
|
||||
clearDebuff(this);
|
||||
if (groupLeader != 0)
|
||||
groupRetreat(this);
|
||||
@ -649,7 +648,7 @@ void Mob::roamingStep(time_t currTime) {
|
||||
* do so more often than if we waited for nextMovement (which is way too slow).
|
||||
* In the case of group leaders, this step will be called by dead mobs, so disable attack.
|
||||
*/
|
||||
if (state != MobState::DEAD && (nextAttack == 0 || currTime >= nextAttack)) {
|
||||
if (state != AIState::DEAD && (nextAttack == 0 || currTime >= nextAttack)) {
|
||||
nextAttack = currTime + 500;
|
||||
if (aggroCheck(this, currTime))
|
||||
return;
|
||||
@ -673,7 +672,6 @@ void Mob::roamingStep(time_t currTime) {
|
||||
|
||||
int xStart = spawnX - idleRange/2;
|
||||
int yStart = spawnY - idleRange/2;
|
||||
int speed = speed;
|
||||
|
||||
// some mobs don't move (and we mustn't divide/modulus by zero)
|
||||
if (idleRange == 0 || speed == 0)
|
||||
@ -760,7 +758,7 @@ void Mob::retreatStep(time_t currTime) {
|
||||
// if we got there
|
||||
//if (distance <= mob->data["m_iIdleRange"]) {
|
||||
if (distance <= 10) { // retreat back to the spawn point
|
||||
state = MobState::ROAMING;
|
||||
state = AIState::ROAMING;
|
||||
hp = maxHealth;
|
||||
killedTime = 0;
|
||||
nextAttack = 0;
|
||||
@ -775,33 +773,3 @@ void Mob::retreatStep(time_t currTime) {
|
||||
clearDebuff(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::step(time_t currTime) {
|
||||
assert(kind == EntityType::MOB);
|
||||
|
||||
if (playersInView < 0)
|
||||
std::cout << "[WARN] Weird playerview value " << playersInView << std::endl;
|
||||
|
||||
// skip mob movement and combat if disabled or not in view
|
||||
if ((!simulateMobs || playersInView == 0) && state != MobState::DEAD
|
||||
&& state != MobState::RETREAT)
|
||||
return;
|
||||
|
||||
switch (state) {
|
||||
case MobState::INACTIVE:
|
||||
// no-op
|
||||
break;
|
||||
case MobState::ROAMING:
|
||||
roamingStep(currTime);
|
||||
break;
|
||||
case MobState::COMBAT:
|
||||
combatStep(currTime);
|
||||
break;
|
||||
case MobState::RETREAT:
|
||||
retreatStep(currTime);
|
||||
break;
|
||||
case MobState::DEAD:
|
||||
deadStep(currTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2,19 +2,10 @@
|
||||
|
||||
#include "core/Core.hpp"
|
||||
#include "NPCManager.hpp"
|
||||
|
||||
enum class MobState {
|
||||
INACTIVE,
|
||||
ROAMING,
|
||||
COMBAT,
|
||||
RETREAT,
|
||||
DEAD
|
||||
};
|
||||
#include "Entities.hpp"
|
||||
|
||||
struct Mob : public CombatNPC {
|
||||
// general
|
||||
MobState state = MobState::INACTIVE;
|
||||
|
||||
std::unordered_map<int32_t,time_t> unbuffTimes = {};
|
||||
|
||||
// dead
|
||||
@ -42,16 +33,13 @@ struct Mob : public CombatNPC {
|
||||
int offsetX = 0, offsetY = 0;
|
||||
int groupMember[4] = {};
|
||||
|
||||
// for optimizing away AI in empty chunks
|
||||
int playersInView = 0;
|
||||
|
||||
// temporary; until we're sure what's what
|
||||
nlohmann::json data = {};
|
||||
|
||||
Mob(int x, int y, int z, int angle, uint64_t iID, int t, nlohmann::json d, int32_t id)
|
||||
: CombatNPC(x, y, z, angle, iID, t, id, d["m_iHP"]),
|
||||
sightRange(d["m_iSightRange"]) {
|
||||
state = MobState::ROAMING;
|
||||
state = AIState::ROAMING;
|
||||
|
||||
data = d;
|
||||
|
||||
@ -83,13 +71,10 @@ struct Mob : public CombatNPC {
|
||||
|
||||
~Mob() {}
|
||||
|
||||
virtual void step(time_t currTime) override;
|
||||
|
||||
// we may or may not want these to be generalized to all CombatNPCs later
|
||||
void roamingStep(time_t currTime);
|
||||
void combatStep(time_t currTime);
|
||||
void retreatStep(time_t currTime);
|
||||
void deadStep(time_t currTime);
|
||||
virtual void roamingStep(time_t currTime) override;
|
||||
virtual void combatStep(time_t currTime) override;
|
||||
virtual void retreatStep(time_t currTime) override;
|
||||
virtual void deadStep(time_t currTime) override;
|
||||
|
||||
auto operator[](std::string s) {
|
||||
return data[s];
|
||||
|
@ -263,7 +263,7 @@ static void stepNPCPathing() {
|
||||
}
|
||||
|
||||
// do not roam if not roaming
|
||||
if (npc->kind == EntityType::MOB && ((Mob*)npc)->state != MobState::ROAMING) {
|
||||
if (npc->kind == EntityType::MOB && ((Mob*)npc)->state != AIState::ROAMING) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user