/** * @file missiles.h * * Interface of missile functionality. */ #pragma once #include #include #include "engine.h" #include "engine/point.hpp" #include "miniwin/miniwin.h" #include "misdat.h" #include "monster.h" #include "spelldat.h" namespace devilution { constexpr Point GolemHoldingCell = Point { 1, 0 }; struct MissilePosition { Point tile; /** Sprite's pixel offset from tile. */ Displacement offset; /** Pixel velocity while moving */ Displacement velocity; /** Start position */ Point start; /** Start position */ Displacement traveled; /** * @brief Specifies the location (tile) while rendering */ Point tileForRendering; /** * @brief Specifies the location (offset) while rendering */ Displacement offsetForRendering; /** * @brief Stops the missile (set velocity to zero and set offset to last renderer location; shouldn't matter cause the missile don't move anymore) */ void StopMissile() { velocity = {}; if (tileForRendering == tile) offset = offsetForRendering; } }; /** * Represent a more fine-grained direction than the 8 value Direction enum. * * This is used when rendering projectiles like arrows which have additional sprites for "half-winds" on a 16-point compass. * The sprite sheets are typically 0-indexed and use the following layout (relative to the screen projection) * * W WSW SW SSW S * ^ * WNW | SSE * | * NW -------+------> SE * | * NNW | ESE * | * N NNE NE ENE E */ enum class Direction16 { South, South_SouthWest, SouthWest, West_SouthWest, West, West_NorthWest, NorthWest, North_NorthWest, North, North_NorthEast, NorthEast, East_NorthEast, East, East_SouthEast, SouthEast, South_SouthEast, }; struct Missile { /** Type of projectile */ missile_id _mitype; MissilePosition position; int _mimfnum; // The direction of the missile (direction enum) int _mispllvl; bool _miDelFlag; // Indicate whether the missile should be deleted uint8_t _miAnimType; MissileDataFlags _miAnimFlags; const byte *_miAnimData; int _miAnimDelay; // Tick length of each frame in the current animation int _miAnimLen; // Number of frames in current animation uint16_t _miAnimWidth; int16_t _miAnimWidth2; int _miAnimCnt; // Increases by one each game tick, counting how close we are to _pAnimDelay int _miAnimAdd; int _miAnimFrame; // Current frame of animation + 1. bool _miDrawFlag; bool _miLightFlag; bool _miPreFlag; uint32_t _miUniqTrans; int _mirange; // Time to live for the missile in game ticks, oncs 0 the missile will be marked for deletion via _miDelFlag int _misource; mienemy_type _micaster; int _midam; bool _miHitFlag; int _midist; // Used for arrows to measure distance travelled (increases by 1 each game tick). Higher value is a penalty for accuracy calculation when hitting enemy int _mlid; int _mirnd; int var1; int var2; int var3; int var4; int var5; int var6; int var7; bool limitReached; /** * @brief For moving missiles lastCollisionTargetHash contains the last entity (player or monster) that was checked in CheckMissileCol (needed to avoid multiple hits for a entity at the same tile). */ int16_t lastCollisionTargetHash; /** @brief Was the missile generated by a trap? */ [[nodiscard]] bool IsTrap() const { return _misource == -1; } }; extern std::list Missiles; extern bool MissilePreFlag; void GetDamageAmt(int i, int *mind, int *maxd); int GetSpellLevel(int playerId, spell_id sn); /** * @brief Returns the direction a vector from p1(x1, y1) to p2(x2, y2) is pointing to. * * W sW SW Sw S * ^ * nW | Se * | * NW ------+-----> SE * | * Nw | sE * | * N Ne NE nE E * * @param x1 the x coordinate of p1 * @param y1 the y coordinate of p1 * @param x2 the x coordinate of p2 * @param y2 the y coordinate of p2 * @return the direction of the p1->p2 vector */ Direction16 GetDirection16(Point p1, Point p2); bool MonsterTrapHit(int m, int mindam, int maxdam, int dist, missile_id t, bool shift); bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, missile_id mtype, bool shift, int earflag, bool *blocked); /** * @brief Could the missile collide with solid objects? (like walls or closed doors) */ bool IsMissileBlockedByTile(Point position); /** * @brief Sets the missile sprite to the given sheet frame * @param missile this object * @param dir Sprite frame, typically representing a direction but there are some exceptions (arrows being 1 indexed, directionless spells) */ void SetMissDir(Missile &missile, int dir); /** * @brief Sets the sprite for this missile so it matches the given Direction * @param missile this object * @param dir Desired facing */ inline void SetMissDir(Missile &missile, Direction dir) { SetMissDir(missile, static_cast(dir)); } /** * @brief Sets the sprite for this missile so it matches the given Direction16 * @param missile this object * @param dir Desired facing at a 22.8125 degree resolution */ inline void SetMissDir(Missile &missile, Direction16 dir) { SetMissDir(missile, static_cast(dir)); } void InitMissiles(); struct AddMissileParameter { Point dst; Direction midir; Missile *pParent; }; void AddHiveExplosion(Missile &missile, const AddMissileParameter ¶meter); void AddFireRune(Missile &missile, const AddMissileParameter ¶meter); void AddLightningRune(Missile &missile, const AddMissileParameter ¶meter); void AddGreatLightningRune(Missile &missile, const AddMissileParameter ¶meter); void AddImmolationRune(Missile &missile, const AddMissileParameter ¶meter); void AddStoneRune(Missile &missile, const AddMissileParameter ¶meter); void AddReflection(Missile &missile, const AddMissileParameter ¶meter); void AddBerserk(Missile &missile, const AddMissileParameter ¶meter); /** * var1: Direction to place the spawn */ void AddHorkSpawn(Missile &missile, const AddMissileParameter ¶meter); void AddJester(Missile &missile, const AddMissileParameter ¶meter); void AddStealPotions(Missile &missile, const AddMissileParameter ¶meter); void AddManaTrap(Missile &missile, const AddMissileParameter ¶meter); void AddSpecArrow(Missile &missile, const AddMissileParameter ¶meter); void AddWarp(Missile &missile, const AddMissileParameter ¶meter); void AddLightningWall(Missile &missile, const AddMissileParameter ¶meter); void AddRuneExplosion(Missile &missile, const AddMissileParameter ¶meter); void AddFireNova(Missile &missile, const AddMissileParameter ¶meter); void AddLightningArrow(Missile &missile, const AddMissileParameter ¶meter); void AddMana(Missile &missile, const AddMissileParameter ¶meter); void AddMagi(Missile &missile, const AddMissileParameter ¶meter); void AddRing(Missile &missile, const AddMissileParameter ¶meter); void AddSearch(Missile &missile, const AddMissileParameter ¶meter); void AddCboltArrow(Missile &missile, const AddMissileParameter ¶meter); void AddLArrow(Missile &missile, const AddMissileParameter ¶meter); void AddArrow(Missile &missile, const AddMissileParameter ¶meter); void AddRndTeleport(Missile &missile, const AddMissileParameter ¶meter); void AddFirebolt(Missile &missile, const AddMissileParameter ¶meter); void AddMagmaball(Missile &missile, const AddMissileParameter ¶meter); void AddTeleport(Missile &missile, const AddMissileParameter ¶meter); void AddLightball(Missile &missile, const AddMissileParameter ¶meter); void AddFirewall(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the missile-light * var2: Y coordinate of the missile-light * var4: X coordinate of the missile-light * var5: Y coordinate of the missile-light */ void AddFireball(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the missile * var2: Y coordinate of the missile */ void AddLightctrl(Missile &missile, const AddMissileParameter ¶meter); void AddLightning(Missile &missile, const AddMissileParameter ¶meter); void AddMisexp(Missile &missile, const AddMissileParameter ¶meter); void AddWeapexp(Missile &missile, const AddMissileParameter ¶meter); /** * var1: Animation */ void AddTown(Missile &missile, const AddMissileParameter ¶meter); void AddFlash(Missile &missile, const AddMissileParameter ¶meter); void AddFlash2(Missile &missile, const AddMissileParameter ¶meter); void AddManashield(Missile &missile, const AddMissileParameter ¶meter); void AddFiremove(Missile &missile, const AddMissileParameter ¶meter); /** * var1: Animation * var3: Light strength */ void AddGuardian(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the destination * var2: Y coordinate of the destination */ void AddChain(Missile &missile, const AddMissileParameter ¶meter); void AddRhino(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the missile-light * var2: Y coordinate of the missile-light */ void AddFlare(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the missile-light * var2: Y coordinate of the missile-light */ void AddAcid(Missile &missile, const AddMissileParameter ¶meter); void AddAcidpud(Missile &missile, const AddMissileParameter ¶meter); /** * var1: mmode of the monster * var2: mnum of the monster */ void AddStone(Missile &missile, const AddMissileParameter ¶meter); void AddGolem(Missile &missile, const AddMissileParameter ¶meter); void AddBoom(Missile &missile, const AddMissileParameter ¶meter); void AddHeal(Missile &missile, const AddMissileParameter ¶meter); void AddHealOther(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the missile-light * var2: Y coordinate of the missile-light * var4: X coordinate of the destination * var5: Y coordinate of the destination */ void AddElement(Missile &missile, const AddMissileParameter ¶meter); void AddIdentify(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the first wave * var2: Y coordinate of the first wave * var3: Direction of the first wave * var4: Direction of the second wave * var5: X coordinate of the second wave * var6: Y coordinate of the second wave */ void AddFirewallC(Missile &missile, const AddMissileParameter ¶meter); void AddInfra(Missile &missile, const AddMissileParameter ¶meter); /** * var1: X coordinate of the destination * var2: Y coordinate of the destination */ void AddWave(Missile &missile, const AddMissileParameter ¶meter); void AddNova(Missile &missile, const AddMissileParameter ¶meter); void AddBlodboil(Missile &missile, const AddMissileParameter ¶meter); void AddRepair(Missile &missile, const AddMissileParameter ¶meter); void AddRecharge(Missile &missile, const AddMissileParameter ¶meter); void AddDisarm(Missile &missile, const AddMissileParameter ¶meter); void AddApoca(Missile &missile, const AddMissileParameter ¶meter); void AddFlame(Missile &missile, const AddMissileParameter ¶meter); void AddFlamec(Missile &missile, const AddMissileParameter ¶meter); /** * var1: Light strength * var2: Base direction */ void AddCbolt(Missile &missile, const AddMissileParameter ¶meter); void AddHbolt(Missile &missile, const AddMissileParameter ¶meter); void AddResurrect(Missile &missile, const AddMissileParameter ¶meter); void AddResurrectBeam(Missile &missile, const AddMissileParameter ¶meter); void AddTelekinesis(Missile &missile, const AddMissileParameter ¶meter); void AddBoneSpirit(Missile &missile, const AddMissileParameter ¶meter); void AddRportal(Missile &missile, const AddMissileParameter ¶meter); void AddDiabApoca(Missile &missile, const AddMissileParameter ¶meter); Missile *AddMissile(Point src, Point dst, Direction midir, missile_id mitype, mienemy_type micaster, int id, int midam, int spllvl, Missile *pParent = nullptr); void MI_LArrow(Missile &missile); void MI_Arrow(Missile &missile); void MI_Firebolt(Missile &missile); void MI_Lightball(Missile &missilei); void MI_Acidpud(Missile &missile); void MI_Firewall(Missile &missile); void MI_Fireball(Missile &missile); void MI_HorkSpawn(Missile &missile); void MI_Rune(Missile &missile); void MI_LightningWall(Missile &missile); void MI_HiveExplode(Missile &missile); void MI_LightningArrow(Missile &missile); void MI_FireRing(Missile &missile); void MI_Search(Missile &missile); void MI_LightningWallC(Missile &missile); void MI_FireNova(Missile &missile); void MI_SpecArrow(Missile &missile); void MI_Lightctrl(Missile &missile); void MI_Lightning(Missile &missile); void MI_Town(Missile &missile); void MI_Flash(Missile &missile); void MI_Flash2(Missile &missile); void MI_Firemove(Missile &missile); void MI_Guardian(Missile &missile); void MI_Chain(Missile &missile); void MI_Weapexp(Missile &missile); void MI_Misexp(Missile &missile); void MI_Acidsplat(Missile &missile); void MI_Teleport(Missile &missile); void MI_Stone(Missile &missile); void MI_Boom(Missile &missile); void MI_Rhino(Missile &missile); void MI_FirewallC(Missile &missile); void MI_Infra(Missile &missile); void MI_Apoca(Missile &missile); void MI_Wave(Missile &missile); void MI_Nova(Missile &missile); void MI_Blodboil(Missile &missile); void MI_Flame(Missile &missile); void MI_Flamec(Missile &missile); void MI_Cbolt(Missile &missile); void MI_Hbolt(Missile &missile); void MI_Element(Missile &missile); void MI_Bonespirit(Missile &missile); void MI_ResurrectBeam(Missile &missile); void MI_Rportal(Missile &missile); void ProcessMissiles(); void missiles_process_charge(); void RedoMissileFlags(); } // namespace devilution