#ifndef STAR_ENTITY_HPP #define STAR_ENTITY_HPP #include "StarCasting.hpp" #include "StarDamage.hpp" #include "StarLightSource.hpp" namespace Star { STAR_CLASS(RenderCallback); STAR_CLASS(World); STAR_STRUCT(DamageNotification); STAR_CLASS(Entity); STAR_EXCEPTION(EntityException, StarException); // Specifies how the client should treat an entity created on the client, // whether it should always be sent to the server and be a slave on the client, // whether it is allowed to be master on the client, and whether client master // entities should contribute to client presence. enum class ClientEntityMode { // Always a slave on the client ClientSlaveOnly, // Can be a master on the client ClientMasterAllowed, // Can be a master on the client, and when it is contributes to client // presence. ClientPresenceMaster }; extern EnumMap const ClientEntityModeNames; // The top-level entity type. The enum order is intended to be in the order in // which entities should be updated every tick enum class EntityType : uint8_t { Plant, Object, Vehicle, ItemDrop, PlantDrop, Projectile, Stagehand, Monster, Npc, Player }; extern EnumMap const EntityTypeNames; class Entity { public: virtual ~Entity(); virtual EntityType entityType() const = 0; // Called when an entity is first inserted into a World. Calling base class // init sets the world pointer, entityId, and entityMode. virtual void init(World* world, EntityId entityId, EntityMode mode); // Should do whatever steps necessary to take an entity out of a world, // default implementation clears the world pointer, entityMode, and entityId. virtual void uninit(); // Write state data that changes over time, and is used to keep slaves in // sync. Can return empty and this is the default. May be called // uninitalized. Should return the delta to be written to the slave, along // with the version to pass into writeDeltaState on the next call. The first // delta written to a slave entity will always be the delta starting with 0. virtual pair writeNetState(uint64_t fromVersion = 0); // Will be called with deltas written by writeDeltaState, including if the // delta is empty. interpolationTime will be provided if interpolation is // enabled. virtual void readNetState(ByteArray data, float interpolationTime = 0.0); virtual void enableInterpolation(float extrapolationHint); virtual void disableInterpolation(); // Base position of this entity, bound boxes, drawables, and other entity // positions are relative to this. virtual Vec2F position() const = 0; // Largest bounding-box of this entity. Any damage boxes / drawables / light // or sound *sources* must be contained within this bounding box. Used for // all top-level spatial queries. virtual RectF metaBoundBox() const = 0; // By default returns a null rect, if non-null, it defines the area around // this entity where it is likely for the entity to physically collide with // collision geometry. virtual RectF collisionArea() const; // Should this entity allow object / block placement over it, and can the // entity immediately be despawned without terribly bad effects? virtual bool ephemeral() const; // How should this entity be treated if created on the client? Defaults to // ClientSlave. virtual ClientEntityMode clientEntityMode() const; // Should this entity only exist on the master side? virtual bool masterOnly() const; virtual String description() const; // Gameplay affecting light sources (separate from light sources added during // rendering) virtual List lightSources() const; // All damage sources for this frame. virtual List damageSources() const; // Return the damage that would result from being hit by the given damage // source. Will be called on master and slave entities. Culling based on // team damage and self damage will be done outside of this query. virtual Maybe queryHit(DamageSource const& source) const; // Return the polygonal area in which the entity can be hit. Not used for // actual hit computation, only for determining more precisely where a // hit intersection occurred (e.g. by projectiles) virtual Maybe hitPoly() const; // Apply a request to damage this entity. Will only be called on Master // entities. DamageRequest might be adjusted based on protection and other // effects virtual List applyDamage(DamageRequest const& damage); // Pull any pending damage notifications applied internally, only called on // Master entities. virtual List selfDamageNotifications(); // Called on master entities when a DamageRequest has been generated due to a // DamageSource from this entity being applied to another entity. Will be // called on the *causing* entity of the damage. virtual void hitOther(EntityId targetEntityId, DamageRequest const& damageRequest); // Called on master entities when this entity has damaged another entity. // Only called on the *source entity* of the damage, which may be different // than the causing entity. virtual void damagedOther(DamageNotification const& damage); // Returning true here indicates that this entity should be removed from the // world, default returns false. virtual bool shouldDestroy() const; // Will be called once before removing the entity from the World on both // master and slave entities. virtual void destroy(RenderCallback* renderCallback); // Entities can send other entities potentially remote messages and get // responses back from them, and should implement this to receive and respond // to messages. If the message is NOT handled, should return Nothing, // otherwise should return some Json value. // This will only ever be called on master entities. virtual Maybe receiveMessage(ConnectionId sendingConnection, String const& message, JsonArray const& args); virtual void update(float dt, uint64_t currentStep); virtual void render(RenderCallback* renderer); virtual void renderLightSources(RenderCallback* renderer); EntityId entityId() const; EntityDamageTeam getTeam() const; // Returns true if an entity is initialized in a world, and thus has a valid // world pointer, entity id, and entity mode. bool inWorld() const; // Throws an exception if not currently in a world. World* world() const; // Returns nullptr if not currently in a world. World* worldPtr() const; // Specifies if the entity is to be saved to disk alongside the sector or // despawned. bool persistent() const; // Entity should keep any sector it is in alive. Default implementation // returns false. bool keepAlive() const; // If set, then the entity will be discoverable by its unique id and will be // indexed in the stored world. Unique ids must be different across all // entities in a single world. Maybe uniqueId() const; // EntityMode will only be set if the entity is initialized, if the entity is // uninitialized then isMaster and isSlave will both return false. Maybe entityMode() const; bool isMaster() const; bool isSlave() const; protected: Entity(); void setPersistent(bool persistent); void setKeepAlive(bool keepAlive); void setUniqueId(Maybe uniqueId); void setTeam(EntityDamageTeam newTeam); private: EntityId m_entityId; Maybe m_entityMode; bool m_persistent; bool m_keepAlive; Maybe m_uniqueId; World* m_world; EntityDamageTeam m_team; }; template using EntityCallbackOf = function const&)>; template using EntityFilterOf = function const&)>; typedef EntityCallbackOf EntityCallback; typedef EntityFilterOf EntityFilter; // Filters based first on dynamic casting to the given type, then optionally on // the given derived type filter. template EntityFilter entityTypeFilter(function const&)> filter = {}) { return [filter](EntityPtr const& e) -> bool { if (auto entity = as(e)) { return !filter || filter(entity); } else { return false; } }; } } #endif