#pragma once #include #include "render/canvas.hpp" #include "resource/character.hpp" #include "util/measurement.hpp" namespace game { enum EntityType { ACTOR, CHARACTER, CURSOR, AUTOCURSOR, ITEM, PARTICLE }; class Entity { public: using Anm2 = resource::xml::Anm2; using FrameOptional = Anm2::FrameOptional; static constexpr auto UPDATE_RATE = 60.0f; static constexpr auto DIGESTION_MAX = 100.0f; enum Mode { PLAY, PLAY_FORCE, SET, }; enum State { STOPPED, PLAYING }; enum class Transition { NOW, AFTER_CURRENT, IF_IDLE }; enum class Interrupt { IF_ALLOWED, ALWAYS, NEVER }; enum CursorState { DEFAULT, HOVER, ACTION }; enum SpritesheetType { NORMAL, ALTERNATE }; class Override { public: enum Mode { SET, ADD }; using Function = void (*)(Override&); int id{-1}; Anm2::Type type{Anm2::NONE}; Mode mode{SET}; FrameOptional frame{}; std::optional time{}; Function function{nullptr}; FrameOptional frameBase{}; std::optional timeStart{}; float cycles{}; Override() = default; Override(int, Anm2::Type, Mode, FrameOptional = {}, std::optional = std::nullopt, Function = nullptr, float = 0); }; struct AnimationRequest { std::string animation{}; std::string appendID{}; int index{-1}; float time{}; float speedMultiplier{1.0f}; Mode mode{PLAY}; Transition transition{Transition::NOW}; Interrupt interrupt{Interrupt::IF_ALLOWED}; inline bool empty() const { return animation.empty() && index == -1; }; }; EntityType entityType{ACTOR}; int fps{30}; std::map spritesheets{}; std::map layers{}; std::map nulls{}; std::map events{}; std::map sounds{}; std::unordered_map layerMap{}; std::unordered_map nullMap{}; std::unordered_map eventMap{}; std::string defaultAnimation{}; int defaultAnimationID{-1}; std::vector animations{}; std::unordered_map animationMap{}; std::unordered_map animationMapReverse{}; std::unordered_set invalidAnimations{}; std::string path{}; bool isValid{}; Anm2::Flags flags{}; State state{STOPPED}; Mode mode{PLAY}; glm::vec2 position{}; float time{}; bool isShowLayers{}; bool isShowNulls{}; bool isVisible{true}; int animationIndex{-1}; int playedEventID{-1}; float startTime{}; float speedMultiplier{}; AnimationRequest pendingAnimation{}; AnimationRequest currentAnimation{}; AnimationRequest nextAnimation{}; std::unordered_set playedTriggers{}; std::vector overrides{}; resource::xml::Character data; float weight{}; int stage{0}; float calories{}; float capacity{}; float digestionProgress{}; float digestionRate{0.05f}; int digestionTimer{}; bool isDigesting{}; bool isJustDigested{}; float totalCaloriesConsumed{}; int totalItemsSpawned{}; int totalItemsConsumed{}; float totalPlaytimeSeconds{}; int talkOverrideID{}; float talkTimer{}; bool isTalking{}; std::vector animationTalkDurations{}; int blinkOverrideID{}; float blinkTimer{}; bool isBlinking{}; std::vector animationBlinkDurations{}; bool isStageUp{}; bool isStageUpDuring{}; bool isJustStageUp{}; bool isJustStageFinal{}; SpritesheetType spritesheetType{}; std::map interactAreaOverrides{}; std::unordered_map expandAreaOverrideLayerIDs{}; std::unordered_map expandAreaOverrideNullIDs{}; CursorState cursorState{DEFAULT}; int interactTypeID{-1}; std::optional positionOverride{}; bool isToBeDeleted{}; bool isRemoveOnAnimationEnd{}; bool isHeld{}; int schemaID{}; int rotationOverrideID{}; glm::vec2 velocity{}; float angularVelocity{}; float phase{}; float magnetTime{}; bool isCollected{}; bool isMagnetized{}; Entity() = default; Entity(const Entity&); Entity(Entity&&) noexcept; Entity& operator=(const Entity&); Entity& operator=(Entity&&) noexcept; Entity(resource::xml::Anm2, glm::vec2 position = {}, Mode = PLAY, float time = 0.0f, int animationIndex = -1); Entity(resource::xml::Character&, glm::ivec2); Entity(resource::xml::Anm2, glm::ivec2 position, int id, glm::vec2 velocity = {}, float rotation = 0.0f); bool is_playing(const std::string& name = {}); glm::vec4 null_frame_rect(int = -1, int animationIndex = -1, float time = -1.0f); glm::vec4 rect(); int animation_index_get(const std::string&); resource::xml::Anm2::Animation* animation_get(const std::string&); resource::xml::Anm2::Animation* animation_get(int = -1); resource::xml::Anm2::Frame frame_generate(resource::xml::Anm2::Item&, float, resource::xml::Anm2::Type, int id = -1); resource::xml::Anm2::Item* item_get(resource::xml::Anm2::Type, int = -1, int = -1); void consume_played_event(); void play(AnimationRequest); bool is_animation_request() const; bool is_animation_finished(); void render(resource::Shader& textureShader, resource::Shader& rectShader, Canvas&); float weight_get(util::measurement::System = util::measurement::METRIC); float digestion_rate_get(); float capacity_percent_get() const; float max_capacity() const; bool is_over_capacity() const; bool is_max_capacity() const; int stage_get() const; int stage_from_weight_get(float weight) const; int stage_max_get() const; int stage_count_get() const; float stage_progress_get(); float stage_threshold_get(int stage = -1, util::measurement::System = util::measurement::METRIC) const; float stage_threshold_next_get(util::measurement::System = util::measurement::METRIC) const; void expand_areas_apply(); void spritesheet_set(SpritesheetType); std::string idle_animation_get() const; std::string animation_append_id_get() const; void update(); private: void anm2_set(const resource::xml::Anm2&); void animation_start(int, const AnimationRequest&); bool animation_request_apply(AnimationRequest&); void pending_animation_apply(); void animation_update(); void character_values_clamp(); void character_update(); }; }