1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#ifndef FS_COMBAT_H_B02CE79230FC43708699EE91FCC8F7CC
21#define FS_COMBAT_H_B02CE79230FC43708699EE91FCC8F7CC
22
23#include "thing.h"
24#include "condition.h"
25#include "map.h"
26#include "baseevents.h"
27
28class Condition;
29class Creature;
30class Item;
31
32struct Position;
33
34//for luascript callback
35class ValueCallback final : public CallBack
36{
37 public:
38 explicit ValueCallback(formulaType_t type): type(type) {}
39 void getMinMaxValues(Player* player, CombatDamage& damage) const;
40
41 private:
42 formulaType_t type;
43};
44
45class TileCallback final : public CallBack
46{
47 public:
48 void onTileCombat(Creature* creature, Tile* tile) const;
49};
50
51class TargetCallback final : public CallBack
52{
53 public:
54 void onTargetCombat(Creature* creature, Creature* target) const;
55};
56
57struct CombatParams {
58 std::forward_list<std::unique_ptr<const Condition>> conditionList;
59
60 std::unique_ptr<ValueCallback> valueCallback;
61 std::unique_ptr<TileCallback> tileCallback;
62 std::unique_ptr<TargetCallback> targetCallback;
63
64 uint16_t itemId = 0;
65
66 ConditionType_t dispelType = CONDITION_NONE;
67 CombatType_t combatType = COMBAT_NONE;
68 CombatOrigin origin = ORIGIN_SPELL;
69
70 uint16_t impactEffect = CONST_ME_NONE;
71 uint8_t distanceEffect = CONST_ANI_NONE;
72
73 bool blockedByArmor = false;
74 bool blockedByShield = false;
75 bool targetCasterOrTopMost = false;
76 bool aggressive = true;
77 bool useCharges = false;
78};
79
80class MatrixArea
81{
82 public:
83 MatrixArea(uint32_t rows, uint32_t cols): centerX(0), centerY(0), rows(rows), cols(cols) {
84 data_ = new bool*[rows];
85
86 for (uint32_t row = 0; row < rows; ++row) {
87 data_[row] = new bool[cols];
88
89 for (uint32_t col = 0; col < cols; ++col) {
90 data_[row][col] = 0;
91 }
92 }
93 }
94
95 MatrixArea(const MatrixArea& rhs) {
96 centerX = rhs.centerX;
97 centerY = rhs.centerY;
98 rows = rhs.rows;
99 cols = rhs.cols;
100
101 data_ = new bool*[rows];
102
103 for (uint32_t row = 0; row < rows; ++row) {
104 data_[row] = new bool[cols];
105
106 for (uint32_t col = 0; col < cols; ++col) {
107 data_[row][col] = rhs.data_[row][col];
108 }
109 }
110 }
111
112 ~MatrixArea() {
113 for (uint32_t row = 0; row < rows; ++row) {
114 delete[] data_[row];
115 }
116
117 delete[] data_;
118 }
119
120 // non-assignable
121 MatrixArea& operator=(const MatrixArea&) = delete;
122
123 void setValue(uint32_t row, uint32_t col, bool value) const {
124 data_[row][col] = value;
125 }
126 bool getValue(uint32_t row, uint32_t col) const {
127 return data_[row][col];
128 }
129
130 void setCenter(uint32_t y, uint32_t x) {
131 centerX = x;
132 centerY = y;
133 }
134 void getCenter(uint32_t& y, uint32_t& x) const {
135 x = centerX;
136 y = centerY;
137 }
138
139 uint32_t getRows() const {
140 return rows;
141 }
142 uint32_t getCols() const {
143 return cols;
144 }
145
146 const bool* operator[](uint32_t i) const {
147 return data_[i];
148 }
149 bool* operator[](uint32_t i) {
150 return data_[i];
151 }
152
153 private:
154 uint32_t centerX;
155 uint32_t centerY;
156
157 uint32_t rows;
158 uint32_t cols;
159 bool** data_;
160};
161
162class AreaCombat
163{
164 public:
165 AreaCombat() = default;
166
167 AreaCombat(const AreaCombat& rhs);
168 ~AreaCombat() {
169 clear();
170 }
171
172 // non-assignable
173 AreaCombat& operator=(const AreaCombat&) = delete;
174
175 void getList(const Position& centerPos, const Position& targetPos, std::forward_list<Tile*>& list) const;
176
177 void setupArea(const std::list<uint32_t>& list, uint32_t rows);
178 void setupArea(int32_t length, int32_t spread);
179 void setupArea(int32_t radius);
180 void setupExtArea(const std::list<uint32_t>& list, uint32_t rows);
181 void clear();
182
183 private:
184 enum MatrixOperation_t {
185 MATRIXOPERATION_COPY,
186 MATRIXOPERATION_MIRROR,
187 MATRIXOPERATION_FLIP,
188 MATRIXOPERATION_ROTATE90,
189 MATRIXOPERATION_ROTATE180,
190 MATRIXOPERATION_ROTATE270,
191 };
192
193 MatrixArea* createArea(const std::list<uint32_t>& list, uint32_t rows);
194 static void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op);
195
196 MatrixArea* getArea(const Position& centerPos, const Position& targetPos) const {
197 int32_t dx = Position::getOffsetX(targetPos, centerPos);
198 int32_t dy = Position::getOffsetY(targetPos, centerPos);
199
200 Direction dir;
201 if (dx < 0) {
202 dir = DIRECTION_WEST;
203 } else if (dx > 0) {
204 dir = DIRECTION_EAST;
205 } else if (dy < 0) {
206 dir = DIRECTION_NORTH;
207 } else {
208 dir = DIRECTION_SOUTH;
209 }
210
211 if (hasExtArea) {
212 if (dx < 0 && dy < 0) {
213 dir = DIRECTION_NORTHWEST;
214 } else if (dx > 0 && dy < 0) {
215 dir = DIRECTION_NORTHEAST;
216 } else if (dx < 0 && dy > 0) {
217 dir = DIRECTION_SOUTHWEST;
218 } else if (dx > 0 && dy > 0) {
219 dir = DIRECTION_SOUTHEAST;
220 }
221 }
222
223 auto it = areas.find(dir);
224 if (it == areas.end()) {
225 return nullptr;
226 }
227 return it->second;
228 }
229
230 std::map<Direction, MatrixArea*> areas;
231 bool hasExtArea = false;
232};
233
234class Combat
235{
236 public:
237 Combat() = default;
238
239 // non-copyable
240 Combat(const Combat&) = delete;
241 Combat& operator=(const Combat&) = delete;
242
243 static void getCombatArea(const Position& centerPos, const Position& targetPos, const AreaCombat* area, std::forward_list<Tile*>& list);
244
245 static bool isInPvpZone(const Creature* attacker, const Creature* target);
246 static bool isProtected(const Player* attacker, const Player* target);
247 static bool isPlayerCombat(const Creature* target);
248 static CombatType_t ConditionToDamageType(ConditionType_t type);
249 static ConditionType_t DamageToConditionType(CombatType_t type);
250 static ReturnValue canTargetCreature(Player* attacker, Creature* target);
251 static ReturnValue canDoCombat(Creature* caster, Tile* tile, bool aggressive);
252 static ReturnValue canDoCombat(Creature* attacker, Creature* target);
253 static void postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params);
254
255 static void addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, uint8_t effect);
256
257 void doCombat(Creature* caster, Creature* target) const;
258 void doCombat(Creature* caster, const Position& position) const;
259
260 static void doTargetCombat(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params);
261 static void doAreaCombat(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params);
262
263 static void checkCriticalHit(Player* caster, CombatDamage& damage);
264 static void checkLeech(Player* caster, CombatDamage& damage);
265
266 bool setCallback(CallBackParam_t key);
267 CallBack* getCallback(CallBackParam_t key);
268
269 bool setParam(CombatParam_t param, uint32_t value);
270 void setArea(AreaCombat* area) {
271 this->area.reset(area);
272 }
273 bool hasArea() const {
274 return area != nullptr;
275 }
276 void addCondition(const Condition* condition) {
277 params.conditionList.emplace_front(condition);
278 }
279 void clearConditions() {
280 params.conditionList.clear();
281 }
282 void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb);
283 void postCombatEffects(Creature* caster, const Position& pos) const {
284 postCombatEffects(caster, pos, params);
285 }
286
287 void setOrigin(CombatOrigin origin) {
288 params.origin = origin;
289 }
290
291 private:
292 static void combatTileEffects(const SpectatorVec& spectators, Creature* caster, Tile* tile, const CombatParams& params);
293 CombatDamage getCombatDamage(Creature* creature, Creature* target) const;
294
295 //configureable
296 CombatParams params;
297
298 //formula variables
299 formulaType_t formulaType = COMBAT_FORMULA_UNDEFINED;
300 double mina = 0.0;
301 double minb = 0.0;
302 double maxa = 0.0;
303 double maxb = 0.0;
304
305 std::unique_ptr<AreaCombat> area;
306};
307
308class MagicField final : public Item
309{
310 public:
311 explicit MagicField(uint16_t type) : Item(type), createTime(OTSYS_TIME()) {}
312
313 MagicField* getMagicField() override {
314 return this;
315 }
316 const MagicField* getMagicField() const override {
317 return this;
318 }
319
320 bool isReplaceable() const {
321 return Item::items[getID()].replaceable;
322 }
323 CombatType_t getCombatType() const {
324 const ItemType& it = items[getID()];
325 return it.combatType;
326 }
327 int32_t getDamage() const {
328 const ItemType& it = items[getID()];
329 if (it.conditionDamage) {
330 return it.conditionDamage->getTotalDamage();
331 }
332 return 0;
333 }
334 void onStepInField(Creature* creature);
335
336 private:
337 int64_t createTime;
338};
339
340#endif
341