player.cpp 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976
  1. // SuperTux
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. #include "object/player.hpp"
  18. #include "audio/sound_manager.hpp"
  19. #include "badguy/badguy.hpp"
  20. #include "control/codecontroller.hpp"
  21. #include "control/input_manager.hpp"
  22. #include "editor/editor.hpp"
  23. #include "math/util.hpp"
  24. #include "math/random.hpp"
  25. #include "object/brick.hpp"
  26. #include "object/bullet.hpp"
  27. #include "object/camera.hpp"
  28. #include "object/display_effect.hpp"
  29. #include "object/falling_coin.hpp"
  30. #include "object/key.hpp"
  31. #include "object/music_object.hpp"
  32. #include "object/particles.hpp"
  33. #include "object/portable.hpp"
  34. #include "object/sprite_particle.hpp"
  35. #include "sprite/sprite.hpp"
  36. #include "sprite/sprite_manager.hpp"
  37. #include "supertux/game_session.hpp"
  38. #include "supertux/gameconfig.hpp"
  39. #include "supertux/resources.hpp"
  40. #include "supertux/sector.hpp"
  41. #include "supertux/tile.hpp"
  42. #include "trigger/climbable.hpp"
  43. #include "trigger/trigger_base.hpp"
  44. #include "video/surface.hpp"
  45. #define SWIMMING
  46. const float TUX_INVINCIBLE_TIME_WARNING = 2.0f;
  47. namespace {
  48. /* Times: */
  49. const float TUX_SAFE_TIME = 1.8f;
  50. const float TUX_INVINCIBLE_TIME = 14.0f;
  51. const float TUX_BACKFLIP_TIME = 2.1f; // minimum air time that backflip results in a loss of control
  52. const int TIME_UNTIL_IDLE = 5000;
  53. /** idle stages */
  54. const std::vector<std::string> IDLE_STAGES
  55. ({
  56. "stand",
  57. "scratch",
  58. "idle"
  59. });
  60. /** acceleration in horizontal direction when walking
  61. * (all accelerations are in pixel/s^2) */
  62. const float WALK_ACCELERATION_X = 300;
  63. /** acceleration in horizontal direction when running */
  64. const float RUN_ACCELERATION_X = 400;
  65. /** acceleration when skidding */
  66. const float SKID_XM = 200;
  67. /** time of skidding in seconds */
  68. const float SKID_TIME = .3f;
  69. /** maximum walk velocity (pixel/s) */
  70. const float MAX_WALK_XM = 230;
  71. /** maximum run velocity (pixel/s) */
  72. const float MAX_RUN_XM = 320;
  73. /** bonus run velocity addition (pixel/s) */
  74. const float BONUS_RUN_XM = 80;
  75. /** maximum horizontal climb velocity */
  76. const float MAX_CLIMB_XM = 96;
  77. /** maximum vertical climb velocity */
  78. const float MAX_CLIMB_YM = 128;
  79. /** maximum vertical glide velocity */
  80. const float MAX_GLIDE_YM = 128;
  81. /** sliding down walls velocity */
  82. const float MAX_WALLCLING_YM = 64;
  83. /** instant velocity when tux starts to walk */
  84. const float WALK_SPEED = 100;
  85. /** rate at which m_boost decreases */
  86. const float BOOST_DECREASE_RATE = 500;
  87. /** rate at which the speed decreases if going above maximum */
  88. const float OVERSPEED_DECELERATION = 100;
  89. /** multiplied by WALK_ACCELERATION to give friction */
  90. const float NORMAL_FRICTION_MULTIPLIER = 1.5f;
  91. /** multiplied by WALK_ACCELERATION to give friction */
  92. const float ICE_FRICTION_MULTIPLIER = 0.1f;
  93. const float ICE_ACCELERATION_MULTIPLIER = 0.25f;
  94. /** time of the kick (kicking mriceblock) animation */
  95. const float KICK_TIME = .3f;
  96. /** if Tux cannot unduck for this long, he will get hurt */
  97. const float UNDUCK_HURT_TIME = 0.25f;
  98. /** gravity is higher after the jump key is released before
  99. the apex of the jump is reached */
  100. const float JUMP_EARLY_APEX_FACTOR = 3.0;
  101. const float JUMP_GRACE_TIME = 0.25f; /**< time before hitting the ground that the jump button may be pressed (and still trigger a jump) */
  102. const float COYOTE_TIME = 0.1f; /**< time between the moment leaving a platform without jumping and being able to jump anyways despite being in the air */
  103. const float MAX_SLIDE_SPEED = 700.f; /**< Max speed for sliding */
  104. const float MAX_FALL_SLIDE_SPEED = 475.f; /**< Max slide speed that Tux can get from falling */
  105. const float DOWN_SLIDE_ACCEL = 1000.f; /** < Acceleration for sliding DOWN slopes */
  106. const float UP_SLIDE_ACCEL = 1100.f; /**< Acceleration for sliding UP slopes */
  107. const float MAX_SLIDE_ROTATING_TIME = 0.15f;
  108. const float MIN_SLIDE_ROTATING_TIME = 0.075f;
  109. /* Tux's collision rectangle */
  110. const float TUX_WIDTH = 31.8f;
  111. const float RUNNING_TUX_WIDTH = 34;
  112. const float SMALL_TUX_HEIGHT = 30.8f;
  113. const float BIG_TUX_HEIGHT = 62.8f;
  114. const float DUCKED_TUX_HEIGHT = 31.8f;
  115. /* Stone Tux variables */
  116. const float MAX_STONE_SPEED = 500.f;
  117. const float STONE_KEY_ACCELERATION = 200.f;
  118. const float STONE_DOWN_ACCELERATION = 300.f;
  119. const float STONE_UP_ACCELERATION = 400.f;
  120. /* Swim variables */
  121. const float SWIM_SPEED = 300.f;
  122. const float SWIM_BOOST_SPEED = 600.f;
  123. const float SWIM_TO_BOOST_ACCEL = 15.f;
  124. const float TURN_MAGNITUDE = 0.15f;
  125. const float TURN_MAGNITUDE_BOOST = 0.2f;
  126. /* Buttjump variables */
  127. const float BUTTJUMP_WAIT_TIME = 0.2f; // the length of time that the buttjump action is being played
  128. const float BUTTJUMP_SPEED = 800.f;
  129. } // namespace
  130. Player::Player(PlayerStatus& player_status, const std::string& name_, int player_id) :
  131. ExposedObject<Player, scripting::Player>(this),
  132. m_id(player_id),
  133. m_target(nullptr),
  134. m_deactivated(false),
  135. m_controller(&InputManager::current()->get_controller(player_id)),
  136. m_scripting_controller(new CodeController()),
  137. m_player_status(player_status),
  138. m_duck(false),
  139. m_crawl(false),
  140. m_dead(false),
  141. m_dying(false),
  142. m_winning(false),
  143. m_backflipping(false),
  144. m_backflip_direction(0),
  145. m_peekingX(Direction::AUTO),
  146. m_peekingY(Direction::AUTO),
  147. m_stone(false),
  148. m_sliding(false),
  149. m_slidejumping(false),
  150. m_swimming(false),
  151. m_swimboosting(false),
  152. m_no_water(true),
  153. m_on_left_wall(false),
  154. m_on_right_wall(false),
  155. m_in_walljump_tile(false),
  156. m_can_walljump(false),
  157. m_boost(0.f),
  158. m_speedlimit(0), //no special limit
  159. m_velocity_override(),
  160. m_scripting_controller_old(nullptr),
  161. m_jump_early_apex(false),
  162. m_on_ice(false),
  163. m_ice_this_frame(false),
  164. //m_santahatsprite(SpriteManager::current()->create("images/creatures/tux/santahat.sprite")),
  165. m_multiplayer_arrow(SpriteManager::current()->create("images/engine/hud/arrowdown.png")),
  166. m_tag_timer(),
  167. m_tag_fade(nullptr),
  168. m_tag_alpha(1.f),
  169. m_has_moved(false),
  170. m_dir(Direction::RIGHT),
  171. m_old_dir(m_dir),
  172. m_last_ground_y(0),
  173. m_fall_mode(ON_GROUND),
  174. m_on_ground_flag(false),
  175. m_jumping(false),
  176. m_can_jump(true),
  177. m_jump_button_timer(),
  178. m_coyote_timer(),
  179. m_wants_buttjump(false),
  180. m_buttjump_stomp(false),
  181. m_does_buttjump(false),
  182. m_invincible_timer(),
  183. m_skidding_timer(),
  184. m_safe_timer(),
  185. m_is_intentionally_safe(false),
  186. m_kick_timer(),
  187. m_buttjump_timer(),
  188. m_dying_timer(),
  189. m_second_growup_sound_timer(),
  190. m_growing(false),
  191. m_backflip_timer(),
  192. m_physic(),
  193. m_visible(true),
  194. m_grabbed_object(nullptr),
  195. m_grabbed_object_remove_listener(new GrabListener(*this)),
  196. m_released_object(false),
  197. // if/when we have complete penny gfx, we can
  198. // load those instead of Tux's sprite in the
  199. // constructor
  200. m_sprite(SpriteManager::current()->create("images/creatures/tux/tux.sprite")),
  201. m_swimming_angle(0),
  202. m_swimming_accel_modifier(100.f),
  203. m_water_jump(false),
  204. m_airarrow(Surface::from_file("images/engine/hud/airarrow.png")),
  205. m_floor_normal(0.0f, 0.0f),
  206. m_ghost_mode(false),
  207. m_unduck_hurt_timer(),
  208. m_idle_timer(),
  209. m_idle_stage(0),
  210. m_climbing(nullptr),
  211. m_ending_direction(0),
  212. m_collected_keys(),
  213. m_last_sliding_angle(0.0f),
  214. m_current_sliding_angle(0.0f),
  215. m_target_sliding_angle(0.0f),
  216. m_sliding_rotation_timer(),
  217. m_is_slidejump_falling(false),
  218. m_was_crawling_before_slide(false)
  219. {
  220. m_name = name_;
  221. m_idle_timer.start(static_cast<float>(TIME_UNTIL_IDLE) / 1000.0f);
  222. SoundManager::current()->preload("sounds/bigjump.wav");
  223. SoundManager::current()->preload("sounds/brick.wav");
  224. SoundManager::current()->preload("sounds/jump.wav");
  225. SoundManager::current()->preload("sounds/hurt.wav");
  226. SoundManager::current()->preload("sounds/kill.wav");
  227. SoundManager::current()->preload("sounds/skid.wav");
  228. SoundManager::current()->preload("sounds/flip.wav");
  229. SoundManager::current()->preload("sounds/invincible_start.ogg");
  230. SoundManager::current()->preload("sounds/splash.wav");
  231. SoundManager::current()->preload("sounds/grow.wav");
  232. m_col.set_size(TUX_WIDTH, is_big() ? BIG_TUX_HEIGHT : SMALL_TUX_HEIGHT);
  233. m_sprite->set_angle(0.0f);
  234. //m_santahatsprite->set_angle(0.0f);
  235. m_physic.reset();
  236. }
  237. Player::~Player()
  238. {
  239. ungrab_object();
  240. if (m_climbing) stop_climbing(*m_climbing);
  241. }
  242. float
  243. Player::get_speedlimit() const
  244. {
  245. return m_speedlimit;
  246. }
  247. void
  248. Player::set_speedlimit(float newlimit)
  249. {
  250. m_speedlimit = newlimit;
  251. }
  252. void
  253. Player::set_id(int id)
  254. {
  255. m_id = id;
  256. m_controller = &(InputManager::current()->get_controller(id));
  257. }
  258. void
  259. Player::set_controller(const Controller* controller_)
  260. {
  261. m_controller = controller_;
  262. }
  263. void
  264. Player::set_winning()
  265. {
  266. if (!is_winning()) {
  267. m_winning = true;
  268. m_invincible_timer.start(10000.0f);
  269. }
  270. }
  271. void
  272. Player::use_scripting_controller(bool use_or_release)
  273. {
  274. if ((use_or_release == true) && (m_controller != m_scripting_controller.get())) {
  275. m_scripting_controller_old = &get_controller();
  276. set_controller(m_scripting_controller.get());
  277. }
  278. if ((use_or_release == false) && (m_controller == m_scripting_controller.get())) {
  279. set_controller(m_scripting_controller_old);
  280. m_scripting_controller_old = nullptr;
  281. }
  282. }
  283. void
  284. Player::do_scripting_controller(const std::string& control_text, bool pressed)
  285. {
  286. if (const auto maybe_control = Control_from_string(control_text)) {
  287. m_scripting_controller->press(*maybe_control, pressed);
  288. }
  289. }
  290. void
  291. Player::move_to_sector(Sector& other)
  292. {
  293. stop_climbing(*m_climbing);
  294. if (m_grabbed_object)
  295. {
  296. auto grabbed_game_object = dynamic_cast<GameObject*>(m_grabbed_object);
  297. if (grabbed_game_object)
  298. get_parent()->move_object(grabbed_game_object->get_uid(), other);
  299. }
  300. for (Key* key : m_collected_keys)
  301. get_parent()->move_object(key->get_uid(), other);
  302. // Move the player.
  303. get_parent()->move_object(get_uid(), other);
  304. }
  305. bool
  306. Player::adjust_height(float new_height, float bottom_offset)
  307. {
  308. Rectf bbox2 = m_col.m_bbox;
  309. bbox2.move(Vector(0, m_col.m_bbox.get_height() - new_height - bottom_offset));
  310. bbox2.set_height(new_height);
  311. if (new_height > m_col.m_bbox.get_height()) {
  312. //Rectf additional_space = bbox2;
  313. //additional_space.set_height(new_height - m_col.m_bbox.get_height());
  314. if (!Sector::get().is_free_of_statics(bbox2, this, true))
  315. return false;
  316. }
  317. // adjust bbox accordingly
  318. // note that we use members of moving_object for this, so we can run this during CD, too
  319. set_pos(bbox2.p1());
  320. m_col.set_size(bbox2.get_width(), bbox2.get_height());
  321. return true;
  322. }
  323. void
  324. Player::trigger_sequence(const std::string& sequence_name, const SequenceData* data)
  325. {
  326. trigger_sequence(string_to_sequence(sequence_name), data);
  327. }
  328. void
  329. Player::trigger_sequence(Sequence seq, const SequenceData* data)
  330. {
  331. if (m_climbing) stop_climbing(*m_climbing);
  332. stop_backflipping();
  333. GameSession::current()->start_sequence(this, seq, data);
  334. }
  335. void
  336. Player::update(float dt_sec)
  337. {
  338. if (is_dead() || Sector::get().get_object_count<Player>() == 1)
  339. {
  340. m_tag_timer.stop();
  341. m_tag_fade = nullptr;
  342. m_tag_alpha = 0.f;
  343. m_has_moved = true;
  344. }
  345. if (m_tag_timer.check())
  346. {
  347. m_tag_timer.stop();
  348. m_tag_fade = std::make_unique<FadeHelper>(1.f, 0.f, 1.f);
  349. }
  350. if (m_tag_fade)
  351. {
  352. m_tag_alpha = m_tag_fade->update(dt_sec);
  353. if (m_tag_fade->completed())
  354. {
  355. m_tag_fade = nullptr;
  356. }
  357. }
  358. // Skip if in multiplayer respawn
  359. if (is_dead() && m_target && Sector::get().get_object_count<Player>([this](const Player& p) { return !p.is_dead() && !p.is_dying() && !p.is_winning() && &p != this; }))
  360. {
  361. auto* target = Sector::get().get_object_by_uid<Player>(*m_target);
  362. if (!target || target->is_dying() || target->is_dead() || target->is_winning())
  363. {
  364. next_target();
  365. }
  366. // Respawn input is handled outside handle_input() because it happens while the player is dead
  367. if (is_dead() && m_target)
  368. {
  369. if (m_controller->pressed(Control::ACTION))
  370. {
  371. multiplayer_respawn();
  372. }
  373. else if (m_controller->pressed(Control::LEFT))
  374. {
  375. prev_target();
  376. }
  377. else if (m_controller->pressed(Control::RIGHT))
  378. {
  379. next_target();
  380. }
  381. }
  382. return;
  383. }
  384. check_bounds();
  385. //catch-all for other circumstances in which Tux's hitbox can't be properly adjusted
  386. if (is_big() &&
  387. !m_duck && !m_swimming && !m_water_jump && !m_backflipping && !m_sliding && !m_stone &&
  388. !adjust_height(BIG_TUX_HEIGHT))
  389. {
  390. //Force Tux's box up a little in order to not phase into floor
  391. adjust_height(BIG_TUX_HEIGHT, 10.f);
  392. }
  393. if (m_velocity_override && glm::length(m_physic.get_velocity()) < SWIM_BOOST_SPEED) {
  394. m_velocity_override = false;
  395. }
  396. //handling of swimming
  397. #ifdef SWIMMING
  398. if (!m_ghost_mode)
  399. {
  400. if (m_no_water)
  401. {
  402. if (m_swimming)
  403. {
  404. m_water_jump = true;
  405. if (m_physic.get_velocity_y() > -350.f && m_controller->hold(Control::UP))
  406. m_physic.set_velocity_y(-350.f);
  407. }
  408. m_swimming = false;
  409. }
  410. if ((on_ground() || m_climbing || m_does_buttjump) && m_water_jump)
  411. {
  412. if (is_big() && !m_stone && !adjust_height(BIG_TUX_HEIGHT))
  413. {
  414. //Force Tux's box up a little in order to not phase into floor
  415. adjust_height(BIG_TUX_HEIGHT, 10.f);
  416. do_duck();
  417. }
  418. else if (!is_big() || m_stone)
  419. {
  420. adjust_height(SMALL_TUX_HEIGHT);
  421. }
  422. m_dir = (m_physic.get_velocity_x() >= 0.f) ? Direction::RIGHT : Direction::LEFT;
  423. m_water_jump = false;
  424. m_swimboosting = false;
  425. //m_santahatsprite->set_angle(0.f);
  426. }
  427. m_no_water = true;
  428. if ((m_swimming || m_water_jump) && is_big())
  429. {
  430. m_col.set_size(TUX_WIDTH, TUX_WIDTH);
  431. adjust_height(TUX_WIDTH);
  432. }
  433. Rectf swim_here_box = get_bbox();
  434. swim_here_box.set_bottom(m_col.m_bbox.get_bottom() - 16.f);
  435. bool can_swim_here = !Sector::get().is_free_of_tiles(swim_here_box, true, Tile::WATER);
  436. if (m_swimming)
  437. {
  438. if (can_swim_here)
  439. {
  440. m_no_water = false;
  441. }
  442. else
  443. {
  444. m_swimming = false;
  445. m_water_jump = true;
  446. if (m_physic.get_velocity_y() > -350.f && m_controller->hold(Control::UP))
  447. m_physic.set_velocity_y(-350.f);
  448. }
  449. }
  450. else
  451. {
  452. if (can_swim_here && !m_climbing)
  453. {
  454. if (m_stone)
  455. stop_rolling();
  456. m_sliding = false;
  457. m_slidejumping = false;
  458. m_was_crawling_before_slide = false;
  459. m_no_water = false;
  460. m_water_jump = false;
  461. m_swimming = true;
  462. m_swimming_angle = math::angle(Vector(m_physic.get_velocity_x(), m_physic.get_velocity_y()));
  463. if (is_big())
  464. adjust_height(TUX_WIDTH);
  465. m_wants_buttjump = m_does_buttjump = m_backflipping = false;
  466. m_dir = (m_physic.get_velocity_x() > 0) ? Direction::LEFT : Direction::RIGHT;
  467. SoundManager::current()->play("sounds/splash.wav", get_pos());
  468. }
  469. }
  470. }
  471. #endif
  472. //end of swimming handling
  473. if (m_dying && m_dying_timer.check()) {
  474. set_bonus(NO_BONUS, true);
  475. m_dead = true;
  476. if (!Sector::get().get_object_count<Player>([](const Player& p) { return !p.is_dead() && !p.is_dying(); }))
  477. {
  478. Sector::get().stop_looping_sounds();
  479. }
  480. else
  481. {
  482. next_target();
  483. }
  484. return;
  485. }
  486. if (!m_dying && !m_deactivated)
  487. handle_input();
  488. /*
  489. // handle_input() calls apply_friction() when Tux is not walking, so we'll have to do this ourselves
  490. if (deactivated)
  491. apply_friction();
  492. */
  493. // extend/shrink tux collision rectangle so that we fall through/walk over 1
  494. // tile holes
  495. //wallclinging and walljumping
  496. Rectf wallclingleft = get_bbox();
  497. wallclingleft.set_left(wallclingleft.get_left() - 8.f);
  498. m_on_left_wall = !Sector::get().is_free_of_statics(wallclingleft);
  499. Rectf wallclingright = get_bbox();
  500. wallclingright.set_right(wallclingright.get_right() + 8.f);
  501. m_on_right_wall = !Sector::get().is_free_of_statics(wallclingright);
  502. m_can_walljump = ((m_on_right_wall || m_on_left_wall) && !on_ground() && !m_swimming && m_in_walljump_tile && !m_stone);
  503. if (m_can_walljump && (m_controller->hold(Control::LEFT) || m_controller->hold(Control::RIGHT)) && m_physic.get_velocity_y() >= 0.f && !m_controller->pressed(Control::JUMP))
  504. {
  505. m_physic.set_velocity_y(MAX_WALLCLING_YM);
  506. m_physic.set_acceleration_y(0);
  507. if (m_water_jump)
  508. {
  509. adjust_height(is_big() ? BIG_TUX_HEIGHT : SMALL_TUX_HEIGHT);
  510. m_water_jump = false;
  511. m_swimboosting = false;
  512. }
  513. if (m_sliding)
  514. {
  515. adjust_height(is_big() ? BIG_TUX_HEIGHT : SMALL_TUX_HEIGHT);
  516. m_sliding = false;
  517. m_slidejumping = false;
  518. }
  519. //m_santahatsprite->set_angle(0.f);
  520. }
  521. m_in_walljump_tile = false;
  522. //End of wallclinging
  523. // Roll the sprite if Tux is rolling
  524. if (m_stone)
  525. {
  526. float f = 1.f;
  527. if (!std::isnan(m_floor_normal.x))
  528. f = std::cos(m_floor_normal.x);
  529. m_sprite->set_angle(m_sprite->get_angle() + m_physic.get_movement(dt_sec).x * 3.141592653898f / 2.f / f);
  530. }
  531. // extend/shrink tux collision rectangle so that we fall through/walk over 1
  532. // tile holes
  533. if (fabsf(m_physic.get_velocity_x()) > MAX_WALK_XM) {
  534. m_col.set_width(RUNNING_TUX_WIDTH);
  535. }
  536. else {
  537. m_col.set_width(TUX_WIDTH);
  538. }
  539. // on downward slopes, adjust vertical velocity so tux walks smoothly down
  540. if (on_ground() && !m_swimming && !m_dying) {
  541. if (m_floor_normal.y != 0) {
  542. if ((m_floor_normal.x * m_physic.get_velocity_x()) >= 0) {
  543. m_physic.set_velocity_y((std::abs(m_physic.get_velocity_x()) * std::abs(m_floor_normal.x)) + 100.f);
  544. }
  545. }
  546. }
  547. // handle backflipping
  548. if (m_backflipping && !m_dying) {
  549. //prevent player from changing direction when backflipping
  550. m_dir = (m_backflip_direction == 1) ? Direction::LEFT : Direction::RIGHT;
  551. if (m_backflip_timer.started()) m_physic.set_velocity_x(100.0f * static_cast<float>(m_backflip_direction));
  552. }
  553. if (on_ground()) {
  554. m_coyote_timer.start(COYOTE_TIME * ((m_sliding || m_stone) ? 2.f : 1.f));
  555. }
  556. // set fall mode...
  557. if (on_ground()) {
  558. m_fall_mode = ON_GROUND;
  559. m_last_ground_y = get_pos().y;
  560. }
  561. else {
  562. if (get_pos().y > m_last_ground_y)
  563. m_fall_mode = FALLING;
  564. else if (m_fall_mode == ON_GROUND)
  565. m_fall_mode = JUMPING;
  566. }
  567. // check if we landed
  568. if (on_ground()) {
  569. m_jumping = false;
  570. if (m_backflipping && (m_backflip_timer.get_timegone() > 0.15f)) {
  571. m_backflipping = false;
  572. m_backflip_direction = 0;
  573. m_physic.set_velocity_x(0);
  574. if (!m_stone && !m_sliding) {
  575. m_sprite->set_angle(0.0f);
  576. //m_santahatsprite->set_angle(0.0f);
  577. }
  578. // if controls are currently deactivated, we take care of standing up ourselves
  579. if (m_deactivated)
  580. do_standup(false);
  581. }
  582. }
  583. if (m_second_growup_sound_timer.check())
  584. {
  585. SoundManager::current()->play("sounds/grow.wav", get_pos());
  586. m_second_growup_sound_timer.stop();
  587. }
  588. if (m_boost != 0.f)
  589. {
  590. bool sign = std::signbit(m_boost);
  591. m_boost = (sign ? -1.f : +1.f) * (std::abs(m_boost) - dt_sec * BOOST_DECREASE_RATE);
  592. if (std::signbit(m_boost) != sign)
  593. m_boost = 0.f;
  594. }
  595. // calculate movement for this frame
  596. m_col.set_movement(m_physic.get_movement(dt_sec) + Vector(m_boost * dt_sec, 0));
  597. if (m_grabbed_object != nullptr && !m_dying)
  598. {
  599. position_grabbed_object();
  600. }
  601. if (m_dying)
  602. ungrab_object();
  603. if (!m_ice_this_frame && on_ground())
  604. m_on_ice = false;
  605. // when invincible, spawn particles
  606. if (m_invincible_timer.started())
  607. {
  608. if (graphicsRandom.rand(0, 2) == 0)
  609. {
  610. float px = graphicsRandom.randf(m_col.m_bbox.get_left() + 0, m_col.m_bbox.get_right() - 0);
  611. float py = graphicsRandom.randf(m_col.m_bbox.get_top() + 0, m_col.m_bbox.get_bottom() - 0);
  612. Vector ppos = Vector(px, py);
  613. Vector pspeed = Vector(0, 0);
  614. Vector paccel = Vector(0, 0);
  615. Sector::get().add<SpriteParticle>(
  616. "images/particles/sparkle.sprite",
  617. // draw bright sparkle when there is lots of time left,
  618. // dark sparkle when invincibility is about to end
  619. (m_invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING) ?
  620. // make every other a longer sparkle to make trail a bit fuzzy
  621. (size_t(g_game_time * 20) % 2) ? "small" : "medium"
  622. :
  623. "dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS + 1 + 5);
  624. }
  625. }
  626. if (m_growing) {
  627. if (m_sprite->animation_done()) m_growing = false;
  628. }
  629. // when climbing animate only while moving
  630. if (m_climbing) {
  631. if ((m_physic.get_velocity_x() == 0) && (m_physic.get_velocity_y() == 0))
  632. {
  633. m_sprite->pause_animation();
  634. //m_santahatsprite->pause_animation();
  635. }
  636. else if (!m_growing)
  637. {
  638. m_sprite->resume_animation();
  639. //m_santahatsprite->resume_animation();
  640. }
  641. }
  642. if (m_floor_normal.y != 0.f && m_crawl)
  643. {
  644. m_crawl = false;
  645. m_sliding = true;
  646. m_was_crawling_before_slide = true;
  647. }
  648. //sliding
  649. if (m_sliding)
  650. {
  651. float sliding_angle = 0.0f;
  652. if (on_ground())
  653. {
  654. float floor_angle = 0.0f;
  655. if (m_floor_normal.y != 0.0f)
  656. {
  657. floor_angle = math::degrees(math::angle(m_floor_normal)) + 90.0f;
  658. }
  659. if (m_target_sliding_angle != floor_angle)
  660. {
  661. const float current_velocity = glm::length(m_physic.get_velocity());
  662. constexpr float max_velocity = 500.0f;
  663. const float rotation_time = MIN_SLIDE_ROTATING_TIME + (1.0f - (std::min(max_velocity, current_velocity) / max_velocity)) * (MAX_SLIDE_ROTATING_TIME - MIN_SLIDE_ROTATING_TIME);
  664. m_last_sliding_angle = m_current_sliding_angle;
  665. m_target_sliding_angle = floor_angle;
  666. m_sliding_rotation_timer.start(rotation_time);
  667. }
  668. if (m_sliding_rotation_timer.started())
  669. {
  670. const float progress = m_sliding_rotation_timer.get_timegone() / m_sliding_rotation_timer.get_period();
  671. const float angle_difference = m_target_sliding_angle - m_last_sliding_angle;
  672. sliding_angle = m_current_sliding_angle = m_last_sliding_angle + (angle_difference * progress);
  673. }
  674. else
  675. {
  676. sliding_angle = m_last_sliding_angle = floor_angle;
  677. }
  678. }
  679. else
  680. {
  681. if (!m_jumping && !m_is_slidejump_falling)
  682. {
  683. sliding_angle = math::degrees(math::angle(m_physic.get_velocity()));
  684. if (m_physic.get_velocity_x() < 0.0f)
  685. {
  686. sliding_angle -= 180.0f;
  687. }
  688. m_target_sliding_angle = m_current_sliding_angle = sliding_angle;
  689. }
  690. else
  691. {
  692. // Do not rotate while slidejump animation is displayed
  693. sliding_angle = 0.0f;
  694. }
  695. }
  696. m_sprite->set_angle(sliding_angle);
  697. //if you stop holding down when sliding, then it stops.
  698. //or, stop sliding if you come to a stop and are not on a slope.
  699. if (!m_controller->hold(Control::DOWN) ||
  700. (m_floor_normal.y == 0.f && std::abs(m_physic.get_velocity_x()) <= 1.f))
  701. {
  702. if (is_big())
  703. {
  704. if (m_controller->hold(Control::LEFT) || m_controller->hold(Control::RIGHT)) {
  705. m_crawl = true;
  706. }
  707. m_duck = true;
  708. }
  709. m_sliding = false;
  710. m_slidejumping = false;
  711. m_was_crawling_before_slide = false;
  712. }
  713. }
  714. if (m_sliding || m_stone)
  715. {
  716. Rectf sidebrickbox = get_bbox().grown(-1.f);
  717. sidebrickbox.set_left(get_bbox().get_left() + (m_dir == Direction::LEFT ? -12.f : 1.f));
  718. sidebrickbox.set_right(get_bbox().get_right() + (m_dir == Direction::RIGHT ? 12.f : -1.f));
  719. for (auto& brick : Sector::get().get_objects_by_type<Brick>()) {
  720. if (sidebrickbox.overlaps(brick.get_bbox()) && (m_stone || (m_sliding && brick.get_class_name() != "heavy-brick")) &&
  721. std::abs(m_physic.get_velocity_x()) >= 150.f) {
  722. brick.try_break(this, is_big());
  723. }
  724. }
  725. }
  726. if (m_does_buttjump || (m_stone && m_physic.get_velocity_y() > 30.f && !m_coyote_timer.started()))
  727. {
  728. Rectf downbox = get_bbox().grown(-1.f);
  729. downbox.set_top(get_bbox().get_bottom());
  730. downbox.set_bottom(downbox.get_bottom() + 16.f);
  731. for (auto& brick : Sector::get().get_objects_by_type<Brick>()) {
  732. // stoneform breaks through any kind of bricks
  733. if (downbox.overlaps(brick.get_bbox()) && (m_stone || !dynamic_cast<HeavyBrick*>(&brick)))
  734. brick.try_break(this, is_big());
  735. }
  736. for (auto& badguy : Sector::get().get_objects_by_type<BadGuy>()) {
  737. if (downbox.overlaps(badguy.get_bbox()) && badguy.is_snipable() && !badguy.is_grabbed())
  738. badguy.kill_fall();
  739. }
  740. }
  741. // break bricks above without stopping
  742. if (m_stone && m_physic.get_velocity_y() < 30.f)
  743. {
  744. Rectf topbox = get_bbox().grown(-1.f);
  745. topbox.set_top(get_bbox().get_top() - 16.f);
  746. for (auto& brick : Sector::get().get_objects_by_type<Brick>()) {
  747. if (topbox.overlaps(brick.get_bbox()))
  748. brick.try_break(this, is_big());
  749. }
  750. }
  751. //launch from slopes
  752. Rectf launchbox = get_bbox();
  753. launchbox.set_bottom(get_bbox().get_bottom() - 8.f);
  754. launchbox.set_left(get_bbox().get_left() + (m_dir == Direction::LEFT ? -32.f : 33.f));
  755. launchbox.set_right(get_bbox().get_right() + (m_dir == Direction::RIGHT ? 32.f : -33.f));
  756. if (m_sliding && on_ground() && m_floor_normal.y != 0 && m_floor_normal.x * m_physic.get_velocity_x() < 0.f &&
  757. Sector::get().is_free_of_statics(launchbox) && !m_slidejumping)
  758. {
  759. m_slidejumping = true;
  760. m_physic.set_velocity_y(-glm::length(m_physic.get_velocity()) * std::abs(m_floor_normal.x));
  761. }
  762. else if (m_sliding && on_ground()) {
  763. m_slidejumping = false;
  764. }
  765. m_ice_this_frame = false;
  766. m_on_ground_flag = false;
  767. }
  768. void
  769. Player::slide()
  770. {
  771. if (m_swimming || m_water_jump || m_stone)
  772. {
  773. m_sliding = false;
  774. m_was_crawling_before_slide = false;
  775. return;
  776. }
  777. m_sliding = true;
  778. if (m_physic.get_velocity_x() > 0.f) {
  779. m_dir = Direction::RIGHT;
  780. }
  781. else if (m_physic.get_velocity_x() < 0.f) {
  782. m_dir = Direction::LEFT;
  783. }
  784. //pre_slide helps us detect the ground where Tux is about to slide on because sometimes on_ground() doesn't work or isn't relevant
  785. Rectf pre_slide_box = get_bbox();
  786. float fast_fall_speed = m_physic.get_velocity_y() <= 400.f ? 0.f : m_physic.get_velocity_y()*0.03f;
  787. pre_slide_box.set_bottom(m_col.m_bbox.get_bottom() + fast_fall_speed + 16.f);
  788. bool pre_slide = !Sector::get().is_free_of_statics(pre_slide_box);
  789. if (std::abs(m_physic.get_velocity_x()) > MAX_SLIDE_SPEED) {
  790. m_physic.set_acceleration_x(-m_physic.get_velocity_x());
  791. }
  792. else
  793. {
  794. if (!pre_slide) {
  795. m_physic.set_acceleration_x(0.f);
  796. }
  797. else
  798. {
  799. //handle adding acceleration from falling down
  800. if (m_sliding && !on_ground() && m_floor_normal.x*m_physic.get_velocity_x() <= 0.f && m_physic.get_velocity_y() > 0.f)
  801. {
  802. //less max momentum when getting it on flat surfaces
  803. if (std::abs(m_physic.get_velocity_x()) + (std::abs(m_physic.get_velocity_y())*0.2f) <= MAX_FALL_SLIDE_SPEED) {
  804. m_physic.set_velocity_x(m_physic.get_velocity_x() + (std::abs(m_physic.get_velocity_y())*(m_dir == Direction::LEFT ? -0.125f : 0.125f)));
  805. }
  806. else {
  807. m_physic.set_velocity_x(MAX_FALL_SLIDE_SPEED * (m_dir == Direction::LEFT ? -1.f : 1.f));
  808. }
  809. }
  810. //handle adding acceleration from sliding down, removing it from reaching floor or an incline
  811. if (m_floor_normal.y == 0.f && m_can_jump)
  812. {
  813. if (!m_slidejumping && !m_jumping) {
  814. apply_friction();
  815. }
  816. }
  817. else
  818. {
  819. if (m_floor_normal.x > 0.f) {
  820. m_physic.set_acceleration_x((m_dir == Direction::LEFT ? UP_SLIDE_ACCEL : DOWN_SLIDE_ACCEL)*std::abs(m_floor_normal.x));
  821. }
  822. if (m_floor_normal.x < 0.f) {
  823. m_physic.set_acceleration_x((m_dir == Direction::RIGHT ? -UP_SLIDE_ACCEL : -DOWN_SLIDE_ACCEL)*std::abs(m_floor_normal.x));
  824. }
  825. }
  826. }
  827. }
  828. }
  829. void
  830. Player::handle_input_swimming()
  831. {
  832. float pointx = float(m_controller->hold(Control::RIGHT)) - float(m_controller->hold(Control::LEFT)),
  833. pointy = float(m_controller->hold(Control::DOWN)) - float(m_controller->hold(Control::UP));
  834. bool boost = m_controller->hold(Control::JUMP);
  835. swim(pointx, pointy, boost);
  836. }
  837. void
  838. Player::swim(float pointx, float pointy, bool boost)
  839. {
  840. if (m_swimming)
  841. m_physic.set_gravity_modifier(.0f);
  842. // Angle
  843. bool is_ang_defined = (pointx != 0) || (pointy != 0);
  844. float pointed_angle = math::angle(Vector(pointx, pointy));
  845. float delta = 0;
  846. if(is_ang_defined)
  847. {
  848. delta = pointed_angle - m_swimming_angle;
  849. if(std::abs(delta) > math::PI)
  850. delta += delta > 0 ? -math::TAU : math::TAU;
  851. float epsilon = (boost ? TURN_MAGNITUDE : TURN_MAGNITUDE_BOOST) * delta;
  852. m_swimming_angle += epsilon;
  853. if (m_swimming_angle > math::PI)
  854. m_swimming_angle -= math::TAU;
  855. if (m_swimming_angle <= -math::PI)
  856. m_swimming_angle += math::TAU;
  857. }
  858. float vx = m_physic.get_velocity_x();
  859. float vy = m_physic.get_velocity_y();
  860. if (m_swimming && !m_water_jump)
  861. {
  862. if(is_ang_defined && std::abs(delta) < 0.01f)
  863. m_swimming_angle = pointed_angle;
  864. m_swimming_accel_modifier = is_ang_defined ? 600.f : 0.f;
  865. Vector swimming_direction = math::vec2_from_polar(m_swimming_accel_modifier, pointed_angle);
  866. m_physic.set_acceleration_x((swimming_direction.x - 1.0f * vx) * 2.f);
  867. m_physic.set_acceleration_y((swimming_direction.y - 1.0f * vy) * 2.f);
  868. // Limit speed, if you go above this speed your acceleration is set to opposite (?)
  869. if (glm::length(m_physic.get_velocity()) > SWIM_SPEED)
  870. {
  871. m_physic.set_acceleration(-vx,-vy); // Was too lazy to set it properly ~~zwatotem
  872. }
  873. // Natural friction
  874. if (!is_ang_defined)
  875. {
  876. m_physic.set_acceleration(-3.f*vx, -3.f*vy);
  877. }
  878. //not boosting? let's slow this penguin down!!!
  879. if (!boost && is_ang_defined && glm::length(m_physic.get_velocity()) > (SWIM_SPEED + 10.f))
  880. {
  881. m_physic.set_acceleration(-5.f*vx, -5.f*vy);
  882. }
  883. // Snapping to prevent unwanted floating
  884. if (!is_ang_defined && glm::length(Vector(vx,vy)) < 100.f)
  885. {
  886. vx = 0;
  887. vy = 0;
  888. }
  889. // Turbo, using pointsign
  890. float minboostspeed = 100.f;
  891. if (boost && glm::length(m_physic.get_velocity()) > minboostspeed)
  892. {
  893. if (glm::length(m_physic.get_velocity()) < SWIM_BOOST_SPEED)
  894. {
  895. m_swimboosting = true;
  896. if (is_ang_defined)
  897. {
  898. vx += SWIM_TO_BOOST_ACCEL * pointx;
  899. vy += SWIM_TO_BOOST_ACCEL * pointy;
  900. }
  901. }
  902. else
  903. {
  904. //cap on boosting
  905. m_physic.set_acceleration(-vx, -vy);
  906. }
  907. m_physic.set_velocity(vx, vy);
  908. }
  909. else
  910. {
  911. if (glm::length(m_physic.get_velocity()) < (SWIM_SPEED + 10.f))
  912. {
  913. m_swimboosting = false;
  914. }
  915. }
  916. }
  917. if (m_water_jump && !m_swimming)
  918. {
  919. m_swimming_angle = math::angle(Vector(vx, vy));
  920. }
  921. // snap angle dir when water jumping to avoid crazy spinning graphics...
  922. if (m_water_jump && !m_swimming && std::abs(m_physic.get_velocity_x()) < 10.f)
  923. {
  924. m_sprite->set_angle(math::degrees(m_swimming_angle));
  925. //m_santahatsprite->set_angle(math::degrees(m_swimming_angle));
  926. }
  927. else
  928. {
  929. // otherwise angle the sprite normally
  930. float angle = (std::abs(m_swimming_angle) <= math::PI_2) ?
  931. math::degrees(m_swimming_angle) :
  932. math::degrees(math::PI + m_swimming_angle);
  933. m_sprite->set_angle(angle);
  934. //m_santahatsprite->set_angle(angle);
  935. //Force the speed to point in the direction Tux is going unless Tux is being pushed by something else
  936. if (m_swimming && !m_water_jump && boost && m_boost == 0.f && !m_velocity_override)
  937. {
  938. m_physic.set_velocity(math::at_angle(m_physic.get_velocity(), m_swimming_angle));
  939. }
  940. }
  941. }
  942. bool
  943. Player::on_ground() const
  944. {
  945. return m_on_ground_flag;
  946. }
  947. void
  948. Player::set_on_ground(bool flag)
  949. {
  950. m_on_ground_flag = flag;
  951. }
  952. void
  953. Player::apply_friction()
  954. {
  955. bool is_on_ground = on_ground();
  956. float velx = m_physic.get_velocity_x();
  957. if (is_on_ground && (fabsf(velx) < (m_stone ? 5.f : WALK_SPEED))) {
  958. m_physic.set_velocity_x(0);
  959. m_physic.set_acceleration_x(0);
  960. return;
  961. }
  962. float friction = WALK_ACCELERATION_X;
  963. if (m_on_ice && is_on_ground)
  964. //we need this or else sliding on ice will cause Tux to go on for a very long time
  965. friction *= (ICE_FRICTION_MULTIPLIER*(m_sliding ? 4.f : m_stone ? 5.f : 1.f));
  966. else
  967. friction *= (NORMAL_FRICTION_MULTIPLIER*(m_sliding ? 0.8f : m_stone ? 0.4f : 1.f));
  968. if (velx < 0) {
  969. m_physic.set_acceleration_x(friction);
  970. } else if (velx > 0) {
  971. m_physic.set_acceleration_x(-friction);
  972. } // no friction for physic.get_velocity_x() == 0
  973. }
  974. void
  975. Player::handle_horizontal_input()
  976. {
  977. float vx = m_physic.get_velocity_x();
  978. float vy = m_physic.get_velocity_y();
  979. float ax = 0;
  980. float ay = m_physic.get_acceleration_y();
  981. float dirsign = 0;
  982. if (!m_duck || m_physic.get_velocity_y() != 0) {
  983. if (m_controller->hold(Control::LEFT) && !m_controller->hold(Control::RIGHT)) {
  984. m_old_dir = m_dir;
  985. if (!m_water_jump) m_dir = Direction::LEFT;
  986. dirsign = -1;
  987. } else if (!m_controller->hold(Control::LEFT)
  988. && m_controller->hold(Control::RIGHT)) {
  989. m_old_dir = m_dir;
  990. if (!m_water_jump) m_dir = Direction::RIGHT;
  991. dirsign = 1;
  992. }
  993. }
  994. if (m_duck && (m_controller->hold(Control::LEFT) || m_controller->hold(Control::RIGHT))) {
  995. m_crawl = true;
  996. }
  997. if (m_crawl && on_ground() && std::abs(m_physic.get_velocity_x()) < WALK_SPEED)
  998. {
  999. if (m_controller->hold(Control::LEFT) && !m_controller->hold(Control::RIGHT))
  1000. {
  1001. vx = -WALK_SPEED;
  1002. m_dir = Direction::LEFT;
  1003. }
  1004. else if (m_controller->hold(Control::RIGHT) && !m_controller->hold(Control::LEFT))
  1005. {
  1006. vx = WALK_SPEED;
  1007. m_dir = Direction::RIGHT;
  1008. }
  1009. else {
  1010. vx = 0.f;
  1011. }
  1012. }
  1013. // do not run if we're holding something which slows us down
  1014. if ( m_grabbed_object && m_grabbed_object->is_hampering() ) {
  1015. ax = dirsign * WALK_ACCELERATION_X;
  1016. // limit speed
  1017. if (vx >= MAX_WALK_XM && dirsign > 0) {
  1018. ax = std::min(ax, -OVERSPEED_DECELERATION);
  1019. } else if (vx <= -MAX_WALK_XM && dirsign < 0) {
  1020. ax = std::max(ax, OVERSPEED_DECELERATION);
  1021. }
  1022. } else {
  1023. if ( vx * dirsign < MAX_WALK_XM ) {
  1024. ax = dirsign * WALK_ACCELERATION_X;
  1025. } else {
  1026. ax = dirsign * RUN_ACCELERATION_X;
  1027. }
  1028. // limit speed
  1029. if (vx >= MAX_RUN_XM + BONUS_RUN_XM *((get_bonus() == AIR_BONUS) ? 1 : 0)) {
  1030. ax = std::min(ax, -OVERSPEED_DECELERATION);
  1031. } else if (vx <= -MAX_RUN_XM - BONUS_RUN_XM * ((get_bonus() == AIR_BONUS) ? 1 : 0)) {
  1032. ax = std::max(ax, OVERSPEED_DECELERATION);
  1033. }
  1034. }
  1035. // we can reach WALK_SPEED without any acceleration
  1036. if (dirsign != 0 && fabsf(vx) < WALK_SPEED) {
  1037. vx = dirsign * WALK_SPEED;
  1038. }
  1039. //Check speedlimit.
  1040. if ( m_speedlimit > 0 && vx * dirsign >= m_speedlimit ) {
  1041. vx = dirsign * m_speedlimit;
  1042. ax = 0;
  1043. }
  1044. // changing directions?
  1045. if ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0)) {
  1046. if (on_ground()) {
  1047. // let's skid!
  1048. if (fabsf(vx)>SKID_XM && !m_skidding_timer.started()) {
  1049. m_skidding_timer.start(SKID_TIME);
  1050. SoundManager::current()->play("sounds/skid.wav", get_pos());
  1051. // dust some particles
  1052. Sector::get().add<Particles>(
  1053. Vector(m_dir == Direction::LEFT ? m_col.m_bbox.get_right() : m_col.m_bbox.get_left(), m_col.m_bbox.get_bottom()),
  1054. m_dir == Direction::LEFT ? 50 : -70, m_dir == Direction::LEFT ? 70 : -50, 260.0f, 280.0f,
  1055. Vector(0, 300), 3, Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS+1);
  1056. ax *= 2.5f;
  1057. } else {
  1058. ax *= 2;
  1059. }
  1060. }
  1061. else {
  1062. // give Tux tighter air control
  1063. ax *= 2.f;
  1064. }
  1065. }
  1066. if (m_on_ice && on_ground()) {
  1067. ax *= ICE_ACCELERATION_MULTIPLIER;
  1068. }
  1069. if(get_collision_object()->get_pressure() != Vector(0.0f, 0.0f)) {
  1070. vx = 0.0f; vy = 0.0f;
  1071. ax = 0.0f; ay = 0.0f;
  1072. }
  1073. m_physic.set_velocity(vx, vy);
  1074. m_physic.set_acceleration(ax, ay);
  1075. // we get slower when not pressing any keys
  1076. if (dirsign == 0) {
  1077. apply_friction();
  1078. }
  1079. }
  1080. void
  1081. Player::do_cheer()
  1082. {
  1083. do_duck();
  1084. do_backflip();
  1085. do_standup(false);
  1086. }
  1087. void
  1088. Player::do_duck() {
  1089. if (m_duck)
  1090. return;
  1091. if (!is_big())
  1092. return;
  1093. if (m_sliding && Sector::get().is_free_of_statics(Rectf(get_bbox().get_left(), get_bbox().get_top() - 32.f,
  1094. get_bbox().get_right(), get_bbox().get_bottom())))
  1095. return;
  1096. if (!m_swimming && !m_water_jump && m_physic.get_velocity_y() != 0)
  1097. return;
  1098. if (!on_ground())
  1099. return;
  1100. if (m_does_buttjump)
  1101. return;
  1102. if (adjust_height(DUCKED_TUX_HEIGHT)) {
  1103. m_duck = true;
  1104. m_growing = false;
  1105. m_unduck_hurt_timer.stop();
  1106. } else {
  1107. // FIXME: what now?
  1108. }
  1109. }
  1110. void
  1111. Player::do_standup(bool force_standup) {
  1112. if (!m_duck || !is_big() || m_backflipping || m_stone)
  1113. {
  1114. m_crawl = false;
  1115. return;
  1116. }
  1117. Rectf new_bbox = m_col.m_bbox;
  1118. float new_height = m_swimming ? TUX_WIDTH : BIG_TUX_HEIGHT;
  1119. new_bbox.move(Vector(0, m_col.m_bbox.get_height() - new_height));
  1120. new_bbox.set_height(new_height);
  1121. if (!Sector::get().is_free_of_movingstatics(new_bbox, this) && !force_standup)
  1122. {
  1123. m_crawl = true;
  1124. return;
  1125. }
  1126. if (m_swimming ? adjust_height(TUX_WIDTH) : adjust_height(BIG_TUX_HEIGHT)) {
  1127. m_duck = false;
  1128. m_crawl = false;
  1129. m_unduck_hurt_timer.stop();
  1130. } else if (force_standup) {
  1131. // if timer is not already running, start it.
  1132. if (m_unduck_hurt_timer.get_period() == 0) {
  1133. m_unduck_hurt_timer.start(UNDUCK_HURT_TIME);
  1134. }
  1135. else if (m_unduck_hurt_timer.check()) {
  1136. kill(false);
  1137. }
  1138. }
  1139. }
  1140. void
  1141. Player::do_backflip() {
  1142. if (!m_duck)
  1143. return;
  1144. if (m_crawl)
  1145. return;
  1146. if (!on_ground())
  1147. return;
  1148. m_backflip_direction = (m_dir == Direction::LEFT)?(+1):(-1);
  1149. m_backflipping = true;
  1150. do_jump((get_bonus() == AIR_BONUS) ? -720.0f : -580.0f);
  1151. SoundManager::current()->play("sounds/flip.wav", get_pos());
  1152. m_backflip_timer.start(TUX_BACKFLIP_TIME);
  1153. }
  1154. void
  1155. Player::do_jump(float yspeed) {
  1156. if (!m_can_walljump && !m_in_walljump_tile && !on_ground() && !m_coyote_timer.started())
  1157. return;
  1158. // jump only if it would make Tux go faster upwards
  1159. if (m_physic.get_velocity_y() > yspeed) {
  1160. m_physic.set_velocity_y(yspeed * (m_sliding ? 0.75f : 1.f));
  1161. //bbox.move(Vector(0, -1));
  1162. m_jumping = true;
  1163. m_on_ground_flag = false;
  1164. m_can_jump = false;
  1165. /*if (m_sliding)
  1166. {
  1167. if (!adjust_height(is_big() ? BIG_TUX_HEIGHT : SMALL_TUX_HEIGHT)) {
  1168. m_duck = true;
  1169. }
  1170. m_sliding = false;
  1171. m_slidejumping = false;
  1172. }*/
  1173. // play sound
  1174. if (is_big()) {
  1175. SoundManager::current()->play("sounds/bigjump.wav", get_pos());
  1176. } else {
  1177. SoundManager::current()->play("sounds/jump.wav", get_pos());
  1178. }
  1179. }
  1180. }
  1181. void
  1182. Player::early_jump_apex()
  1183. {
  1184. if (!m_jump_early_apex)
  1185. {
  1186. m_jump_early_apex = true;
  1187. m_physic.set_gravity_modifier(JUMP_EARLY_APEX_FACTOR);
  1188. }
  1189. }
  1190. void
  1191. Player::do_jump_apex()
  1192. {
  1193. if (m_jump_early_apex)
  1194. {
  1195. m_jump_early_apex = false;
  1196. m_physic.set_gravity_modifier(1.0f);
  1197. }
  1198. }
  1199. void
  1200. Player::handle_vertical_input()
  1201. {
  1202. // Press jump key
  1203. if (m_controller->pressed(Control::JUMP)) m_jump_button_timer.start(JUMP_GRACE_TIME);
  1204. if (m_controller->hold(Control::JUMP) && m_jump_button_timer.started() && (m_can_jump || m_coyote_timer.started())) {
  1205. m_jump_button_timer.stop();
  1206. if (m_duck) {
  1207. // when running, only jump a little bit; else do a backflip
  1208. if ((m_physic.get_velocity_x() != 0) ||
  1209. (m_controller->hold(Control::LEFT)) ||
  1210. (m_controller->hold(Control::RIGHT)))
  1211. {
  1212. do_jump(-300);
  1213. }
  1214. else
  1215. {
  1216. do_backflip();
  1217. }
  1218. } else {
  1219. // airflower allows for higher jumps-
  1220. // jump a bit higher if we are running; else do a normal jump
  1221. if (get_bonus() == AIR_BONUS)
  1222. do_jump((fabsf(m_physic.get_velocity_x()) > MAX_WALK_XM) ? -620.0f : -580.0f);
  1223. else
  1224. do_jump((fabsf(m_physic.get_velocity_x()) > MAX_WALK_XM) ? -580.0f : -520.0f);
  1225. }
  1226. //Stop the coyote timer only after calling do_jump, because do_jump also checks for the timer
  1227. m_coyote_timer.stop();
  1228. // airflower glide only when holding jump key
  1229. }
  1230. else if (m_controller->hold(Control::JUMP) && get_bonus() == AIR_BONUS && m_physic.get_velocity_y() > MAX_GLIDE_YM) {
  1231. // glide stops if buttjump is initiated
  1232. if (!m_controller->hold(Control::DOWN))
  1233. {
  1234. m_physic.set_velocity_y(MAX_GLIDE_YM);
  1235. m_physic.set_acceleration_y(0);
  1236. }
  1237. }
  1238. // Let go of jump key
  1239. else if (!m_controller->hold(Control::JUMP)) {
  1240. if (!m_backflipping && m_jumping && m_physic.get_velocity_y() < 0) {
  1241. m_jumping = false;
  1242. if (m_sliding)
  1243. {
  1244. m_is_slidejump_falling = true;
  1245. }
  1246. early_jump_apex();
  1247. }
  1248. }
  1249. if (m_jump_early_apex && m_physic.get_velocity_y() >= 0) {
  1250. do_jump_apex();
  1251. }
  1252. /* In case the player has pressed Down while in a certain range of air,
  1253. enable butt jump action */
  1254. if (m_controller->hold(Control::DOWN) && !m_duck && is_big() && !on_ground() && !m_sliding && !m_stone) {
  1255. if (!m_wants_buttjump && !m_does_buttjump) {
  1256. m_buttjump_timer.start(BUTTJUMP_WAIT_TIME);
  1257. }
  1258. m_wants_buttjump = true;
  1259. if (m_buttjump_timer.check())
  1260. {
  1261. m_buttjump_timer.stop();
  1262. m_does_buttjump = true;
  1263. }
  1264. if (m_does_buttjump) {
  1265. m_physic.set_velocity_y(BUTTJUMP_SPEED);
  1266. }
  1267. }
  1268. /* When Down is not held anymore, disable butt jump */
  1269. if (!m_controller->hold(Control::DOWN)) {
  1270. m_wants_buttjump = false;
  1271. m_does_buttjump = false;
  1272. m_buttjump_stomp = false;
  1273. }
  1274. //The real walljumping magic
  1275. if (m_controller->pressed(Control::JUMP) && m_can_walljump && !m_backflipping)
  1276. {
  1277. SoundManager::current()->play((is_big()) ? "sounds/bigjump.wav" : "sounds/jump.wav", get_pos());
  1278. m_physic.set_velocity_x(get_bonus() == AIR_BONUS ?
  1279. m_on_left_wall ? 480.f : -480.f : m_on_left_wall ? 380.f : -380.f);
  1280. do_jump(-520.f);
  1281. }
  1282. m_physic.set_acceleration_y(0);
  1283. }
  1284. void
  1285. Player::handle_input()
  1286. {
  1287. // Display the player's ID on top of them at the beginning of the level/sector
  1288. // and persist the number until the player moves, because players will be
  1289. // stacked upon spawning.
  1290. // It is probably possible to displace the player without touching left or
  1291. // right, but for simplicity, only those can make the player number vanish.
  1292. if (!m_has_moved && (m_controller->hold(Control::LEFT) || m_controller->hold(Control::RIGHT)))
  1293. {
  1294. m_has_moved = true;
  1295. m_tag_timer.start(1.f);
  1296. }
  1297. if (m_ghost_mode) {
  1298. handle_input_ghost();
  1299. return;
  1300. }
  1301. if (m_climbing) {
  1302. handle_input_climbing();
  1303. return;
  1304. }
  1305. if (m_stone) {
  1306. handle_input_rolling();
  1307. return;
  1308. }
  1309. if (m_swimming) {
  1310. handle_input_swimming();
  1311. }
  1312. else
  1313. {
  1314. if (m_water_jump)
  1315. {
  1316. swim(0, 0, 0);
  1317. }
  1318. }
  1319. if (!m_swimming)
  1320. {
  1321. if (!m_water_jump && !m_backflipping && !m_sliding)
  1322. {
  1323. m_sprite->set_angle(0);
  1324. //m_santahatsprite->set_angle(0);
  1325. }
  1326. if (!m_jump_early_apex) {
  1327. m_physic.set_gravity_modifier(1.0f);
  1328. }
  1329. else {
  1330. m_physic.set_gravity_modifier(JUMP_EARLY_APEX_FACTOR);
  1331. }
  1332. }
  1333. /* Peeking */
  1334. if (!m_controller->hold( Control::PEEK_LEFT ) && !m_controller->hold( Control::PEEK_RIGHT))
  1335. m_peekingX = Direction::AUTO;
  1336. if (!m_controller->hold( Control::PEEK_UP ) && !m_controller->hold( Control::PEEK_DOWN))
  1337. m_peekingY = Direction::AUTO;
  1338. if (m_controller->pressed(Control::PEEK_LEFT))
  1339. m_peekingX = Direction::LEFT;
  1340. else if (m_controller->pressed(Control::PEEK_RIGHT))
  1341. m_peekingX = Direction::RIGHT;
  1342. if (m_controller->pressed(Control::PEEK_UP))
  1343. m_peekingY = Direction::UP;
  1344. else if (m_controller->pressed(Control::PEEK_DOWN))
  1345. m_peekingY = Direction::DOWN;
  1346. /* Handle horizontal movement: */
  1347. if (!m_backflipping && !m_stone && !m_swimming && !m_sliding) handle_horizontal_input();
  1348. /* Jump/jumping? */
  1349. if (on_ground())
  1350. m_can_jump = true;
  1351. /* Handle vertical movement: */
  1352. if (!m_stone && !m_swimming) handle_vertical_input();
  1353. /* grabbing */
  1354. bool just_grabbed = try_grab();
  1355. /* Shoot! */
  1356. auto active_bullets = Sector::get().get_object_count<Bullet>([this](const Bullet& b){ return &b.get_player() == this; });
  1357. if (m_controller->pressed(Control::ACTION) && (get_bonus() == FIRE_BONUS || get_bonus() == ICE_BONUS) && !just_grabbed) {
  1358. if ((get_bonus() == FIRE_BONUS &&
  1359. active_bullets < m_player_status.max_fire_bullets[get_id()]) ||
  1360. (get_bonus() == ICE_BONUS &&
  1361. active_bullets < m_player_status.max_ice_bullets[get_id()]))
  1362. {
  1363. Vector pos = get_pos() + Vector(m_col.m_bbox.get_width() / 2.f, m_col.m_bbox.get_height() / 2.f);
  1364. Direction swim_dir;
  1365. swim_dir = ((std::abs(m_swimming_angle) <= math::PI_2)
  1366. || (m_water_jump && std::abs(m_physic.get_velocity_x()) < 10.f)) ? Direction::RIGHT : Direction::LEFT;
  1367. if (m_swimming || m_water_jump)
  1368. {
  1369. m_dir = swim_dir;
  1370. }
  1371. Sector::get().add<Bullet>(pos, (m_swimming || m_water_jump) ?
  1372. m_physic.get_velocity() + (Vector(std::cos(m_swimming_angle), std::sin(m_swimming_angle)) * 600.f) :
  1373. Vector(((m_dir == Direction::RIGHT ? 600.f : -600.f) + m_physic.get_velocity_x()), 0.f),
  1374. m_dir, get_bonus(), *this);
  1375. SoundManager::current()->play("sounds/shoot.wav", get_pos());
  1376. }
  1377. }
  1378. /* Turn to Stone */
  1379. if (m_controller->hold(Control::DOWN) && !m_does_buttjump && m_coyote_timer.started() && !m_swimming && (std::abs(m_physic.get_velocity_x()) > 150.f) && get_bonus() == EARTH_BONUS) {
  1380. m_physic.set_gravity_modifier(1.0f); // Undo jump_early_apex
  1381. adjust_height(TUX_WIDTH);
  1382. m_stone = true;
  1383. m_swimming = false;
  1384. m_sliding = false;
  1385. m_was_crawling_before_slide = false;
  1386. m_crawl = false;
  1387. m_duck = false;
  1388. }
  1389. if (m_stone)
  1390. apply_friction();
  1391. /* Duck or Standup! */
  1392. if ((m_controller->pressed(Control::DOWN) || ((m_duck || m_wants_buttjump) && m_controller->hold(Control::DOWN))) &&
  1393. !m_swimming && !m_sliding && !m_stone) {
  1394. do_duck();
  1395. }
  1396. else {
  1397. do_standup(false);
  1398. }
  1399. /* Drop grabbed object when releasing the Action button on keyboard or gamepad, and on the second button press when using touchscreen */
  1400. if ((m_controller->is_touchscreen() ? m_controller->pressed(Control::ACTION) : !m_controller->hold(Control::ACTION)) &&
  1401. m_grabbed_object && !just_grabbed) {
  1402. auto moving_object = dynamic_cast<MovingObject*> (m_grabbed_object);
  1403. if (moving_object) {
  1404. // move the grabbed object a bit away from tux
  1405. Rectf grabbed_bbox = moving_object->get_bbox();
  1406. Rectf dest_;
  1407. if (m_swimming || m_water_jump)
  1408. {
  1409. dest_.set_bottom(m_col.m_bbox.get_bottom() + (std::sin(m_swimming_angle) * 32.f));
  1410. dest_.set_top(dest_.get_bottom() - grabbed_bbox.get_height());
  1411. dest_.set_left(m_col.m_bbox.get_left() + (std::cos(m_swimming_angle) * 32.f));
  1412. dest_.set_right(dest_.get_left() + grabbed_bbox.get_width());
  1413. }
  1414. else
  1415. {
  1416. dest_.set_bottom(m_col.m_bbox.get_top() + m_col.m_bbox.get_height() * 0.66666f);
  1417. dest_.set_top(dest_.get_bottom() - grabbed_bbox.get_height());
  1418. if (m_dir == Direction::LEFT)
  1419. {
  1420. dest_.set_right(m_col.m_bbox.get_left() - 1);
  1421. dest_.set_left(dest_.get_right() - grabbed_bbox.get_width());
  1422. }
  1423. else
  1424. {
  1425. dest_.set_left(m_col.m_bbox.get_right() + 1);
  1426. dest_.set_right(dest_.get_left() + grabbed_bbox.get_width());
  1427. }
  1428. }
  1429. if (Sector::get().is_free_of_tiles(dest_, true) &&
  1430. Sector::get().is_free_of_statics(dest_, moving_object, true))
  1431. {
  1432. moving_object->set_pos(dest_.p1());
  1433. if (m_controller->hold(Control::UP))
  1434. {
  1435. m_grabbed_object->ungrab(*this, Direction::UP);
  1436. }
  1437. else if (m_controller->hold(Control::DOWN))
  1438. {
  1439. m_grabbed_object->ungrab(*this, Direction::DOWN);
  1440. }
  1441. else if (m_swimming || m_water_jump)
  1442. {
  1443. m_grabbed_object->ungrab(*this,
  1444. std::abs(m_swimming_angle) <= math::PI_2 ? Direction::RIGHT : Direction::LEFT);
  1445. }
  1446. else
  1447. {
  1448. m_grabbed_object->ungrab(*this, m_dir);
  1449. }
  1450. moving_object->del_remove_listener(m_grabbed_object_remove_listener.get());
  1451. m_grabbed_object = nullptr;
  1452. m_released_object = true;
  1453. }
  1454. } else {
  1455. log_debug << "Non MovingObject grabbed?!?" << std::endl;
  1456. }
  1457. }
  1458. if (!m_controller->hold(Control::ACTION) && m_released_object) {
  1459. m_released_object = false;
  1460. }
  1461. /* stop backflipping at will */
  1462. if ( m_backflipping && ( !m_controller->hold(Control::JUMP) && !m_backflip_timer.started()) ){
  1463. stop_backflipping();
  1464. }
  1465. if (m_sliding)
  1466. {
  1467. adjust_height(DUCKED_TUX_HEIGHT);
  1468. slide();
  1469. }
  1470. else if (!m_sliding && (m_coyote_timer.started()) && !m_skidding_timer.started() &&
  1471. (m_floor_normal.y != 0 || (m_controller->hold(Control::LEFT) || m_controller->hold(Control::RIGHT)))
  1472. && m_controller->pressed(Control::DOWN) && std::abs(m_physic.get_velocity_x()) > 1.f &&
  1473. get_bonus()!= EARTH_BONUS)
  1474. {
  1475. sideways_push(m_dir == Direction::LEFT ? -100.f : 100.f);
  1476. adjust_height(DUCKED_TUX_HEIGHT);
  1477. slide();
  1478. }
  1479. }
  1480. void
  1481. Player::position_grabbed_object(bool teleport)
  1482. {
  1483. if (!m_grabbed_object)
  1484. return;
  1485. auto moving_object = dynamic_cast<MovingObject*>(m_grabbed_object);
  1486. assert(moving_object);
  1487. const auto& object_bbox = moving_object->get_bbox();
  1488. Vector pos;
  1489. if (!m_swimming && !m_water_jump)
  1490. {
  1491. // Position where we will hold the lower-inner corner
  1492. pos = Vector(m_col.m_bbox.get_left() + m_col.m_bbox.get_width() / 2,
  1493. m_col.m_bbox.get_top() + m_col.m_bbox.get_height() * 0.66666f);
  1494. // Adjust to find the grabbed object's upper-left corner
  1495. if (m_dir == Direction::LEFT)
  1496. pos.x -= object_bbox.get_width();
  1497. pos.y -= object_bbox.get_height();
  1498. }
  1499. else
  1500. {
  1501. pos = Vector(m_col.m_bbox.get_left() + (std::cos(m_swimming_angle) * 32.f),
  1502. m_col.m_bbox.get_top() + (std::sin(m_swimming_angle) * 32.f));
  1503. }
  1504. if (teleport)
  1505. moving_object->set_pos(pos);
  1506. m_grabbed_object->grab(*this, pos, m_dir);
  1507. }
  1508. bool
  1509. Player::try_grab()
  1510. {
  1511. if (m_controller->hold(Control::ACTION) && !m_grabbed_object && !m_duck && !m_released_object)
  1512. {
  1513. Vector pos(0.0f, 0.0f);
  1514. if (!m_swimming && !m_water_jump)
  1515. {
  1516. if (m_dir == Direction::LEFT)
  1517. {
  1518. pos = Vector(m_col.m_bbox.get_left() - 5, m_col.m_bbox.get_bottom() - 16);
  1519. }
  1520. else
  1521. {
  1522. pos = Vector(m_col.m_bbox.get_right() + 5, m_col.m_bbox.get_bottom() - 16);
  1523. }
  1524. }
  1525. else
  1526. {
  1527. pos = Vector(m_col.m_bbox.get_left() + 16.f + (std::cos(m_swimming_angle) * 48.f),
  1528. m_col.m_bbox.get_top() + 16.f + (std::sin(m_swimming_angle) * 48.f));
  1529. }
  1530. for (auto& moving_object : Sector::get().get_objects_by_type<MovingObject>())
  1531. {
  1532. Portable* portable = dynamic_cast<Portable*>(&moving_object);
  1533. if (portable && portable->is_portable())
  1534. {
  1535. // make sure the Portable isn't currently non-solid
  1536. if (moving_object.get_group() == COLGROUP_DISABLED) continue;
  1537. // check if we are within reach
  1538. if (moving_object.get_bbox().contains(pos))
  1539. {
  1540. if (m_climbing)
  1541. stop_climbing(*m_climbing);
  1542. m_grabbed_object = portable;
  1543. moving_object.add_remove_listener(m_grabbed_object_remove_listener.get());
  1544. position_grabbed_object();
  1545. return true;
  1546. }
  1547. }
  1548. }
  1549. }
  1550. return false;
  1551. }
  1552. void
  1553. Player::handle_input_ghost()
  1554. {
  1555. float vx = 0;
  1556. float vy = 0;
  1557. if (m_controller->hold(Control::LEFT)) {
  1558. m_dir = Direction::LEFT;
  1559. vx -= MAX_RUN_XM * 2;
  1560. }
  1561. if (m_controller->hold(Control::RIGHT)) {
  1562. m_dir = Direction::RIGHT;
  1563. vx += MAX_RUN_XM * 2;
  1564. }
  1565. if (m_controller->hold(Control::UP)) {
  1566. vy -= MAX_RUN_XM * 2;
  1567. }
  1568. if (m_controller->hold(Control::DOWN)) {
  1569. vy += MAX_RUN_XM * 2;
  1570. }
  1571. if (m_controller->hold(Control::ACTION)) {
  1572. set_ghost_mode(false);
  1573. }
  1574. m_physic.set_velocity(Vector(vx, vy) * (m_controller->hold(Control::JUMP) ? 2.5f : 1.f));
  1575. m_physic.set_acceleration(0, 0);
  1576. }
  1577. void
  1578. Player::add_coins(int count)
  1579. {
  1580. m_player_status.add_coins(count);
  1581. }
  1582. int
  1583. Player::get_coins() const
  1584. {
  1585. return m_player_status.coins;
  1586. }
  1587. BonusType
  1588. Player::string_to_bonus(const std::string& bonus) const {
  1589. BonusType type = NO_BONUS;
  1590. if (bonus == "grow") {
  1591. type = GROWUP_BONUS;
  1592. } else if (bonus == "fireflower") {
  1593. type = FIRE_BONUS;
  1594. } else if (bonus == "iceflower") {
  1595. type = ICE_BONUS;
  1596. } else if (bonus == "airflower") {
  1597. type = AIR_BONUS;
  1598. } else if (bonus == "earthflower") {
  1599. type = EARTH_BONUS;
  1600. } else if (bonus == "none") {
  1601. type = NO_BONUS;
  1602. } else {
  1603. std::ostringstream msg;
  1604. msg << "Unknown bonus type " << bonus;
  1605. throw std::runtime_error(msg.str());
  1606. }
  1607. return type;
  1608. }
  1609. std::string
  1610. Player::bonus_to_string() const
  1611. {
  1612. switch(get_bonus())
  1613. {
  1614. case GROWUP_BONUS:
  1615. return "grow";
  1616. case FIRE_BONUS:
  1617. return "fireflower";
  1618. case ICE_BONUS:
  1619. return "iceflower";
  1620. case AIR_BONUS:
  1621. return "airflower";
  1622. case EARTH_BONUS:
  1623. return "earthflower";
  1624. default:
  1625. return "none";
  1626. }
  1627. }
  1628. bool
  1629. Player::add_bonus(const std::string& bonustype)
  1630. {
  1631. return add_bonus(string_to_bonus(bonustype));
  1632. }
  1633. bool
  1634. Player::set_bonus(const std::string& bonustype)
  1635. {
  1636. return set_bonus(string_to_bonus(bonustype));
  1637. }
  1638. bool
  1639. Player::add_bonus(BonusType type, bool animate)
  1640. {
  1641. // always ignore NO_BONUS
  1642. if (type == NO_BONUS) {
  1643. return true;
  1644. }
  1645. // ignore GROWUP_BONUS if we're already big
  1646. if (type == GROWUP_BONUS) {
  1647. if (get_bonus() != NO_BONUS)
  1648. return true;
  1649. }
  1650. return set_bonus(type, animate);
  1651. }
  1652. bool
  1653. Player::set_bonus(BonusType type, bool animate, bool increment_powerup_counter)
  1654. {
  1655. if (m_dying) {
  1656. return false;
  1657. }
  1658. if ((get_bonus() == NO_BONUS) && (type != NO_BONUS || m_stone)) {
  1659. if (!m_swimming)
  1660. {
  1661. if (!adjust_height(BIG_TUX_HEIGHT))
  1662. {
  1663. log_debug << "Can't adjust Tux height" << std::endl;
  1664. return false;
  1665. }
  1666. }
  1667. if (animate) {
  1668. m_growing = true;
  1669. if (m_climbing)
  1670. m_sprite->set_action("climbgrow", m_dir, 1);
  1671. else if (m_swimming)
  1672. m_sprite->set_action("swimgrow", m_dir, 1);
  1673. else if (m_sliding)
  1674. m_sprite->set_action("slidegrow", m_dir, 1);
  1675. else
  1676. m_sprite->set_action("grow", m_dir , 1);
  1677. }
  1678. }
  1679. if (type == NO_BONUS) {
  1680. if (!adjust_height(SMALL_TUX_HEIGHT)) {
  1681. log_debug << "Can't adjust Tux height" << std::endl;
  1682. return false;
  1683. }
  1684. if (m_does_buttjump) m_does_buttjump = false;
  1685. }
  1686. if ((type == NO_BONUS) || (type == GROWUP_BONUS)) {
  1687. m_player_status.max_fire_bullets[get_id()] = 0;
  1688. m_player_status.max_ice_bullets[get_id()] = 0;
  1689. m_player_status.max_air_time[get_id()] = 0;
  1690. m_player_status.max_earth_time[get_id()] = 0;
  1691. }
  1692. if (increment_powerup_counter)
  1693. {
  1694. if (type == FIRE_BONUS) m_player_status.max_fire_bullets[get_id()]++;
  1695. if (type == ICE_BONUS) m_player_status.max_ice_bullets[get_id()]++;
  1696. if (type == AIR_BONUS) m_player_status.max_air_time[get_id()]++;
  1697. if (type == EARTH_BONUS) m_player_status.max_earth_time[get_id()]++;
  1698. }
  1699. if (!m_second_growup_sound_timer.started() &&
  1700. type > GROWUP_BONUS && type != get_bonus())
  1701. {
  1702. m_second_growup_sound_timer.start(0.5);
  1703. }
  1704. m_player_status.bonus[get_id()] = type;
  1705. return true;
  1706. }
  1707. BonusType
  1708. Player::get_bonus() const
  1709. {
  1710. return m_player_status.bonus[m_id];
  1711. }
  1712. void
  1713. Player::set_visible(bool visible)
  1714. {
  1715. m_visible = visible;
  1716. }
  1717. bool
  1718. Player::get_visible() const
  1719. {
  1720. return m_visible;
  1721. }
  1722. void
  1723. Player::kick()
  1724. {
  1725. m_kick_timer.start(KICK_TIME);
  1726. }
  1727. std::string
  1728. Player::get_action() const
  1729. {
  1730. return m_sprite->get_action();
  1731. }
  1732. void
  1733. Player::draw(DrawingContext& context)
  1734. {
  1735. if(Editor::is_active())
  1736. return;
  1737. if (is_dead() && m_target && Sector::get().get_object_count<Player>([this](const Player& p){ return !p.is_dead() && !p.is_dying() && !p.is_winning() && &p != this; }))
  1738. {
  1739. auto* target = Sector::get().get_object_by_uid<Player>(*m_target);
  1740. if (target)
  1741. {
  1742. Vector pos(target->get_bbox().get_middle().x, target->get_bbox().get_top() - static_cast<float>(m_multiplayer_arrow->get_height()) * 1.5f);
  1743. Vector pos_surf(pos - Vector(static_cast<float>(m_multiplayer_arrow->get_width()) / 2.f, 0.f));
  1744. m_multiplayer_arrow->draw(context.color(), pos_surf, LAYER_LIGHTMAP + 1);
  1745. context.color().draw_text(Resources::normal_font, std::to_string(get_id() + 1), pos,
  1746. FontAlignment::ALIGN_CENTER, LAYER_LIGHTMAP + 1);
  1747. }
  1748. return;
  1749. }
  1750. if (m_tag_alpha > 0.f)
  1751. {
  1752. context.color().draw_text(Resources::normal_font, std::to_string(get_id() + 1),
  1753. m_col.m_bbox.get_middle() - Vector(0.f, Resources::normal_font->get_height() / 2.f),
  1754. FontAlignment::ALIGN_CENTER, LAYER_LIGHTMAP + 1,
  1755. Color(1.f, 1.f, 1.f, m_tag_alpha));
  1756. }
  1757. // if Tux is above camera, draw little "air arrow" to show where he is x-wise
  1758. if (m_col.m_bbox.get_bottom() - 16 < Sector::get().get_camera().get_translation().y) {
  1759. float px = m_col.m_bbox.get_left() + (m_col.m_bbox.get_right() - m_col.m_bbox.get_left() - static_cast<float>(m_airarrow.get()->get_width())) / 2.0f;
  1760. float py = Sector::get().get_camera().get_translation().y;
  1761. py += std::min(((py - (m_col.m_bbox.get_bottom() + 16)) / 4), 16.0f);
  1762. context.color().draw_surface(m_airarrow, Vector(px, py), LAYER_HUD - 1);
  1763. }
  1764. std::string sa_prefix = "";
  1765. std::string sa_postfix = "";
  1766. if (get_bonus() == GROWUP_BONUS)
  1767. sa_prefix = "big";
  1768. else if (get_bonus() == FIRE_BONUS)
  1769. sa_prefix = "fire";
  1770. else if (get_bonus() == ICE_BONUS)
  1771. sa_prefix = "ice";
  1772. else if (get_bonus() == AIR_BONUS)
  1773. sa_prefix = "air";
  1774. else if (get_bonus() == EARTH_BONUS)
  1775. sa_prefix = "earth";
  1776. else
  1777. sa_prefix = "small";
  1778. if (!m_swimming && !m_water_jump)
  1779. {
  1780. sa_postfix = (m_dir == Direction::RIGHT) ? "-right" : "-left";
  1781. }
  1782. else
  1783. {
  1784. sa_postfix = ((std::abs(m_swimming_angle) <= math::PI_2)
  1785. || (m_water_jump && std::abs(m_physic.get_velocity_x()) < 10.f))
  1786. ? "-right" : "-left";
  1787. }
  1788. /* Set Tux sprite action */
  1789. if (m_dying) {
  1790. m_sprite->set_angle(0.0f);
  1791. m_sprite->set_action("gameover");
  1792. }
  1793. else if (m_growing)
  1794. {
  1795. // while growing, do not change action
  1796. // do_duck() will take care of cancelling growing manually
  1797. // update() will take care of cancelling when growing completed
  1798. std::string action = "grow";
  1799. if (m_swimming || m_water_jump) {
  1800. action = "swimgrow";
  1801. }
  1802. else if (m_sliding) {
  1803. action = "slidegrow";
  1804. }
  1805. else if (m_climbing) {
  1806. action = "climbgrow";
  1807. }
  1808. m_sprite->set_action(action + sa_postfix, Sprite::LOOPS_CONTINUED);
  1809. }
  1810. else if (m_stone) {
  1811. m_sprite->set_action("earth-stone");
  1812. }
  1813. else if (m_climbing) {
  1814. m_sprite->set_action(sa_prefix+"-climb"+sa_postfix);
  1815. // Avoid flickering briefly after growing on ladder
  1816. if ((m_physic.get_velocity_x()==0)&&(m_physic.get_velocity_y()==0))
  1817. m_sprite->pause_animation();
  1818. }
  1819. else if (m_backflipping) {
  1820. m_sprite->set_action(sa_prefix+"-backflip"+sa_postfix);
  1821. }
  1822. else if (m_sliding) {
  1823. if (m_jumping || m_is_slidejump_falling) {
  1824. m_sprite->set_action(sa_prefix +"-slidejump"+ sa_postfix);
  1825. }
  1826. else {
  1827. m_sprite->set_action(sa_prefix + "-slide" + sa_postfix);
  1828. if (m_was_crawling_before_slide)
  1829. {
  1830. m_sprite->set_frame(m_sprite->get_frames()); // Skip the "duck" animation when coming from crawling
  1831. m_was_crawling_before_slide = false;
  1832. }
  1833. }
  1834. }
  1835. else if (m_duck && is_big() && !m_swimming && !m_crawl) {
  1836. m_sprite->set_action(sa_prefix+"-duck"+sa_postfix);
  1837. }
  1838. else if (m_crawl)
  1839. {
  1840. if (on_ground())
  1841. {
  1842. m_sprite->set_action(sa_prefix + "-crawl" + sa_postfix);
  1843. if (m_physic.get_velocity_x() != 0.f) {
  1844. m_sprite->resume_animation();
  1845. }
  1846. else {
  1847. m_sprite->pause_animation();
  1848. }
  1849. }
  1850. else {
  1851. m_sprite->set_action(sa_prefix + "-slidejump" + sa_postfix);
  1852. }
  1853. }
  1854. else if (m_skidding_timer.started() && !m_skidding_timer.check() && !m_swimming) {
  1855. m_sprite->set_action(sa_prefix + "-skid" + sa_postfix);
  1856. }
  1857. else if (m_kick_timer.started() && !m_kick_timer.check() && !m_swimming && !m_water_jump) {
  1858. m_sprite->set_action(sa_prefix+"-kick"+sa_postfix);
  1859. }
  1860. else if ((m_wants_buttjump || m_does_buttjump) && is_big() && !m_water_jump) {
  1861. if (m_buttjump_stomp) {
  1862. m_sprite->set_action(sa_prefix + "-stomp" + sa_postfix, 1);
  1863. }
  1864. else {
  1865. m_sprite->set_action(sa_prefix + "-buttjump" + sa_postfix, 1);
  1866. }
  1867. }
  1868. else if ((m_controller->hold(Control::LEFT) || m_controller->hold(Control::RIGHT)) && m_can_walljump)
  1869. {
  1870. m_sprite->set_action(sa_prefix+"-walljump"+(m_on_left_wall ? "-left" : "-right"), 1);
  1871. }
  1872. else if (!on_ground() || m_fall_mode != ON_GROUND)
  1873. {
  1874. if (m_physic.get_velocity_x() != 0 || m_fall_mode != ON_GROUND)
  1875. {
  1876. if (m_swimming || m_water_jump)
  1877. {
  1878. if (m_water_jump && m_dir != m_old_dir)
  1879. log_debug << "Obracanko (:" << std::endl;
  1880. if (glm::length(m_physic.get_velocity()) < 50.f)
  1881. m_sprite->set_action(sa_prefix + "-float" + sa_postfix);
  1882. else if (m_water_jump)
  1883. m_sprite->set_action(sa_prefix + "-swimjump" + sa_postfix);
  1884. else if (m_swimboosting)
  1885. m_sprite->set_action(sa_prefix + "-boost" + sa_postfix);
  1886. else
  1887. m_sprite->set_action(sa_prefix + "-swim" + sa_postfix);
  1888. }
  1889. else
  1890. {
  1891. if (m_physic.get_velocity_y() > 0)
  1892. m_sprite->set_action(sa_prefix + "-fall" + sa_postfix);
  1893. else if (m_physic.get_velocity_y() <= 0)
  1894. m_sprite->set_action(sa_prefix + "-jump" + sa_postfix);
  1895. }
  1896. }
  1897. }
  1898. else
  1899. {
  1900. if (fabsf(m_physic.get_velocity_x()) < 1.0f) {
  1901. if (std::all_of(IDLE_STAGES.begin(), IDLE_STAGES.end(),
  1902. [this](const std::string& stage) { return m_sprite->get_action().find("-" + stage + "-") == std::string::npos; }))
  1903. {
  1904. m_idle_stage = 0;
  1905. m_idle_timer.start(static_cast<float>(TIME_UNTIL_IDLE) / 1000.0f);
  1906. m_sprite->set_action(sa_prefix+("-" + IDLE_STAGES[m_idle_stage])+sa_postfix, Sprite::LOOPS_CONTINUED);
  1907. }
  1908. else if (m_idle_timer.check() || m_sprite->animation_done()) {
  1909. m_idle_stage++;
  1910. if (m_idle_stage >= static_cast<unsigned int>(IDLE_STAGES.size()))
  1911. {
  1912. m_idle_stage = static_cast<int>(IDLE_STAGES.size()) - 1;
  1913. m_sprite->set_action(sa_prefix+("-" + IDLE_STAGES[m_idle_stage])+sa_postfix);
  1914. m_sprite->set_animation_loops(-1);
  1915. }
  1916. else
  1917. {
  1918. m_sprite->set_action(sa_prefix+("-" + IDLE_STAGES[m_idle_stage])+sa_postfix, 1);
  1919. }
  1920. }
  1921. else {
  1922. m_sprite->set_action(sa_prefix+("-" + IDLE_STAGES[m_idle_stage])+sa_postfix, Sprite::LOOPS_CONTINUED);
  1923. }
  1924. }
  1925. else
  1926. {
  1927. if (std::abs(m_physic.get_velocity_x()) >= MAX_RUN_XM-3)
  1928. {
  1929. m_sprite->set_action(sa_prefix+"-run"+sa_postfix);
  1930. }
  1931. else
  1932. {
  1933. m_sprite->set_action(sa_prefix+"-walk"+sa_postfix);
  1934. }
  1935. }
  1936. }
  1937. /* Set Tux powerup sprite action */
  1938. if (g_config->christmas_mode)
  1939. {
  1940. //TODO: Implement new santa hats
  1941. //m_santahatsprite->set_action(m_sprite->get_action());
  1942. /*if (m_santahatsprite->get_frames() == m_sprite->get_frames())
  1943. {
  1944. m_santahatsprite->set_frame(m_sprite->get_current_frame());
  1945. m_santahatsprite->set_frame_progress(m_sprite->get_current_frame_progress());
  1946. }*/
  1947. }
  1948. /*
  1949. // Tux is holding something
  1950. if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
  1951. (shooting_timer.get_timeleft() > 0 && !shooting_timer.check())) {
  1952. if (duck) {
  1953. } else {
  1954. }
  1955. }
  1956. */
  1957. /* Draw Tux */
  1958. if (!m_visible || (m_safe_timer.started() && !m_is_intentionally_safe && size_t(g_game_time * 40) % 2))
  1959. {
  1960. } // don't draw Tux
  1961. else if (m_dying)
  1962. m_sprite->draw(context.color(), get_pos(), Sector::get().get_foremost_opaque_layer() + 1);
  1963. else
  1964. m_sprite->draw(context.color(), get_pos(), LAYER_OBJECTS + 1);
  1965. //TODO: Replace recoloring with proper costumes
  1966. Color power_color = (get_bonus() == FIRE_BONUS ? Color(1.f, 0.7f, 0.5f) :
  1967. get_bonus() == ICE_BONUS ? Color(0.7f, 1.f, 1.f) :
  1968. get_bonus() == AIR_BONUS ? Color(0.7f, 1.f, 0.5f) :
  1969. get_bonus() == EARTH_BONUS ? Color(1.f, 0.9f, 0.6f) :
  1970. Color(1.f, 1.f, 1.f));
  1971. m_sprite->set_color(m_stone ? Color(1.f, 1.f, 1.f) : power_color);
  1972. }
  1973. void
  1974. Player::collision_tile(uint32_t tile_attributes)
  1975. {
  1976. if (tile_attributes & Tile::HURTS)
  1977. {
  1978. if (tile_attributes & Tile::UNISOLID)
  1979. kill(false);
  1980. else
  1981. {
  1982. Rectf hurtbox = get_bbox().grown(-6.f);
  1983. if (!Sector::get().is_free_of_tiles(hurtbox, false, Tile::HURTS))
  1984. kill(false);
  1985. }
  1986. }
  1987. if (tile_attributes & Tile::WALLJUMP)
  1988. {
  1989. m_in_walljump_tile = true;
  1990. }
  1991. if (tile_attributes & Tile::ICE) {
  1992. m_ice_this_frame = true;
  1993. m_on_ice = true;
  1994. }
  1995. }
  1996. void
  1997. Player::collision_solid(const CollisionHit& hit)
  1998. {
  1999. if (hit.bottom) {
  2000. if (m_physic.get_velocity_y() > 0)
  2001. m_physic.set_velocity_y(0);
  2002. if (!m_swimming)
  2003. m_on_ground_flag = true;
  2004. m_floor_normal = hit.slope_normal;
  2005. m_is_slidejump_falling = false;
  2006. m_slidejumping = false;
  2007. // Butt Jump landed
  2008. if (m_does_buttjump) {
  2009. m_does_buttjump = false;
  2010. m_buttjump_stomp = true;
  2011. m_physic.set_velocity_y(-300);
  2012. m_on_ground_flag = false;
  2013. Sector::get().add<Particles>(
  2014. m_col.m_bbox.p2(),
  2015. 50, 70, 260, 280, Vector(0, 300), 3,
  2016. Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS+1);
  2017. Sector::get().add<Particles>(
  2018. Vector(m_col.m_bbox.get_left(), m_col.m_bbox.get_bottom()),
  2019. -70, -50, 260, 280, Vector(0, 300), 3,
  2020. Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS+1);
  2021. Sector::get().get_camera().shake(.1f, 0.f, 10.f);
  2022. }
  2023. } else if (hit.top) {
  2024. if (m_physic.get_velocity_y() < 0)
  2025. m_physic.set_velocity_y(.2f);
  2026. }
  2027. if (m_stone && m_floor_normal.y == 0 && (((m_physic.get_velocity_x() < -MAX_RUN_XM) && hit.left) ||
  2028. ((m_physic.get_velocity_x() > MAX_RUN_XM) && hit.right)))
  2029. {
  2030. m_physic.set_acceleration_x(0);
  2031. m_physic.set_velocity_x(0);
  2032. stop_rolling();
  2033. }
  2034. if ((hit.left || hit.right) && hit.slope_normal.x == 0) {
  2035. m_physic.set_velocity_x(0);
  2036. }
  2037. // crushed?
  2038. if (hit.crush) {
  2039. if (hit.left || hit.right) {
  2040. kill(true);
  2041. } else if (hit.top || hit.bottom) {
  2042. kill(false);
  2043. }
  2044. }
  2045. if ((hit.left && m_boost < 0.f) || (hit.right && m_boost > 0.f))
  2046. m_boost = 0.f;
  2047. }
  2048. HitResponse
  2049. Player::collision(GameObject& other, const CollisionHit& hit)
  2050. {
  2051. auto bullet = dynamic_cast<Bullet*> (&other);
  2052. if (bullet) {
  2053. return FORCE_MOVE;
  2054. }
  2055. auto player = dynamic_cast<Player*> (&other);
  2056. if (player) {
  2057. return ABORT_MOVE;
  2058. }
  2059. if (hit.left || hit.right) {
  2060. try_grab(); //grab objects right now, in update it will be too late
  2061. }
  2062. assert(dynamic_cast<MovingObject*> (&other) != nullptr);
  2063. auto moving_object = static_cast<MovingObject*> (&other);
  2064. if (moving_object->get_group() == COLGROUP_TOUCHABLE) {
  2065. auto trigger = dynamic_cast<TriggerBase*> (&other);
  2066. if (trigger && !m_deactivated) {
  2067. if (m_controller->pressed(Control::UP))
  2068. trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
  2069. }
  2070. return FORCE_MOVE;
  2071. }
  2072. auto badguy = dynamic_cast<BadGuy*> (&other);
  2073. if (badguy != nullptr) {
  2074. if (m_safe_timer.started() || m_invincible_timer.started())
  2075. return FORCE_MOVE;
  2076. if (m_stone)
  2077. return ABORT_MOVE;
  2078. }
  2079. return CONTINUE;
  2080. }
  2081. void
  2082. Player::on_flip(float height)
  2083. {
  2084. Vector pos = get_pos();
  2085. pos.y = height - pos.y - get_bbox().get_height();
  2086. move(pos);
  2087. }
  2088. void
  2089. Player::remove_me()
  2090. {
  2091. InputManager::current()->on_player_removed(get_id());
  2092. MovingObject::remove_me();
  2093. }
  2094. void
  2095. Player::make_invincible()
  2096. {
  2097. // No get_pos() here since the music affects the whole sector
  2098. SoundManager::current()->play("sounds/invincible_start.ogg");
  2099. m_invincible_timer.start(TUX_INVINCIBLE_TIME);
  2100. Sector::get().get_singleton_by_type<MusicObject>().play_music(HERRING_MUSIC);
  2101. }
  2102. void
  2103. Player::make_temporarily_safe(float safe_time)
  2104. {
  2105. m_safe_timer.start(safe_time);
  2106. m_is_intentionally_safe = true;
  2107. }
  2108. void
  2109. Player::kill(bool completely)
  2110. {
  2111. if (m_dying || m_deactivated || is_winning() )
  2112. return;
  2113. if (!completely && (m_safe_timer.started() || m_invincible_timer.started()))
  2114. return;
  2115. m_growing = false;
  2116. if (m_climbing) stop_climbing(*m_climbing);
  2117. m_physic.set_velocity_x(0);
  2118. m_boost = 0.f;
  2119. m_sprite->set_angle(0.0f);
  2120. //m_santahatsprite->set_angle(0.0f);
  2121. if (!completely && is_big()) {
  2122. SoundManager::current()->play("sounds/hurt.wav", get_pos());
  2123. if (get_bonus() == FIRE_BONUS
  2124. || get_bonus() == ICE_BONUS
  2125. || get_bonus() == AIR_BONUS
  2126. || get_bonus() == EARTH_BONUS) {
  2127. m_safe_timer.start(TUX_SAFE_TIME);
  2128. m_is_intentionally_safe = false;
  2129. set_bonus(GROWUP_BONUS, true);
  2130. } else if (get_bonus() == GROWUP_BONUS) {
  2131. m_safe_timer.start(TUX_SAFE_TIME /* + GROWING_TIME */);
  2132. m_is_intentionally_safe = false;
  2133. m_duck = false;
  2134. stop_backflipping();
  2135. set_bonus(NO_BONUS, true);
  2136. }
  2137. } else {
  2138. SoundManager::current()->play("sounds/kill.wav", get_pos());
  2139. auto* session = GameSession::current();
  2140. if (session && session->m_prevent_death &&
  2141. !session->reset_checkpoint_button)
  2142. {
  2143. set_ghost_mode(true);
  2144. return;
  2145. }
  2146. m_physic.enable_gravity(true);
  2147. m_physic.set_gravity_modifier(1.0f); // Undo jump_early_apex
  2148. m_safe_timer.stop();
  2149. m_invincible_timer.stop();
  2150. m_physic.set_acceleration(0, 0);
  2151. m_physic.set_velocity(0, -700);
  2152. set_bonus(NO_BONUS, true);
  2153. m_dying = true;
  2154. m_dying_timer.start(3.0);
  2155. set_group(COLGROUP_DISABLED);
  2156. auto alive_players = Sector::get().get_object_count<Player>([](const Player& p){ return !p.is_dead() && !p.is_dying(); });
  2157. if (!alive_players)
  2158. {
  2159. if (m_player_status.respawns_at_checkpoint())
  2160. {
  2161. for (int i = 0; i < 5; i++)
  2162. {
  2163. // the numbers: starting x, starting y, velocity y
  2164. Sector::get().add<FallingCoin>(get_pos() +
  2165. Vector(graphicsRandom.randf(5.0f), graphicsRandom.randf(-32.0f, 18.0f)),
  2166. graphicsRandom.randf(-100.0f, 100.0f));
  2167. }
  2168. m_player_status.take_checkpoint_coins();
  2169. }
  2170. Sector::get().get_effect().fade_out(3.0);
  2171. SoundManager::current()->pause_music(3.0);
  2172. }
  2173. }
  2174. //Sector::get().get_camera().shake(0.1f, m_dying ? 32.f : 0.f, m_dying ? 20.f : 10.f);
  2175. }
  2176. void
  2177. Player::move(const Vector& vector)
  2178. {
  2179. set_pos(vector);
  2180. // Reset size to get correct hitbox if Tux was eg. ducked before moving
  2181. if (is_big())
  2182. m_col.set_size(TUX_WIDTH, BIG_TUX_HEIGHT);
  2183. else
  2184. m_col.set_size(TUX_WIDTH, SMALL_TUX_HEIGHT);
  2185. m_duck = false;
  2186. stop_backflipping();
  2187. m_last_ground_y = vector.y;
  2188. if (m_climbing) stop_climbing(*m_climbing);
  2189. // Make sure objects following Tux move directly with him
  2190. position_grabbed_object(true);
  2191. for (Key* key : m_collected_keys)
  2192. key->update_pos();
  2193. m_physic.reset();
  2194. }
  2195. void
  2196. Player::check_bounds()
  2197. {
  2198. /* Keep tux in sector bounds: */
  2199. if (get_pos().x < 0) {
  2200. // Lock Tux to the size of the level, so that he doesn't fall off
  2201. // the left side
  2202. set_pos(Vector(0, get_pos().y));
  2203. }
  2204. if (m_col.m_bbox.get_right() > Sector::get().get_width()) {
  2205. // Lock Tux to the size of the level, so that he doesn't fall off
  2206. // the right side
  2207. set_pos(Vector(Sector::get().get_width() - m_col.m_bbox.get_width(),
  2208. m_col.m_bbox.get_top()));
  2209. }
  2210. // If Tux is swimming, don't allow him to go below the sector
  2211. if (m_swimming && !m_ghost_mode && !is_dying() && !is_dead()
  2212. && m_col.m_bbox.get_bottom() > Sector::get().get_height()) {
  2213. set_pos(Vector(m_col.m_bbox.get_left(),
  2214. Sector::get().get_height() - m_col.m_bbox.get_height()));
  2215. }
  2216. /* fallen out of the level? */
  2217. if ((get_pos().y > Sector::get().get_height())
  2218. && !m_ghost_mode
  2219. && !(m_is_intentionally_safe && m_safe_timer.started())) {
  2220. kill(true);
  2221. return;
  2222. }
  2223. }
  2224. void
  2225. Player::add_velocity(const Vector& velocity)
  2226. {
  2227. m_physic.set_velocity(m_physic.get_velocity() + velocity);
  2228. }
  2229. void
  2230. Player::add_velocity(const Vector& velocity, const Vector& end_speed)
  2231. {
  2232. if (end_speed.x > 0)
  2233. m_physic.set_velocity_x(std::min(m_physic.get_velocity_x() + velocity.x, end_speed.x));
  2234. if (end_speed.x < 0)
  2235. m_physic.set_velocity_x(std::max(m_physic.get_velocity_x() + velocity.x, end_speed.x));
  2236. if (end_speed.y > 0)
  2237. m_physic.set_velocity_y(std::min(m_physic.get_velocity_y() + velocity.y, end_speed.y));
  2238. if (end_speed.y < 0)
  2239. m_physic.set_velocity_y(std::max(m_physic.get_velocity_y() + velocity.y, end_speed.y));
  2240. }
  2241. Vector
  2242. Player::get_velocity() const
  2243. {
  2244. return m_physic.get_velocity();
  2245. }
  2246. void
  2247. Player::bounce(BadGuy& )
  2248. {
  2249. if (!(get_bonus() == AIR_BONUS))
  2250. m_physic.set_velocity_y(m_controller->hold(Control::JUMP) ? -520.0f : -300.0f);
  2251. else {
  2252. m_physic.set_velocity_y(m_controller->hold(Control::JUMP) ? -580.0f : -340.0f);
  2253. }
  2254. }
  2255. //scripting Functions Below
  2256. void
  2257. Player::deactivate()
  2258. {
  2259. if (m_deactivated)
  2260. return;
  2261. m_deactivated = true;
  2262. m_physic.set_velocity(0, 0);
  2263. m_physic.set_acceleration_x(0);
  2264. m_physic.set_acceleration_y(0);
  2265. if (m_climbing) stop_climbing(*m_climbing);
  2266. }
  2267. void
  2268. Player::activate()
  2269. {
  2270. if (!m_deactivated)
  2271. return;
  2272. m_deactivated = false;
  2273. }
  2274. void Player::walk(float speed)
  2275. {
  2276. m_physic.set_velocity_x(speed);
  2277. }
  2278. void Player::set_dir(bool right)
  2279. {
  2280. m_dir = right ? Direction::RIGHT : Direction::LEFT;
  2281. }
  2282. void
  2283. Player::set_ghost_mode(bool enable)
  2284. {
  2285. if (m_ghost_mode == enable)
  2286. return;
  2287. if (m_climbing) stop_climbing(*m_climbing);
  2288. ungrab_object();
  2289. if (enable) {
  2290. m_ghost_mode = true;
  2291. set_group(COLGROUP_DISABLED);
  2292. m_physic.enable_gravity(false);
  2293. log_debug << "You feel lightheaded. Use movement controls to float around, press ACTION to scare badguys." << std::endl;
  2294. } else {
  2295. m_ghost_mode = false;
  2296. set_group(COLGROUP_MOVING);
  2297. m_physic.enable_gravity(true);
  2298. log_debug << "You feel solid again." << std::endl;
  2299. }
  2300. }
  2301. void
  2302. Player::start_climbing(Climbable& climbable)
  2303. {
  2304. if (m_climbing || m_swimming || m_stone)
  2305. return;
  2306. m_climbing = &climbable;
  2307. m_sprite->set_angle(0.0f);
  2308. m_boost = 0.f;
  2309. m_physic.enable_gravity(false);
  2310. m_physic.set_velocity(0, 0);
  2311. m_physic.set_acceleration(0, 0);
  2312. if (m_backflipping) {
  2313. stop_backflipping();
  2314. do_standup(true);
  2315. }
  2316. }
  2317. void
  2318. Player::stop_climbing(Climbable& /*climbable*/)
  2319. {
  2320. if (!m_climbing) return;
  2321. m_climbing = nullptr;
  2322. ungrab_object();
  2323. m_physic.enable_gravity(true);
  2324. m_physic.set_velocity(0, 0);
  2325. m_physic.set_acceleration(0, 0);
  2326. if (m_controller->hold(Control::JUMP) && !m_controller->hold(Control::DOWN)) {
  2327. m_on_ground_flag = true;
  2328. m_jump_early_apex = false;
  2329. do_jump(get_bonus() == AIR_BONUS ? -540.0f : -480.0f);
  2330. }
  2331. else if (m_controller->hold(Control::UP)) {
  2332. m_on_ground_flag = true;
  2333. // TODO: This won't help. Why?
  2334. do_jump(-300);
  2335. }
  2336. }
  2337. void
  2338. Player::handle_input_climbing()
  2339. {
  2340. if (!m_climbing) {
  2341. log_warning << "handle_input_climbing called with climbing set to 0. Input handling skipped" << std::endl;
  2342. return;
  2343. }
  2344. float vx = 0;
  2345. float vy = 0;
  2346. auto obj_bbox = m_climbing->get_bbox();
  2347. if (m_controller->hold(Control::LEFT) && m_col.m_bbox.get_left() > obj_bbox.get_left()) {
  2348. m_dir = Direction::LEFT;
  2349. vx -= MAX_CLIMB_XM;
  2350. }
  2351. if (m_controller->hold(Control::RIGHT) && m_col.m_bbox.get_right() < obj_bbox.get_right()) {
  2352. m_dir = Direction::RIGHT;
  2353. vx += MAX_CLIMB_XM;
  2354. }
  2355. if (m_controller->hold(Control::UP) && m_col.m_bbox.get_top() > obj_bbox.get_top()) {
  2356. vy -= MAX_CLIMB_YM;
  2357. }
  2358. if (m_controller->hold(Control::DOWN) && m_col.m_bbox.get_bottom() < obj_bbox.get_bottom()) {
  2359. vy += MAX_CLIMB_YM;
  2360. }
  2361. if (m_controller->hold(Control::JUMP)) {
  2362. if (m_can_jump) {
  2363. stop_climbing(*m_climbing);
  2364. return;
  2365. }
  2366. } else {
  2367. m_can_jump = true;
  2368. }
  2369. m_physic.set_velocity(vx, vy);
  2370. m_physic.set_acceleration(0, 0);
  2371. }
  2372. void
  2373. Player::handle_input_rolling()
  2374. {
  2375. // handle exiting
  2376. if (m_stone)
  2377. {
  2378. if (!m_controller->hold(Control::DOWN)) {
  2379. stop_rolling(false);
  2380. }
  2381. else if (get_bonus() != EARTH_BONUS) {
  2382. stop_rolling();
  2383. }
  2384. }
  2385. // handle jumping
  2386. if (m_controller->pressed(Control::JUMP)) m_jump_button_timer.start(JUMP_GRACE_TIME);
  2387. if (m_controller->hold(Control::JUMP) && m_jump_button_timer.started() && (m_can_jump || m_coyote_timer.started()))
  2388. {
  2389. m_jump_button_timer.stop();
  2390. do_jump(-450.f);
  2391. m_coyote_timer.stop();
  2392. }
  2393. // Let go of jump key
  2394. else if (!m_controller->hold(Control::JUMP)) {
  2395. if (!m_backflipping && m_jumping && m_physic.get_velocity_y() < 0) {
  2396. m_jumping = false;
  2397. early_jump_apex();
  2398. }
  2399. }
  2400. if (m_jump_early_apex && m_physic.get_velocity_y() >= 0) {
  2401. do_jump_apex();
  2402. }
  2403. // handle x-movement
  2404. if (std::abs(m_physic.get_velocity_x()) > MAX_STONE_SPEED) {
  2405. m_physic.set_acceleration_x(-m_physic.get_velocity_x());
  2406. }
  2407. else
  2408. {
  2409. // these variables are apparently used differently and must be initialized differently to avoid errors
  2410. float ax;
  2411. float sx = 0.f;
  2412. // slope velocity
  2413. if (m_floor_normal.y != 0)
  2414. {
  2415. if (m_floor_normal.x > 0.f) {
  2416. sx = ((m_dir == Direction::LEFT ? STONE_UP_ACCELERATION : STONE_DOWN_ACCELERATION)*std::abs(m_floor_normal.x));
  2417. }
  2418. if (m_floor_normal.x < 0.f) {
  2419. sx = ((m_dir == Direction::RIGHT ? -STONE_UP_ACCELERATION : -STONE_DOWN_ACCELERATION)*std::abs(m_floor_normal.x));
  2420. }
  2421. }
  2422. else
  2423. {
  2424. sx = 0.f;
  2425. }
  2426. // key velocity
  2427. if (m_controller->hold(Control::LEFT) && !m_controller->hold(Control::RIGHT))
  2428. {
  2429. ax = -STONE_KEY_ACCELERATION;
  2430. m_dir = Direction::LEFT;
  2431. }
  2432. else if (m_controller->hold(Control::RIGHT) && !m_controller->hold(Control::LEFT))
  2433. {
  2434. ax = STONE_KEY_ACCELERATION;
  2435. m_dir = Direction::RIGHT;
  2436. }
  2437. else {
  2438. ax = 0.f;
  2439. }
  2440. if (m_controller->hold(Control::RIGHT) || m_controller->hold(Control::LEFT) || m_floor_normal.y != 0.f) {
  2441. m_physic.set_acceleration_x(ax + sx);
  2442. }
  2443. else {
  2444. apply_friction();
  2445. }
  2446. }
  2447. }
  2448. void
  2449. Player::stop_backflipping()
  2450. {
  2451. m_backflipping = false;
  2452. m_backflip_direction = 0;
  2453. m_sprite->set_angle(0.0f);
  2454. //m_santahatsprite->set_angle(0.0f);
  2455. }
  2456. bool
  2457. Player::has_grabbed(const std::string& object_name) const
  2458. {
  2459. if (object_name.empty())
  2460. {
  2461. return false;
  2462. }
  2463. if (auto object = dynamic_cast<GameObject*>(m_grabbed_object))
  2464. {
  2465. return object->get_name() == object_name;
  2466. }
  2467. return false;
  2468. }
  2469. void
  2470. Player::sideways_push(float delta)
  2471. {
  2472. m_boost = delta;
  2473. }
  2474. void
  2475. Player::ungrab_object(GameObject* gameobject)
  2476. {
  2477. if (!m_grabbed_object)
  2478. return;
  2479. // If gameobject is not null, then the function was called from the
  2480. // ObjectRemoveListener.
  2481. if (!gameobject)
  2482. m_grabbed_object->ungrab(*this, m_dir);
  2483. GameObject* go = dynamic_cast<GameObject*>(m_grabbed_object);
  2484. if (go && m_grabbed_object_remove_listener)
  2485. go->del_remove_listener(m_grabbed_object_remove_listener.get());
  2486. m_grabbed_object = nullptr;
  2487. }
  2488. void
  2489. Player::next_target()
  2490. {
  2491. const auto& players = Sector::get().get_players();
  2492. Player* first = nullptr;
  2493. bool is_next = false;
  2494. for (auto* player : players)
  2495. {
  2496. if (!player->is_dead() && !player->is_dying() && !player->is_winning())
  2497. {
  2498. if (!first)
  2499. {
  2500. first = player;
  2501. }
  2502. if (is_next)
  2503. {
  2504. m_target.reset(new UID());
  2505. *m_target = player->get_uid();
  2506. return;
  2507. }
  2508. if (m_target && player->get_uid() == *m_target)
  2509. {
  2510. is_next = true;
  2511. }
  2512. }
  2513. }
  2514. if (first)
  2515. {
  2516. m_target.reset(new UID());
  2517. *m_target = first->get_uid();
  2518. }
  2519. else
  2520. {
  2521. m_target.reset(nullptr);
  2522. }
  2523. }
  2524. void
  2525. Player::prev_target()
  2526. {
  2527. const auto& players = Sector::get().get_players();
  2528. Player* last = nullptr;
  2529. for (auto* player : players)
  2530. {
  2531. if (!player->is_dead() && !player->is_dying() && !player->is_winning())
  2532. {
  2533. if (m_target && player->get_uid() == *m_target && last)
  2534. {
  2535. *m_target = last->get_uid();
  2536. return;
  2537. }
  2538. last = player;
  2539. }
  2540. }
  2541. if (last)
  2542. {
  2543. m_target.reset(new UID());
  2544. *m_target = last->get_uid();
  2545. }
  2546. else
  2547. {
  2548. m_target.reset(nullptr);
  2549. }
  2550. }
  2551. void
  2552. Player::multiplayer_prepare_spawn()
  2553. {
  2554. m_physic.enable_gravity(true);
  2555. m_physic.set_gravity_modifier(1.0f); // Undo jump_early_apex
  2556. m_safe_timer.stop();
  2557. m_invincible_timer.stop();
  2558. m_physic.set_acceleration(0, -9999);
  2559. m_physic.set_velocity(0, -9999);
  2560. m_dying = true;
  2561. set_group(COLGROUP_DISABLED);
  2562. m_dead = true;
  2563. next_target();
  2564. }
  2565. void
  2566. Player::multiplayer_respawn()
  2567. {
  2568. if (!m_target)
  2569. {
  2570. log_warning << "Can't respawn multiplayer player, no target" << std::endl;
  2571. return;
  2572. }
  2573. auto target = Sector::get().get_object_by_uid<Player>(*m_target);
  2574. if (!target)
  2575. {
  2576. log_warning << "Can't respawn multiplayer player, target missing" << std::endl;
  2577. return;
  2578. }
  2579. m_dying = false;
  2580. m_dead = false;
  2581. m_deactivated = false;
  2582. m_ghost_mode = false;
  2583. set_group(COLGROUP_MOVING);
  2584. m_physic.reset();
  2585. move(target->get_pos());
  2586. m_target.reset();
  2587. }
  2588. void
  2589. Player::stop_rolling(bool violent)
  2590. {
  2591. m_sprite->set_angle(0.0f);
  2592. if (!m_swimming && !m_water_jump && !m_sliding && !m_duck)
  2593. {
  2594. if (!adjust_height(BIG_TUX_HEIGHT))
  2595. {
  2596. adjust_height(BIG_TUX_HEIGHT, 10.f);
  2597. do_duck();
  2598. }
  2599. }
  2600. if (violent)
  2601. {
  2602. for (int i = 0; i < 5; i++)
  2603. {
  2604. Vector pspeed = Vector(graphicsRandom.randf(-100.f, 100.f)*(static_cast<float>(i)-2), graphicsRandom.randf(-200.f, -150.f));
  2605. Vector paccel = Vector(0, 1000.f + graphicsRandom.randf(-100.f, 100.f));
  2606. Sector::get().add<SpriteParticle>(
  2607. "images/particles/rock.sprite", "rock-"+std::to_string(i),
  2608. get_bbox().get_middle(),
  2609. ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS + 6, true);
  2610. }
  2611. SoundManager::current()->play("sounds/brick.wav", get_pos());
  2612. }
  2613. m_stone = false;
  2614. }
  2615. void
  2616. Player::add_collected_key(Key* key)
  2617. {
  2618. m_collected_keys.push_back(key);
  2619. }
  2620. void
  2621. Player::remove_collected_key(Key* key)
  2622. {
  2623. m_collected_keys.erase(std::remove(m_collected_keys.begin(),
  2624. m_collected_keys.end(),
  2625. key),
  2626. m_collected_keys.end());
  2627. }
  2628. /* EOF */