/** * Tibia GIMUD Server - a free and open-source MMORPG server emulator * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef FS_COMBAT_H_B02CE79230FC43708699EE91FCC8F7CC #define FS_COMBAT_H_B02CE79230FC43708699EE91FCC8F7CC #include "thing.h" #include "condition.h" #include "map.h" #include "baseevents.h" class Condition; class Creature; class Item; struct Position; //for luascript callback class ValueCallback final : public CallBack { public: explicit ValueCallback(formulaType_t type): type(type) {} void getMinMaxValues(Player* player, CombatDamage& damage, bool useCharges) const; protected: formulaType_t type; }; class TileCallback final : public CallBack { public: void onTileCombat(Creature* creature, Tile* tile) const; protected: formulaType_t type; }; class TargetCallback final : public CallBack { public: void onTargetCombat(Creature* creature, Creature* target) const; protected: formulaType_t type; }; struct CombatParams { std::forward_list> conditionList; std::unique_ptr valueCallback; std::unique_ptr tileCallback; std::unique_ptr targetCallback; uint16_t itemId = 0; uint16_t decreaseDamage = 0; uint16_t maximumDecreasedDamage = 0; ConditionType_t dispelType = CONDITION_NONE; CombatType_t combatType = COMBAT_NONE; CombatOrigin origin = ORIGIN_SPELL; uint8_t impactEffect = CONST_ME_NONE; uint8_t distanceEffect = CONST_ANI_NONE; bool blockedByArmor = false; bool blockedByShield = false; bool targetCasterOrTopMost = false; bool aggressive = true; bool useCharges = false; }; struct Impact { Creature* actor = nullptr; virtual void handleCreature(Creature*) { // } }; struct DamageImpact : Impact { CombatParams params; CombatDamage damage; void handleCreature(Creature* target) final; }; struct SpeedImpact : Impact { int32_t percent = 0; int32_t duration = 0; void handleCreature(Creature* target) final; }; struct DunkenImpact : Impact { int32_t duration = 0; void handleCreature(Creature* target) final; }; typedef void(*COMBATFUNC)(Creature*, Creature*, const CombatParams&, CombatDamage*); class MatrixArea { public: MatrixArea(uint32_t rows, uint32_t cols): centerX(0), centerY(0), rows(rows), cols(cols) { data_ = new bool*[rows]; for (uint32_t row = 0; row < rows; ++row) { data_[row] = new bool[cols]; for (uint32_t col = 0; col < cols; ++col) { data_[row][col] = 0; } } } MatrixArea(const MatrixArea& rhs) { centerX = rhs.centerX; centerY = rhs.centerY; rows = rhs.rows; cols = rhs.cols; data_ = new bool*[rows]; for (uint32_t row = 0; row < rows; ++row) { data_[row] = new bool[cols]; for (uint32_t col = 0; col < cols; ++col) { data_[row][col] = rhs.data_[row][col]; } } } ~MatrixArea() { for (uint32_t row = 0; row < rows; ++row) { delete[] data_[row]; } delete[] data_; } // non-assignable MatrixArea& operator=(const MatrixArea&) = delete; void setValue(uint32_t row, uint32_t col, bool value) const { data_[row][col] = value; } bool getValue(uint32_t row, uint32_t col) const { return data_[row][col]; } void setCenter(uint32_t y, uint32_t x) { centerX = x; centerY = y; } void getCenter(uint32_t& y, uint32_t& x) const { x = centerX; y = centerY; } uint32_t getRows() const { return rows; } uint32_t getCols() const { return cols; } inline const bool* operator[](uint32_t i) const { return data_[i]; } inline bool* operator[](uint32_t i) { return data_[i]; } protected: uint32_t centerX; uint32_t centerY; uint32_t rows; uint32_t cols; bool** data_; }; class AreaCombat { public: AreaCombat() = default; AreaCombat(const AreaCombat& rhs); ~AreaCombat() { clear(); } // non-assignable AreaCombat& operator=(const AreaCombat&) = delete; void getList(const Position& centerPos, const Position& targetPos, std::forward_list& list) const; void setupArea(const std::list& list, uint32_t rows); void setupArea(int32_t length, int32_t spread); void setupArea(int32_t radius); void setupExtArea(const std::list& list, uint32_t rows); void clear(); protected: enum MatrixOperation_t { MATRIXOPERATION_COPY, MATRIXOPERATION_MIRROR, MATRIXOPERATION_FLIP, MATRIXOPERATION_ROTATE90, MATRIXOPERATION_ROTATE180, MATRIXOPERATION_ROTATE270, }; MatrixArea* createArea(const std::list& list, uint32_t rows); void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const; MatrixArea* getArea(const Position& centerPos, const Position& targetPos) const { int32_t dx = Position::getOffsetX(targetPos, centerPos); int32_t dy = Position::getOffsetY(targetPos, centerPos); Direction dir; if (dx < 0) { dir = DIRECTION_WEST; } else if (dx > 0) { dir = DIRECTION_EAST; } else if (dy < 0) { dir = DIRECTION_NORTH; } else { dir = DIRECTION_SOUTH; } if (hasExtArea) { if (dx < 0 && dy < 0) { dir = DIRECTION_NORTHWEST; } else if (dx > 0 && dy < 0) { dir = DIRECTION_NORTHEAST; } else if (dx < 0 && dy > 0) { dir = DIRECTION_SOUTHWEST; } else if (dx > 0 && dy > 0) { dir = DIRECTION_SOUTHEAST; } } auto it = areas.find(dir); if (it == areas.end()) { return nullptr; } return it->second; } std::map areas; bool hasExtArea = false; }; class Combat { public: Combat() = default; // non-copyable Combat(const Combat&) = delete; Combat& operator=(const Combat&) = delete; static int32_t computeDamage(Creature* creature, int32_t strength, int32_t variation); static int32_t getTotalDamage(int32_t attackSkill, int32_t attackValue, fightMode_t fightMode); static bool attack(Creature* attacker, Creature* target); static bool closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode); static bool rangeAttack(Creature* attacker, Creature* target, fightMode_t fightMode); static void circleShapeSpell(Creature* attacker, const Position& toPos, int32_t range, int32_t animation, int32_t radius, DamageImpact* impact, int32_t effect); static void getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill); static bool doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); static void doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params); static void doCombatMana(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); static void doCombatMana(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params); static void doCombatCondition(Creature* caster, Creature* target, const CombatParams& params); static void doCombatCondition(Creature* caster, const Position& position, const AreaCombat* area, const CombatParams& params); static void doCombatDispel(Creature* caster, Creature* target, const CombatParams& params); static void doCombatDispel(Creature* caster, const Position& position, const AreaCombat* area, const CombatParams& params); static void getCombatArea(const Position& centerPos, const Position& targetPos, const AreaCombat* area, std::forward_list& list); static bool isInPvpZone(const Creature* attacker, const Creature* target); static bool isProtected(const Player* attacker, const Player* target); static bool isPlayerCombat(const Creature* target); static CombatType_t ConditionToDamageType(ConditionType_t type); static ConditionType_t DamageToConditionType(CombatType_t type); static ReturnValue canTargetCreature(Player* attacker, Creature* target); static ReturnValue canDoCombat(Creature* caster, Tile* tile, bool aggressive); static ReturnValue canDoCombat(Creature* attacker, Creature* target); static void postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params); static void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect); void doCombat(Creature* caster, Creature* target) const; void doCombat(Creature* caster, const Position& pos) const; bool setCallback(CallBackParam_t key); CallBack* getCallback(CallBackParam_t key); bool setParam(CombatParam_t param, uint32_t value); void setArea(AreaCombat* area) { this->area.reset(area); } bool hasArea() const { return area != nullptr; } void setCondition(const Condition* condition) { params.conditionList.emplace_front(condition); } void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb); void postCombatEffects(Creature* caster, const Position& pos) const { postCombatEffects(caster, pos, params); } void setOrigin(CombatOrigin origin) { params.origin = origin; } protected: static bool canUseWeapon(Player* player, Item* weapon); static void postWeaponEffects(Player* player, Item* weapon); static void doCombatDefault(Creature* caster, Creature* target, const CombatParams& params); static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, COMBATFUNC func, CombatDamage* data); static void CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); static void CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage); static void CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); static void CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); static void CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); static void combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params); CombatDamage getCombatDamage(Creature* creature) const; //configureable CombatParams params; //formula variables formulaType_t formulaType = COMBAT_FORMULA_UNDEFINED; double mina = 0.0; double minb = 0.0; double maxa = 0.0; double maxb = 0.0; std::unique_ptr area; }; class MagicField final : public Item { public: explicit MagicField(uint16_t type) : Item(type), createTime(OTSYS_TIME()) {} MagicField* getMagicField() final { return this; } const MagicField* getMagicField() const final { return this; } bool isReplaceable() const { return Item::items[getID()].replaceable; } CombatType_t getCombatType() const { const ItemType& it = items[getID()]; return it.combatType; } void onStepInField(Creature* creature); private: int64_t createTime; }; #endif