2021-03-21 01:42:45 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "core/Core.hpp"
|
2022-07-22 00:40:33 +00:00
|
|
|
#include "Chunking.hpp"
|
2021-03-21 01:42:45 +00:00
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <set>
|
|
|
|
|
2022-04-13 22:01:52 +00:00
|
|
|
enum EntityKind {
|
2021-03-21 18:29:17 +00:00
|
|
|
INVALID,
|
2021-03-21 01:42:45 +00:00
|
|
|
PLAYER,
|
|
|
|
SIMPLE_NPC,
|
|
|
|
COMBAT_NPC,
|
2021-03-21 02:54:24 +00:00
|
|
|
MOB,
|
2021-03-21 01:42:45 +00:00
|
|
|
EGG,
|
|
|
|
BUS
|
|
|
|
};
|
|
|
|
|
2022-04-13 02:22:12 +00:00
|
|
|
enum class AIState {
|
|
|
|
INACTIVE,
|
|
|
|
ROAMING,
|
|
|
|
COMBAT,
|
|
|
|
RETREAT,
|
|
|
|
DEAD
|
|
|
|
};
|
|
|
|
|
|
|
|
class Chunk;
|
2022-04-23 01:13:00 +00:00
|
|
|
struct Group;
|
2022-04-13 02:22:12 +00:00
|
|
|
|
2021-03-21 01:42:45 +00:00
|
|
|
struct Entity {
|
2022-04-13 19:56:12 +00:00
|
|
|
EntityKind kind = EntityKind::INVALID;
|
2021-03-21 18:29:17 +00:00
|
|
|
int x = 0, y = 0, z = 0;
|
|
|
|
uint64_t instanceID = 0;
|
|
|
|
ChunkPos chunkPos = {};
|
|
|
|
std::set<Chunk*> viewableChunks = {};
|
2021-03-21 01:42:45 +00:00
|
|
|
|
|
|
|
// destructor must be virtual, apparently
|
|
|
|
virtual ~Entity() {}
|
|
|
|
|
2022-04-11 14:26:57 +00:00
|
|
|
virtual bool isExtant() { return true; }
|
2021-03-21 01:42:45 +00:00
|
|
|
|
|
|
|
// stubs
|
2021-03-31 19:05:49 +00:00
|
|
|
virtual void enterIntoViewOf(CNSocket *sock) = 0;
|
|
|
|
virtual void disappearFromViewOf(CNSocket *sock) = 0;
|
2021-03-21 01:42:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct EntityRef {
|
2022-04-13 19:56:12 +00:00
|
|
|
EntityKind kind;
|
2021-03-21 01:42:45 +00:00
|
|
|
union {
|
|
|
|
CNSocket *sock;
|
|
|
|
int32_t id;
|
|
|
|
};
|
|
|
|
|
|
|
|
EntityRef(CNSocket *s);
|
|
|
|
EntityRef(int32_t i);
|
|
|
|
|
|
|
|
bool isValid() const;
|
|
|
|
Entity *getEntity() const;
|
|
|
|
|
|
|
|
bool operator==(const EntityRef& other) const {
|
2022-04-13 19:56:12 +00:00
|
|
|
if (kind != other.kind)
|
2021-03-21 01:42:45 +00:00
|
|
|
return false;
|
|
|
|
|
2022-04-13 19:56:12 +00:00
|
|
|
if (kind == EntityKind::PLAYER)
|
2021-03-21 01:42:45 +00:00
|
|
|
return sock == other.sock;
|
|
|
|
|
|
|
|
return id == other.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
// arbitrary ordering
|
|
|
|
bool operator<(const EntityRef& other) const {
|
2022-04-13 19:56:12 +00:00
|
|
|
if (kind == other.kind) {
|
|
|
|
if (kind == EntityKind::PLAYER)
|
2021-03-21 01:42:45 +00:00
|
|
|
return sock < other.sock;
|
|
|
|
else
|
|
|
|
return id < other.id;
|
|
|
|
}
|
|
|
|
|
2022-04-13 19:56:12 +00:00
|
|
|
return kind < other.kind;
|
2021-03-21 01:42:45 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-11 14:26:57 +00:00
|
|
|
/*
|
|
|
|
* Interfaces
|
|
|
|
*/
|
|
|
|
class ICombatant {
|
|
|
|
public:
|
|
|
|
ICombatant() {}
|
|
|
|
virtual ~ICombatant() {}
|
|
|
|
|
2022-04-12 03:14:03 +00:00
|
|
|
virtual int takeDamage(EntityRef, int) = 0;
|
2022-04-11 14:26:57 +00:00
|
|
|
virtual void heal(EntityRef, int) = 0;
|
|
|
|
virtual bool isAlive() = 0;
|
2022-04-12 03:14:03 +00:00
|
|
|
virtual int getCurrentHP() = 0;
|
|
|
|
virtual int32_t getID() = 0;
|
2022-04-12 21:12:08 +00:00
|
|
|
virtual void step(time_t currTime) = 0;
|
2022-04-11 14:26:57 +00:00
|
|
|
};
|
|
|
|
|
2021-03-21 02:54:24 +00:00
|
|
|
/*
|
|
|
|
* Subclasses
|
|
|
|
*/
|
2021-03-21 01:42:45 +00:00
|
|
|
class BaseNPC : public Entity {
|
|
|
|
public:
|
2021-06-20 18:37:37 +00:00
|
|
|
int id;
|
|
|
|
int type;
|
|
|
|
int hp;
|
|
|
|
int angle;
|
|
|
|
int cbf;
|
2021-05-09 12:37:36 +00:00
|
|
|
bool loopingPath = false;
|
2021-03-21 01:42:45 +00:00
|
|
|
|
2021-10-19 22:30:53 +00:00
|
|
|
BaseNPC(int _A, uint64_t iID, int t, int _id) {
|
2021-06-20 18:37:37 +00:00
|
|
|
type = t;
|
|
|
|
hp = 400;
|
|
|
|
angle = _A;
|
|
|
|
cbf = 0;
|
|
|
|
id = _id;
|
2021-03-21 01:42:45 +00:00
|
|
|
instanceID = iID;
|
|
|
|
};
|
|
|
|
|
|
|
|
virtual void enterIntoViewOf(CNSocket *sock) override;
|
|
|
|
virtual void disappearFromViewOf(CNSocket *sock) override;
|
2021-06-20 18:37:37 +00:00
|
|
|
|
|
|
|
sNPCAppearanceData getAppearanceData();
|
2021-03-21 01:42:45 +00:00
|
|
|
};
|
|
|
|
|
2022-04-11 14:26:57 +00:00
|
|
|
struct CombatNPC : public BaseNPC, public ICombatant {
|
2021-03-21 18:29:17 +00:00
|
|
|
int maxHealth = 0;
|
|
|
|
int spawnX = 0;
|
|
|
|
int spawnY = 0;
|
|
|
|
int spawnZ = 0;
|
|
|
|
int level = 0;
|
2021-04-30 00:20:53 +00:00
|
|
|
int speed = 300;
|
2022-04-13 02:22:12 +00:00
|
|
|
AIState state = AIState::INACTIVE;
|
2022-04-23 01:13:00 +00:00
|
|
|
Group* group = nullptr;
|
2022-04-13 02:22:12 +00:00
|
|
|
int playersInView = 0; // for optimizing away AI in empty chunks
|
2021-03-21 18:29:17 +00:00
|
|
|
|
2022-04-13 22:01:52 +00:00
|
|
|
std::map<AIState, void (*)(CombatNPC*, time_t)> stateHandlers;
|
|
|
|
std::map<AIState, void (*)(CombatNPC*, EntityRef)> transitionHandlers;
|
|
|
|
|
2021-10-19 22:30:53 +00:00
|
|
|
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) {
|
|
|
|
spawnX = x;
|
|
|
|
spawnY = y;
|
|
|
|
spawnZ = z;
|
2022-04-13 22:01:52 +00:00
|
|
|
|
|
|
|
stateHandlers[AIState::INACTIVE] = {};
|
|
|
|
transitionHandlers[AIState::INACTIVE] = {};
|
2021-10-19 22:30:53 +00:00
|
|
|
}
|
2021-03-21 18:29:17 +00:00
|
|
|
|
2022-04-11 20:52:35 +00:00
|
|
|
virtual bool isExtant() override { return hp > 0; }
|
2022-04-11 14:26:57 +00:00
|
|
|
|
2022-04-12 03:14:03 +00:00
|
|
|
virtual int takeDamage(EntityRef src, int amt) override;
|
2022-04-11 20:52:35 +00:00
|
|
|
virtual void heal(EntityRef src, int amt) override;
|
|
|
|
virtual bool isAlive() override;
|
2022-04-12 03:14:03 +00:00
|
|
|
virtual int getCurrentHP() override;
|
|
|
|
virtual int32_t getID() override;
|
2022-04-12 21:12:08 +00:00
|
|
|
virtual void step(time_t currTime) override;
|
2022-04-13 03:29:11 +00:00
|
|
|
|
2022-04-13 03:44:12 +00:00
|
|
|
virtual void transition(AIState newState, EntityRef src);
|
2021-03-21 18:29:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Mob is in MobAI.hpp, Player is in Player.hpp
|
|
|
|
|
2021-03-21 01:42:45 +00:00
|
|
|
struct Egg : public BaseNPC {
|
2021-03-21 18:29:17 +00:00
|
|
|
bool summoned = false;
|
2021-03-21 01:42:45 +00:00
|
|
|
bool dead = false;
|
|
|
|
time_t deadUntil;
|
|
|
|
|
2021-10-19 22:30:53 +00:00
|
|
|
Egg(uint64_t iID, int t, int32_t id, bool summon)
|
|
|
|
: BaseNPC(0, iID, t, id) {
|
2021-03-21 01:42:45 +00:00
|
|
|
summoned = summon;
|
2022-04-13 19:56:12 +00:00
|
|
|
kind = EntityKind::EGG;
|
2021-03-21 01:42:45 +00:00
|
|
|
}
|
|
|
|
|
2022-04-11 14:26:57 +00:00
|
|
|
virtual bool isExtant() override { return !dead; }
|
2021-03-21 01:42:45 +00:00
|
|
|
|
|
|
|
virtual void enterIntoViewOf(CNSocket *sock) override;
|
|
|
|
virtual void disappearFromViewOf(CNSocket *sock) override;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Bus : public BaseNPC {
|
2021-10-19 22:30:53 +00:00
|
|
|
Bus(int angle, uint64_t iID, int t, int id) :
|
|
|
|
BaseNPC(angle, iID, t, id) {
|
2022-04-13 19:56:12 +00:00
|
|
|
kind = EntityKind::BUS;
|
2021-05-16 19:39:45 +00:00
|
|
|
loopingPath = true;
|
2021-03-22 16:53:46 +00:00
|
|
|
}
|
|
|
|
|
2021-03-21 01:42:45 +00:00
|
|
|
virtual void enterIntoViewOf(CNSocket *sock) override;
|
|
|
|
virtual void disappearFromViewOf(CNSocket *sock) override;
|
|
|
|
};
|