player_status.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. // SuperTux
  2. // Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
  3. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  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 3 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
  16. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #include "supertux/player_status.hpp"
  18. #include <sstream>
  19. #include "audio/sound_manager.hpp"
  20. #include "object/player.hpp"
  21. #include "supertux/globals.hpp"
  22. #include "supertux/game_session.hpp"
  23. #include "supertux/sector.hpp"
  24. #include "util/log.hpp"
  25. #include "util/reader_mapping.hpp"
  26. #include "util/writer.hpp"
  27. #include "worldmap/level_tile.hpp"
  28. #include "worldmap/worldmap.hpp"
  29. static const int START_COINS = 100;
  30. static const int MAX_COINS = 9999;
  31. PlayerStatus::PlayerStatus(int num_players) :
  32. m_num_players(num_players),
  33. m_item_pockets(num_players),
  34. m_override_item_pocket(Level::INHERIT),
  35. coins(START_COINS),
  36. bonus(num_players),
  37. worldmap_sprite("images/worldmap/common/tux.sprite"),
  38. last_worldmap(),
  39. title_level()
  40. {
  41. reset(num_players);
  42. // FIXME: Move sound handling into PlayerStatusHUD
  43. if (SoundManager::current()) {
  44. SoundManager::current()->preload("sounds/coin.wav");
  45. SoundManager::current()->preload("sounds/lifeup.wav");
  46. }
  47. }
  48. void
  49. PlayerStatus::take_checkpoint_coins()
  50. {
  51. int subtract_value = std::max(coins / 10, 25);
  52. if (coins - subtract_value >= 0)
  53. coins -= subtract_value;
  54. else
  55. coins = 0;
  56. }
  57. void
  58. PlayerStatus::reset(int num_players)
  59. {
  60. coins = START_COINS;
  61. // Keep in sync with a section in read()
  62. bonus.clear();
  63. bonus.resize(num_players, BONUS_NONE);
  64. m_item_pockets.clear();
  65. m_item_pockets.resize(num_players, BONUS_NONE);
  66. m_num_players = num_players;
  67. }
  68. int
  69. PlayerStatus::get_max_coins() const
  70. {
  71. return MAX_COINS;
  72. }
  73. bool
  74. PlayerStatus::can_reach_checkpoint() const
  75. {
  76. return GameSession::current()->get_active_checkpoint_spawnpoint();
  77. }
  78. bool
  79. PlayerStatus::respawns_at_checkpoint() const
  80. {
  81. return GameSession::current()->get_last_spawnpoint().is_checkpoint ||
  82. GameSession::current()->reset_checkpoint_button;
  83. }
  84. std::string
  85. PlayerStatus::get_bonus_name(BonusType bonustype)
  86. {
  87. switch (bonustype) {
  88. case BONUS_FIRE:
  89. return "fireflower";
  90. case BONUS_ICE:
  91. return "iceflower";
  92. case BONUS_AIR:
  93. return "airflower";
  94. case BONUS_EARTH:
  95. return "earthflower";
  96. case BONUS_GROWUP:
  97. return "egg";
  98. case BONUS_NONE:
  99. return "none";
  100. default:
  101. log_warning << "Unknown bonus " << static_cast<int>(bonustype) << std::endl;
  102. return "none";
  103. }
  104. }
  105. BonusType
  106. PlayerStatus::get_bonus_from_name(const std::string& name)
  107. {
  108. if (name == "none") {
  109. return BONUS_NONE;
  110. } else if (name == "growup") {
  111. return BONUS_GROWUP;
  112. } else if (name == "fireflower") {
  113. return BONUS_FIRE;
  114. } else if (name == "iceflower") {
  115. return BONUS_ICE;
  116. } else if (name == "airflower") {
  117. return BONUS_AIR;
  118. } else if (name == "earthflower") {
  119. return BONUS_EARTH;
  120. } else {
  121. log_warning << "Unknown bonus '" << name << "' in savefile"<< std::endl;
  122. return BONUS_NONE;
  123. }
  124. }
  125. std::string
  126. PlayerStatus::get_bonus_sprite(BonusType bonustype)
  127. {
  128. switch (bonustype) {
  129. case BONUS_FIRE:
  130. return "images/powerups/fireflower/fireflower.sprite";
  131. case BONUS_ICE:
  132. return "images/powerups/iceflower/iceflower.sprite";
  133. case BONUS_AIR:
  134. return "images/powerups/airflower/airflower.sprite";
  135. case BONUS_EARTH:
  136. return "images/powerups/earthflower/earthflower.sprite";
  137. case BONUS_GROWUP:
  138. return "images/powerups/egg/egg.sprite";
  139. default:
  140. return "";
  141. }
  142. }
  143. void
  144. PlayerStatus::add_coins(int count, bool play_sound)
  145. {
  146. coins = std::min(coins + count, MAX_COINS);
  147. if (!play_sound)
  148. return;
  149. static float sound_played_time = 0;
  150. if (count >= 100)
  151. SoundManager::current()->play("sounds/lifeup.wav");
  152. else if (g_real_time > sound_played_time + 0.010f) {
  153. SoundManager::current()->play("sounds/coin.wav");
  154. sound_played_time = g_real_time;
  155. }
  156. }
  157. void
  158. PlayerStatus::write(Writer& writer)
  159. {
  160. writer.write("num_players", m_num_players);
  161. for (int i = 0; i < m_num_players; i++)
  162. {
  163. if (i != 0)
  164. {
  165. writer.start_list("tux" + std::to_string(i + 1));
  166. }
  167. writer.write("bonus", get_bonus_name(bonus[i]));
  168. writer.write("item-pocket", get_bonus_name(m_item_pockets[i]));
  169. if (i != 0)
  170. {
  171. writer.end_list("tux" + std::to_string(i + 1));
  172. }
  173. }
  174. writer.write("coins", coins);
  175. writer.write("worldmap-sprite", worldmap_sprite, false);
  176. writer.write("last-worldmap", last_worldmap, false);
  177. writer.write("title-level", title_level);
  178. }
  179. void
  180. PlayerStatus::read(const ReaderMapping& mapping)
  181. {
  182. int num_players_in_file = 1;
  183. mapping.get("num_players", num_players_in_file);
  184. reset(std::max(m_num_players, num_players_in_file));
  185. auto iter = mapping.get_iter();
  186. while (iter.next())
  187. {
  188. try
  189. {
  190. if (iter.get_key().size() > 3 && iter.get_key().substr(0, 3) == "tux")
  191. {
  192. int id = std::stoi(iter.get_key().substr(3)) - 1;
  193. if (id >= m_num_players)
  194. {
  195. log_warning << "ID larger than amount of players when reading player state: " << id << std::endl;
  196. // Keep this in sync with reset()
  197. if (bonus.size() < static_cast<size_t>(id))
  198. bonus.resize(id, BONUS_NONE);
  199. if (m_item_pockets.size() < static_cast<size_t>(id))
  200. m_item_pockets.resize(id, BONUS_NONE);
  201. }
  202. else if (id == 0)
  203. {
  204. log_warning << "Refusing to parse player 1 when reading player state,"
  205. "please don't put player 1 data in a (tux1 ...)"
  206. "wrapper for retrocompatibiility" << std::endl;
  207. }
  208. auto map = iter.as_mapping();
  209. parse_bonus_mapping(map, id);
  210. }
  211. }
  212. catch (const std::exception& e)
  213. {
  214. log_warning << "Couldn't parse player from player status save: " << e.what() << std::endl;
  215. }
  216. }
  217. parse_bonus_mapping(mapping, 0);
  218. mapping.get("coins", coins);
  219. mapping.get("worldmap-sprite", worldmap_sprite);
  220. mapping.get("last-worldmap", last_worldmap);
  221. mapping.get("title-level", title_level);
  222. }
  223. void
  224. PlayerStatus::give_item_from_pocket(Player* player)
  225. {
  226. if (!is_item_pocket_allowed())
  227. return;
  228. BonusType bonustype = m_item_pockets[player->get_id()];
  229. if (bonustype == BONUS_NONE)
  230. return;
  231. m_item_pockets[player->get_id()] = BONUS_NONE;
  232. auto& powerup = Sector::get().add<PocketPowerUp>(bonustype, Vector(0,0));
  233. powerup.set_pos(player->get_pos() - Vector(0.f, powerup.get_bbox().get_height() + 15));
  234. }
  235. void
  236. PlayerStatus::add_item_to_pocket(BonusType bonustype, Player* player)
  237. {
  238. if (!is_item_pocket_allowed())
  239. return;
  240. if (bonustype <= BONUS_GROWUP)
  241. return;
  242. m_item_pockets[player->get_id()] = bonustype;
  243. }
  244. BonusType
  245. PlayerStatus::get_item_pocket(const Player* player) const
  246. {
  247. return m_item_pockets[player->get_id()];
  248. }
  249. bool
  250. PlayerStatus::is_item_pocket_allowed() const
  251. {
  252. if (m_override_item_pocket != Level::INHERIT)
  253. {
  254. return m_override_item_pocket == Level::ON;
  255. }
  256. GameSession* session = GameSession::current();
  257. if (!session)
  258. {
  259. worldmap::WorldMap* worldmap = worldmap::WorldMap::current();
  260. if (worldmap)
  261. {
  262. return worldmap->is_item_pocket_allowed();
  263. }
  264. else
  265. {
  266. // Not in a level nor in a worldmap. Return true, I guess.
  267. return true;
  268. }
  269. }
  270. Level& level = session->get_current_level();
  271. int allowed = static_cast<Level::Setting>(level.m_allow_item_pocket);
  272. if (allowed != Level::INHERIT)
  273. {
  274. return allowed == Level::ON;
  275. }
  276. else
  277. {
  278. worldmap::WorldMap* worldmap = worldmap::WorldMap::current();
  279. if (worldmap)
  280. {
  281. return worldmap->is_item_pocket_allowed();
  282. }
  283. else
  284. {
  285. // This level is probably in a levelset, pick ON.
  286. return true;
  287. }
  288. }
  289. }
  290. void
  291. PlayerStatus::parse_bonus_mapping(const ReaderMapping& map, int id)
  292. {
  293. std::string bonusname;
  294. if (map.get("bonus", bonusname)) {
  295. bonus[id] = get_bonus_from_name(bonusname);
  296. }
  297. if (map.get("item-pocket", bonusname)) {
  298. m_item_pockets[id] = get_bonus_from_name(bonusname);
  299. }
  300. }
  301. std::string
  302. PlayerStatus::get_bonus_prefix(int player_id) const
  303. {
  304. switch (bonus[player_id]) {
  305. default:
  306. case BONUS_NONE:
  307. return "small";
  308. case BONUS_GROWUP:
  309. return "big";
  310. case BONUS_FIRE:
  311. return "fire";
  312. case BONUS_ICE:
  313. return "ice";
  314. case BONUS_AIR:
  315. return "air";
  316. case BONUS_EARTH:
  317. return "earth";
  318. }
  319. }
  320. void
  321. PlayerStatus::add_player()
  322. {
  323. m_num_players++;
  324. bonus.resize(m_num_players, BONUS_NONE);
  325. m_item_pockets.resize(m_num_players, BONUS_NONE);
  326. }
  327. void
  328. PlayerStatus::remove_player(int player_id)
  329. {
  330. m_num_players--;
  331. for (int i = player_id; i < m_num_players; i++)
  332. {
  333. bonus[i] = bonus[i + 1];
  334. m_item_pockets[i] = m_item_pockets[i + 1];
  335. }
  336. bonus.resize(m_num_players, BONUS_NONE);
  337. m_item_pockets.resize(m_num_players, BONUS_NONE);
  338. }
  339. PlayerStatus::PocketPowerUp::PocketPowerUp(BonusType bonustype, Vector pos):
  340. PowerUp(pos, PowerUp::get_type_from_bonustype(bonustype)),
  341. m_cooldown_timer(),
  342. m_blink_timer(),
  343. m_visible(true)
  344. {
  345. physic.set_velocity_y(-325.f);
  346. physic.set_gravity_modifier(0.4f);
  347. set_layer(LAYER_FOREGROUND1);
  348. m_col.m_group = COLGROUP_DISABLED;
  349. }
  350. void
  351. PlayerStatus::PocketPowerUp::update(float dt_sec)
  352. {
  353. PowerUp::update(dt_sec);
  354. bool check = m_cooldown_timer.check();
  355. if (!m_cooldown_timer.started() && !check && m_col.m_group != COLGROUP_TOUCHABLE)
  356. {
  357. m_cooldown_timer.start(1.3f);
  358. m_blink_timer.start(.15f, true);
  359. }
  360. if (check)
  361. {
  362. m_visible = true;
  363. m_blink_timer.stop();
  364. m_col.m_group = COLGROUP_TOUCHABLE;
  365. }
  366. if (m_blink_timer.check())
  367. m_visible = !m_visible;
  368. }
  369. void
  370. PlayerStatus::PocketPowerUp::draw(DrawingContext& context)
  371. {
  372. if (!m_visible)
  373. return;
  374. PowerUp::draw(context);
  375. }
  376. /* EOF */