1 /*
2  * Copyright (C) 2002-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #ifndef WL_LOGIC_MAP_OBJECTS_TRIBES_SOLDIER_H
21 #define WL_LOGIC_MAP_OBJECTS_TRIBES_SOLDIER_H
22 
23 #include <memory>
24 
25 #include "base/macros.h"
26 #include "logic/map_objects/tribes/training_attribute.h"
27 #include "logic/map_objects/tribes/worker.h"
28 
29 namespace Widelands {
30 
31 // Constants used to launch attacks
32 #define WEAKEST 0
33 #define STRONGEST 1
34 
35 class EditorGameBase;
36 class Battle;
37 
38 struct SoldierLevelRange {
39 	SoldierLevelRange();
40 	SoldierLevelRange(const LuaTable&);
41 	SoldierLevelRange(const SoldierLevelRange&) = default;
42 	SoldierLevelRange& operator=(const SoldierLevelRange& other) = default;
43 
44 	bool matches(const Soldier* soldier) const;
45 	bool matches(int32_t health, int32_t attack, int32_t defense, int32_t evade) const;
46 
47 	bool operator==(const SoldierLevelRange& other) const {
48 		return min_health == other.min_health && min_attack == other.min_attack &&
49 		       min_defense == other.min_defense && min_evade == other.min_evade &&
50 		       max_health == other.max_health && max_attack == other.max_attack &&
51 		       max_defense == other.max_defense && max_evade == other.max_evade;
52 	}
53 
54 	int32_t min_health;
55 	int32_t min_attack;
56 	int32_t min_defense;
57 	int32_t min_evade;
58 	int32_t max_health;
59 	int32_t max_attack;
60 	int32_t max_defense;
61 	int32_t max_evade;
62 };
63 using SoldierAnimationsList = std::map<std::string, SoldierLevelRange>;
64 
65 class SoldierDescr : public WorkerDescr {
66 public:
67 	friend class Economy;
68 
69 	SoldierDescr(const std::string& init_descname, const LuaTable& t, const Tribes& tribes);
~SoldierDescr()70 	~SoldierDescr() override {
71 	}
72 
get_max_health_level()73 	uint32_t get_max_health_level() const {
74 		return health_.max_level;
75 	}
get_max_attack_level()76 	uint32_t get_max_attack_level() const {
77 		return attack_.max_level;
78 	}
get_max_defense_level()79 	uint32_t get_max_defense_level() const {
80 		return defense_.max_level;
81 	}
get_max_evade_level()82 	uint32_t get_max_evade_level() const {
83 		return evade_.max_level;
84 	}
85 
get_base_health()86 	uint32_t get_base_health() const {
87 		return health_.base;
88 	}
get_base_min_attack()89 	uint32_t get_base_min_attack() const {
90 		return attack_.base;
91 	}
get_base_max_attack()92 	uint32_t get_base_max_attack() const {
93 		return attack_.maximum;
94 	}
get_base_defense()95 	uint32_t get_base_defense() const {
96 		return defense_.base;
97 	}
get_base_evade()98 	uint32_t get_base_evade() const {
99 		return evade_.base;
100 	}
101 
get_health_incr_per_level()102 	uint32_t get_health_incr_per_level() const {
103 		return health_.increase;
104 	}
get_attack_incr_per_level()105 	uint32_t get_attack_incr_per_level() const {
106 		return attack_.increase;
107 	}
get_defense_incr_per_level()108 	uint32_t get_defense_incr_per_level() const {
109 		return defense_.increase;
110 	}
get_evade_incr_per_level()111 	uint32_t get_evade_incr_per_level() const {
112 		return evade_.increase;
113 	}
114 
get_health_level_pic(uint32_t const level)115 	const Image* get_health_level_pic(uint32_t const level) const {
116 		assert(level <= get_max_health_level());
117 		return health_.images[level];
118 	}
get_attack_level_pic(uint32_t const level)119 	const Image* get_attack_level_pic(uint32_t const level) const {
120 		assert(level <= get_max_attack_level());
121 		return attack_.images[level];
122 	}
get_defense_level_pic(uint32_t const level)123 	const Image* get_defense_level_pic(uint32_t const level) const {
124 		assert(level <= get_max_defense_level());
125 		return defense_.images[level];
126 	}
get_evade_level_pic(uint32_t const level)127 	const Image* get_evade_level_pic(uint32_t const level) const {
128 		assert(level <= get_max_evade_level());
129 		return evade_.images[level];
130 	}
131 
132 	uint32_t get_rand_anim(Game& game, const std::string& name, const Soldier* soldier) const;
133 
134 	const DirAnimations& get_right_walk_anims(bool const ware, Worker* w) const override;
135 	uint32_t get_animation(const std::string& anim, const MapObject* mo = nullptr) const override;
136 
137 protected:
138 	Bob& create_object() const override;
139 
140 private:
141 	// Health, Attack, Defense and Evade values.
142 	struct BattleAttribute {
143 		explicit BattleAttribute(std::unique_ptr<LuaTable> table);
144 
145 		uint32_t base;                     // Base value
146 		uint32_t maximum;                  // Maximum value for randomizing attack values
147 		uint32_t increase;                 // Per level increase
148 		uint32_t max_level;                // Maximum level
149 		std::vector<const Image*> images;  // Level images
150 	};
151 
152 	BattleAttribute health_;
153 	BattleAttribute attack_;
154 	BattleAttribute defense_;
155 	BattleAttribute evade_;
156 
157 	// Battle animation names
158 	SoldierAnimationsList attack_success_w_name_;
159 	SoldierAnimationsList attack_failure_w_name_;
160 	SoldierAnimationsList evade_success_w_name_;
161 	SoldierAnimationsList evade_failure_w_name_;
162 	SoldierAnimationsList die_w_name_;
163 
164 	SoldierAnimationsList attack_success_e_name_;
165 	SoldierAnimationsList attack_failure_e_name_;
166 	SoldierAnimationsList evade_success_e_name_;
167 	SoldierAnimationsList evade_failure_e_name_;
168 	SoldierAnimationsList die_e_name_;
169 
170 	// We can have per-level walking and idle anims
171 	// NOTE: I expect no soldier will ever agree to carry a ware, so we don't provide animations for
172 	// that. NOTE: All walking animations are expected to have the same set of ranges.
173 	SoldierAnimationsList idle_name_;
174 	std::unordered_map<std::unique_ptr<SoldierLevelRange>, std::map<uint8_t, std::string>>
175 	   walk_name_;
176 
177 	// Reads list of animation names from the table and pushes them into result.
178 	void add_battle_animation(std::unique_ptr<LuaTable> table, SoldierAnimationsList* result);
179 
180 	DISALLOW_COPY_AND_ASSIGN(SoldierDescr);
181 };
182 
183 enum CombatWalkingDir {
184 	CD_NONE = 0,      // Not in combat
185 	CD_WALK_W = 1,    // Going to west       (facing west)
186 	CD_WALK_E = 2,    // Going to east       (facing east)
187 	CD_COMBAT_W = 3,  // Fighting at west    (facing east!!)
188 	CD_COMBAT_E = 4,  // Fighting at east    (facing west!!)
189 	CD_RETURN_W = 5,  // Returning from west (facing east!!)
190 	CD_RETURN_E = 6,  // Returning from east (facing west!!)
191 };
192 
193 enum CombatFlags {
194 	/// Soldier will wait enemies at his building flag. Only for defenders.
195 	CF_DEFEND_STAYHOME = 1,
196 	/// When current health points drop below a fixed percentage, soldier will flee
197 	/// and heal inside military building
198 	CF_RETREAT_WHEN_INJURED = 2,
199 	/// Attackers would try avoid entering combat with others soldiers but 'flag
200 	/// defenders'.
201 	CF_AVOID_COMBAT = 4,
202 };
203 
204 class Soldier : public Worker {
205 	friend struct MapBobdataPacket;
206 	MO_DESCR(SoldierDescr)
207 
208 public:
209 	enum class InfoMode { kWalkingAround, kInBuilding };
210 
211 	explicit Soldier(const SoldierDescr&);
212 
213 	bool init(EditorGameBase&) override;
214 	void cleanup(EditorGameBase&) override;
215 
216 	void set_level(uint32_t health, uint32_t attack, uint32_t defense, uint32_t evade);
217 	void set_health_level(uint32_t);
218 	void set_attack_level(uint32_t);
219 	void set_defense_level(uint32_t);
220 	void set_evade_level(uint32_t);
221 	void set_retreat_health(uint32_t);
222 	uint32_t get_level(TrainingAttribute) const;
get_health_level()223 	uint32_t get_health_level() const {
224 		return health_level_;
225 	}
get_attack_level()226 	uint32_t get_attack_level() const {
227 		return attack_level_;
228 	}
get_defense_level()229 	uint32_t get_defense_level() const {
230 		return defense_level_;
231 	}
get_evade_level()232 	uint32_t get_evade_level() const {
233 		return evade_level_;
234 	}
get_total_level()235 	uint32_t get_total_level() const {
236 		return health_level_ + attack_level_ + defense_level_ + evade_level_;
237 	}
238 
239 	/// Automatically select a task.
240 	void init_auto_task(Game&) override;
241 
242 	Vector2f
243 	calc_drawpos(const EditorGameBase& game, const Vector2f& field_on_dst, const float scale) const;
244 
245 	/// Draw this soldier
246 	void draw(const EditorGameBase&,
247 	          const InfoToDraw& info_to_draw,
248 	          const Vector2f& point_on_dst,
249 	          const Widelands::Coords& coords,
250 	          float scale,
251 	          RenderTarget* dst) const override;
252 
253 	static void calc_info_icon_size(const TribeDescr&, int& w, int& h);
254 
255 	// Draw the info icon containing health bar and levels. If 'anchor_below' is
256 	// true, the icon is drawn horizontally centered above Otherwise, the icon
257 	// is drawn below and right of 'draw_position'.
258 	void draw_info_icon(Vector2i draw_position,
259 	                    const float scale,
260 	                    const InfoMode draw_mode,
261 	                    const InfoToDraw info_to_draw,
262 	                    RenderTarget*) const;
263 
get_current_health()264 	uint32_t get_current_health() const {
265 		return current_health_;
266 	}
get_retreat_health()267 	uint32_t get_retreat_health() const {
268 		return retreat_health_;
269 	}
270 	uint32_t get_max_health() const;
271 	uint32_t get_min_attack() const;
272 	uint32_t get_max_attack() const;
273 	uint32_t get_defense() const;
274 	uint32_t get_evade() const;
275 
get_health_level_pic()276 	const Image* get_health_level_pic() const {
277 		return descr().get_health_level_pic(health_level_);
278 	}
get_attack_level_pic()279 	const Image* get_attack_level_pic() const {
280 		return descr().get_attack_level_pic(attack_level_);
281 	}
get_defense_level_pic()282 	const Image* get_defense_level_pic() const {
283 		return descr().get_defense_level_pic(defense_level_);
284 	}
get_evade_level_pic()285 	const Image* get_evade_level_pic() const {
286 		return descr().get_evade_level_pic(evade_level_);
287 	}
288 
289 	int32_t get_training_attribute(TrainingAttribute attr) const override;
290 
291 	/// Sets a random animation of desired type and start playing it.
292 	void start_animation(EditorGameBase&, const std::string& animname, uint32_t time);
293 
294 	/// Heal quantity of health points instantly
295 	void heal(uint32_t);
296 	void damage(uint32_t);  /// Damage quantity of health points
297 
298 	void log_general_info(const EditorGameBase&) const override;
299 
300 	bool is_on_battlefield();
301 	bool is_attacking_player(Game&, Player&);
302 	Battle* get_battle() const;
303 	bool can_be_challenged();
304 	bool check_node_blocked(Game&, const FCoords&, bool commit) override;
305 
306 	void set_battle(Game&, Battle*);
307 
308 	void start_task_attack(Game& game, Building&);
309 	void start_task_defense(Game& game, bool stayhome);
310 	void start_task_battle(Game&);
311 	void start_task_move_in_battle(Game&, CombatWalkingDir);
312 	void start_task_die(Game&);
313 
314 	std::pair<std::unique_ptr<SoldierLevelRange>, std::unique_ptr<DirAnimations>>&
get_walking_animations_cache()315 	get_walking_animations_cache() {
316 		return walking_animations_cache_;
317 	}
318 
319 private:
320 	void attack_update(Game&, State&);
321 	void attack_pop(Game&, State&);
322 	void defense_update(Game&, State&);
323 	void defense_pop(Game&, State&);
324 	void battle_update(Game&, State&);
325 	void battle_pop(Game&, State&);
326 	void move_in_battle_update(Game&, State&);
327 	void die_update(Game&, State&);
328 	void die_pop(Game&, State&);
329 
330 	void send_space_signals(Game&);
331 	bool stay_home();
332 
333 	// Pop the current task or, if challenged, start the fighting task.
334 	void pop_task_or_fight(Game&);
335 
336 protected:
337 	static Task const taskAttack;
338 	static Task const taskDefense;
339 	static Task const taskBattle;
340 	static Task const taskMoveInBattle;
341 	// May be this can be moved this to bob when finished
342 	static Task const taskDie;
343 
344 	bool is_evict_allowed() override;
345 
346 private:
347 	uint32_t current_health_;
348 	uint32_t health_level_;
349 	uint32_t attack_level_;
350 	uint32_t defense_level_;
351 	uint32_t evade_level_;
352 	uint32_t retreat_health_;
353 
354 	/// This is used to replicate walk for soldiers but only just before and
355 	/// just after figthing in a battle, to draw soldier at proper position.
356 	/// Maybe Bob.walking_ could be used, but then that variable should be
357 	/// protected instead of private, and some type of rework needed to allow
358 	/// the new states. I thought that it is cleaner to have this variable
359 	/// separate.
360 	CombatWalkingDir combat_walking_;
361 	uint32_t combat_walkstart_;
362 	uint32_t combat_walkend_;
363 
364 	/**
365 	 * If the soldier is involved in a challenge, it is assigned a battle
366 	 * object.
367 	 */
368 	Battle* battle_;
369 
370 	std::pair<std::unique_ptr<SoldierLevelRange>, std::unique_ptr<DirAnimations>>
371 	   walking_animations_cache_;
372 
373 	/// Number of consecutive blocked signals until the soldiers are considered permanently stuck
374 	static constexpr uint8_t kBockCountIsStuck = 10;
375 
376 	// saving and loading
377 protected:
378 	struct Loader : public Worker::Loader {
379 	public:
380 		Loader();
381 
382 		void load(FileRead&) override;
383 		void load_pointers() override;
384 
385 	protected:
386 		const Task* get_task(const std::string& name) override;
387 
388 	private:
389 		uint32_t battle_;
390 	};
391 
392 	Loader* create_loader() override;
393 
394 public:
395 	void do_save(EditorGameBase&, MapObjectSaver&, FileWrite&) override;
396 };
397 }  // namespace Widelands
398 
399 #endif  // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_SOLDIER_H
400