/** * @file misdat.h * * Interface of data related to missiles. */ #pragma once #include #include #include #include #include #include "effects.h" #include "engine.h" #include "engine/clx_sprite.hpp" #include "spelldat.h" #include "utils/enum_traits.h" namespace devilution { enum mienemy_type : uint8_t { TARGET_MONSTERS, TARGET_PLAYERS, TARGET_BOTH, }; enum class DamageType : uint8_t { Physical, Fire, Lightning, Magic, Acid, }; enum class MissileGraphicID : uint8_t { Arrow, Fireball, Guardian, Lightning, FireWall, MagmaBallExplosion, TownPortal, FlashBottom, FlashTop, ManaShield, BloodHit, BoneHit, MetalHit, FireArrow, DoomSerpents, Golem, Spurt, ApocalypseBoom, StoneCurseShatter, BigExplosion, Inferno, ThinLightning, BloodStar, BloodStarExplosion, MagmaBall, Krull, ChargedBolt, HolyBolt, HolyBoltExplosion, LightningArrow, FireArrowExplosion, Acid, AcidSplat, AcidPuddle, Etherealize, Elemental, Resurrect, BoneSpirit, RedPortal, DiabloApocalypseBoom, BloodStarBlue, BloodStarBlueExplosion, BloodStarYellow, BloodStarYellowExplosion, BloodStarRed, BloodStarRedExplosion, HorkSpawn, Reflect, OrangeFlare, BlueFlare, RedFlare, YellowFlare, Rune, YellowFlareExplosion, BlueFlareExplosion, RedFlareExplosion, BlueFlare2, OrangeFlareExplosion, BlueFlareExplosion2, None, }; /** * @brief Specifies what if and how movement distribution is applied */ enum class MissileMovementDistribution : uint8_t { /** * @brief No movement distribution is calculated. Normally this means the missile doesn't move at all. */ Disabled, /** * @brief The missile moves and if it hits a enemey it stops (for example firebolt) */ Blockable, /** * @brief The missile moves and even it hits a enemy it keeps moving (for example flame wave) */ Unblockable, }; struct Missile; struct AddMissileParameter; enum class MissileDataFlags : uint8_t { // The lower 3 bytes are used to store DamageType. Physical = static_cast(DamageType::Physical), Fire = static_cast(DamageType::Fire), Lightning = static_cast(DamageType::Lightning), Magic = static_cast(DamageType::Magic), Acid = static_cast(DamageType::Acid), Arrow = 1 << 4, Invisible = 1 << 5, }; use_enum_as_flags(MissileDataFlags); struct MissileData { void (*mAddProc)(Missile &, AddMissileParameter &); void (*mProc)(Missile &); _sfx_id mlSFX; _sfx_id miSFX; MissileGraphicID mFileNum; MissileDataFlags flags; MissileMovementDistribution movementDistribution; [[nodiscard]] bool isDrawn() const { return !HasAnyOf(flags, MissileDataFlags::Invisible); } [[nodiscard]] bool isArrow() const { return HasAnyOf(flags, MissileDataFlags::Arrow); } [[nodiscard]] DamageType damageType() const { return static_cast(static_cast::type>(flags) & 0b111U); } }; enum class MissileGraphicsFlags : uint8_t { // clang-format off None = 0, MonsterOwned = 1 << 0, NotAnimated = 1 << 1, // clang-format on }; struct MissileFileData { OptionalOwnedClxSpriteListOrSheet sprites; uint16_t animWidth; int8_t animWidth2; char name[9]; uint8_t animFAmt; MissileGraphicsFlags flags; uint8_t animDelayIdx; uint8_t animLenIdx; [[nodiscard]] uint8_t animDelay(uint8_t dir) const; [[nodiscard]] uint8_t animLen(uint8_t dir) const; void LoadGFX(); void FreeGFX() { sprites = std::nullopt; } /** * @brief Returns the sprite list for a given direction. * * @param direction One of the 16 directions. Valid range: [0, 15]. * @return OptionalClxSpriteList */ [[nodiscard]] OptionalClxSpriteList spritesForDirection(size_t direction) const { if (!sprites) return std::nullopt; return sprites->isSheet() ? sprites->sheet()[direction] : sprites->list(); } }; extern const MissileData MissilesData[]; inline const MissileData &GetMissileData(MissileID missileId) { return MissilesData[static_cast::type>(missileId)]; } extern MissileFileData MissileSpriteData[]; inline MissileFileData &GetMissileSpriteData(MissileGraphicID graphicId) { return MissileSpriteData[static_cast::type>(graphicId)]; } void InitMissileGFX(bool loadHellfireGraphics = false); void FreeMissileGFX(); } // namespace devilution