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