From f5eea9bed19f1f6b4565b244df4ceff3c1fb20bd Mon Sep 17 00:00:00 2001 From: bruhmoent Date: Sat, 13 Jul 2024 11:39:24 +0200 Subject: [PATCH 01/13] Add bubbles while being underwater --- src/object/player.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++- src/object/player.hpp | 4 +++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 5254fb2cf10..4823038f3ff 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -254,6 +254,12 @@ Player::Player(PlayerStatus& player_status, const std::string& name_, int player SoundManager::current()->preload("sounds/invincible_start.ogg"); SoundManager::current()->preload("sounds/splash.wav"); SoundManager::current()->preload("sounds/grow.wav"); + m_bubble_particles[0] = Surface::from_file("images/particles/air_bubble-1.png"); + m_bubble_particles[1] = Surface::from_file("images/particles/air_bubble-2.png"); + m_bubble_particles[2] = Surface::from_file("images/particles/air_bubble-3.png"); + m_bubble_particles[3] = Surface::from_file("images/particles/air_bubble-4.png"); + m_bubble_timer.start(3.0f + (rand() % 2)); + m_col.set_size(TUX_WIDTH, is_big() ? BIG_TUX_HEIGHT : SMALL_TUX_HEIGHT); m_sprite->set_angle(0.0f); @@ -514,6 +520,71 @@ Player::update(float dt_sec) if (m_physic.get_velocity_y() > -350.f && m_controller->hold(Control::UP)) m_physic.set_velocity_y(-350.f); } + + if (m_bubble_timer.check()) + { + int bubble_count = graphicsRandom.rand(3, 5); + float vertical_spacing = 20.0f - graphicsRandom.randf(5); + + glm::vec2 beak_local_offset(30.f, 0.0f); + + float sprite_angle_rad = glm::radians(m_sprite->get_angle()); + + // Calculate the offsets based on the sprite angle + float offset_x = std::cos(sprite_angle_rad) * 10.0f; + float offset_y = std::sin(sprite_angle_rad) * 10.0f; + + // Rotate the beak offset based on the sprite's angle + float rotated_beak_offset_x = beak_local_offset.x * std::cos(sprite_angle_rad) - beak_local_offset.y * std::sin(sprite_angle_rad); + float rotated_beak_offset_y = beak_local_offset.x * std::sin(sprite_angle_rad) + beak_local_offset.y * std::cos(sprite_angle_rad); + + glm::vec2 player_center = m_col.m_bbox.get_middle(); + glm::vec2 beak_position; + + // Determine direction based on the radians + if (std::abs(sprite_angle_rad) >= 5.0f) // Facing left + { + beak_position = player_center - glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); + } + else // Facing right (including straight up or down) + { + beak_position = player_center + glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); + } + + int random_index = graphicsRandom.rand(0, 3); + SurfacePtr bubble_surface = m_bubble_particles[random_index]; + + glm::vec2 bubble_pos; + if (std::abs(sprite_angle_rad) >= 5.0f) // Facing left + { + bubble_pos = beak_position - glm::vec2(offset_x, offset_y); + } + else // Facing right (including straight up or down) + { + bubble_pos = beak_position + glm::vec2(offset_x, offset_y); + } + + m_active_bubbles.push_back({ bubble_surface, bubble_pos }); + + // Restart the timer for the next wave of bubbles + m_bubble_timer.start(0.8f + graphicsRandom.randf(0.0f, 0.6f)); + } + + for (auto& bubble : m_active_bubbles) + { + bubble.second.y -= dt_sec * 30.0f; + + // Small horizontal oscillation + bubble.second.x += std::sin(bubble.second.y * 0.1f) * dt_sec * 5.0f; + } + + m_active_bubbles.erase(std::remove_if(m_active_bubbles.begin(), m_active_bubbles.end(), + [&](const std::pair& bubble) + { + Rectf bubble_box = Rectf(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f); + bool is_out_of_water = Sector::get().is_free_of_tiles(bubble_box, true, Tile::WATER); + return is_out_of_water; + }), m_active_bubbles.end()); } else { @@ -2245,10 +2316,13 @@ Player::draw(DrawingContext& context) get_bonus() == EARTH_BONUS ? Color(1.f, 0.9f, 0.6f) : Color(1.f, 1.f, 1.f)); + for (auto bubble_sprite : m_active_bubbles) + { + context.color().draw_surface(bubble_sprite.first, bubble_sprite.second, LAYER_OBJECTS); + } m_sprite->set_color(m_stone ? Color(1.f, 1.f, 1.f) : power_color); } - void Player::collision_tile(uint32_t tile_attributes) { diff --git a/src/object/player.hpp b/src/object/player.hpp index 984dbaa5c4d..356b6edc5e4 100644 --- a/src/object/player.hpp +++ b/src/object/player.hpp @@ -552,6 +552,10 @@ class Player final : public MovingObject SurfacePtr m_airarrow; /**< arrow indicating Tux' position when he's above the camera */ + SurfacePtr m_bubble_particles[4]; /**< bubble particles for swimming */ + Timer m_bubble_timer; /**< timer for spawning bubble particles */ + std::vector> m_active_bubbles; /**< active bubble particles */ + Vector m_floor_normal; bool m_ghost_mode; /**< indicates if Tux should float around and through solid objects */ From cc9dbbbbff1e657dcf7509bea8dc357c85c7f177 Mon Sep 17 00:00:00 2001 From: bruhmoent <69918580+bruhmoent@users.noreply.github.com> Date: Sat, 13 Jul 2024 12:06:28 +0200 Subject: [PATCH 02/13] Leftovers --- src/object/player.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 4823038f3ff..02e7cb7e412 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -523,9 +523,6 @@ Player::update(float dt_sec) if (m_bubble_timer.check()) { - int bubble_count = graphicsRandom.rand(3, 5); - float vertical_spacing = 20.0f - graphicsRandom.randf(5); - glm::vec2 beak_local_offset(30.f, 0.0f); float sprite_angle_rad = glm::radians(m_sprite->get_angle()); From a7f5358828b0c4cfcb351c711321e3ed4ae7c4d3 Mon Sep 17 00:00:00 2001 From: bruhmoent <69918580+bruhmoent@users.noreply.github.com> Date: Sat, 13 Jul 2024 16:29:57 +0200 Subject: [PATCH 03/13] Add bubble particles made by @RustyBox [ci skip] --- data/images/particles/air_bubble-1.png | Bin 0 -> 6670 bytes data/images/particles/air_bubble-2.png | Bin 0 -> 7234 bytes data/images/particles/air_bubble-3.png | Bin 0 -> 8157 bytes data/images/particles/air_bubble-4.png | Bin 0 -> 9525 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/images/particles/air_bubble-1.png create mode 100644 data/images/particles/air_bubble-2.png create mode 100644 data/images/particles/air_bubble-3.png create mode 100644 data/images/particles/air_bubble-4.png diff --git a/data/images/particles/air_bubble-1.png b/data/images/particles/air_bubble-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a03db404778b2c469fceb09b149901bc418b0c GIT binary patch literal 6670 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 bt6j%6({3znHB?mx1kPn`?qF7C;uH5Tqc3Y) literal 0 HcmV?d00001 diff --git a/data/images/particles/air_bubble-3.png b/data/images/particles/air_bubble-3.png new file mode 100644 index 0000000000000000000000000000000000000000..cf99200699f09866dee89c63e52a37ce03e8c568 GIT binary patch literal 8157 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 literal 0 HcmV?d00001 diff --git a/data/images/particles/air_bubble-4.png b/data/images/particles/air_bubble-4.png new file mode 100644 index 0000000000000000000000000000000000000000..b5195aeda4b073acbad755bc325ee42f192f942f GIT binary patch literal 9525 zcmeHrXIPWlwr=Q6M4D1Hgf1lzLP_YI&<(vQfg}(}fh2TMngvmiq5@JxDT4Hl2-1rp z3L;3gfS@QSf+8St0=m|6-LvmLcR$ZP|5l!qZ_fFScZ~VYG3J+V#@kw(amv3$4{*}eS3fS@4kMHM|+rx_Qae#T>_=BF# z=*s-a)IFW?*0rxcQJ&PJdaOx`{Pm<~Iol%aYNBM(n0Rg2@rsW}RM0RXo2CyfedvW6 z@cpg0FD9O%K1rTrd^XJ~5 z$a?*{sl4$Mao}6%cIb56;~uBUF0fql59f`g@azWRnM?9JV?r)vs*m55B&9=-pSzn> z{X)4CMQ*r%FiheH%S_R>j8-)$T+^SEzGmfZp+JJ$)fH^I-_?w*y6b}nTe8(w{0I%i z6Au-~y_%o0bA^SaV^ZaaFZJKUmxJ8vl3LPRh~p1~Qe8p?Ok6wB^NlZlXeJ$zcC|2= z5y(DL?{&g4<}%^@!d%9&#B+Y<$M?4=%zhD`=Uv$gLJ#_F-S0e|giQv9R$%g;tx%cZuBiJR$c_VsQeVQ)&w{dLb@1A1+}5R%uBewd*L16N2+%p((c%AZGE6hdpWMFeJqsF%t8osm3mDhegdPnCTb|mn9LvL=x zqp9OHlXeg#?Rz~=c6W=E=%w{fp6xAF57mliVfB^Mc{(0Sy3%NUvXDcAxxVTla8S@h z>nzJQR84;M)AUmc>6G2f&i(IL(qq}c>HDu>lI2_Nlo#7y+x>45hpB!MX2Z4@OGMxG z-tbe|iR#up?QnYQ%xMg$$c+m(G0NHwwSZJ*W+rpJJQgw*Vu;%! zm0*>66y8~u0pIT?(yHJy7nE^)nyRXR%x@H!^J3(g3ra^DE^^ce>slnW*Une2u^NNg zW5J2hgvL{asl^jW|I(Ce?tQ5Nb9D(Msx*sZkKN?=RME0`=FY_Z2^=s~l3F1QJ7p&~ zzg(udp0!0-Y?=4&Q;mxJC??;2pdM`dJ~p}OQBF9QE55&`fJ%z@_pxxTkMgd1=5U1CONl5#_=$<zS`smcYLwAkQ*Tswed-%I|ZWv`Qu8K=+W9%+ciN=Z(FNYkkB4 z6F;`{MC2w=vJ1+O5KJ#CEwmHPgXBsd9KWif6!XyT9i7C@keE9M*W4aiHA^iY>b-e- zv}@d5>ST!0*k!7UWOUz^b~T4|w6Xbley=*-tcp&?g>&WF*z;P+TLm#Aut~KVmvK)C z|Lt^5gkBPXcm#0f&B(+`Y&3*#$d1`U_QOV(%3$g?$? zT!H$L4C73jjKYJ$W;y~v_cy*>fGc{R#tqotEt4Bz6fQa{%ObnaKtJ(dt&oVcLcTug zGWSJO4taO2nz>dkj2$akM+71!0!c%xEihf?&AQx|$$l-#*`MJ7?(9g*qdt(I_9~>R} z{J^;L*XSr$gzE}NA7e^}6r+3)w;UEL;pcbDk%>FJ>xrZIITu1E0l~Gy8_vmn%vq|5 z`)WR{S$#=BH>ZC%r_`SQIw}9~I#J=ZexdNJrH@0G*4fYO1DdywFEM0Z%RLn8Im)5s z9(GtbMJT3-M`*G4d3x)9=v$eYM;6bam)E6>Z|UH&PlJL+q{~)q@)I}ngJ#xPh17$b zXYl4G&Ebcx=_xtE1E-D+a=tm@dJx4~aIsyP2}d?7jL(v^MY+^I=%OB*YcQr5n(`4j z*G!Ebx320rT^Z`f_mm;UU$rv7vIgQ)_6cz-o^>7!5$g|5&KI+Om5k<9#+euj$X5iD__Ggs^7m1Y2N&^# z_>5tC|IK!~2CV0T5rTL7V>)MdXIy%u`J4X5SaxH&UVFN9wfhor9`#^A!t+79YQDS< zpJEOvj3&MLxm)8`v;!@V3*K_nxBNT}67V?qQoKsh>WFM7wAbRv2Ve+G@%y%ucxUR@ zN!Pqo89s+caz=VaUj&|31yLFFO|lH63=EkoUDDC4{>_BbRKvDZ{)t}P`9cn~J<2yE zBwcS^BbHTqfGu?fs@5M{;MTEiQ0rXIe^vFA1Y*kKK*^EX_s5b$8F5IFz?>kEn`)2a6%MytVPWC8%6_0fN^o22w(X|_E5e@~JO8o3R zGdsqk*;@i77 z4z1(CXF>W~U<2OLw7V}D=8yXY+SKW?_{BexqQ7UjVECEuE(?JH?~c5a_E`^PZ!hfW zJ)j@@MiSZXFnr&>7N2kclo4Od{M^k)T98|k`KwvimDm+QjUW!|S%GQgP+2R?_bATl zHKtM2tQl0N-)TTjbe}SqY&g)&&Ck&6{X#+M8LG&_`|ugrH4uAEoSEbPj%#cBgCaHa ze7UbK5`zq)-$%6HXP>(gNP3#ibA12PJfAfcuqFUD}2TfJaczvue zUGbxMH1CVfYwn-}ZBYy`$s30^hpIuIfhBZAqlmx{y68fIq zio|O~=flFI+I((-*Su8bk$ChziMZtekc_21-7Z*AobTJbsC1v4bu9f+3mfCTA0^_ z_*7@1Y@T&I^u}WIOwW5ubB?k#Nh{j|qAeDcZpFV=KbqOH1)IhJ&%d59ezZWhz7VH& z#^94_BHMz4} zJkuLJ8EF$WvK0J8{M;K~&G-m{o&)3C1q;kakBAbzkfx6k-=0Qmwg=|%95KJ=fw)Hr zHQaGK`1q{Wknx2<5BLLI{%GUMDf!S#;c9@+;SuimyBmZLb+(1e8&!Rkw@_n(ZM_yp zKY@t0VVs|n37F5^Y_Yy*x9CrEBIO^!wMWYsd7oh+baDo{#@rxY zZ!8l(sAqV++Byc=DQ*LbACss~yV9EM|B2JYfa!G{;7Lxs2-5FJhM!=H`VByV@h5&| zxwN}7HKGox=M+ocd4IUIra7Ag&=t>Lw?+3I29_BQ;T$SCURkoTE?#im?-zTGHH&L* zQ(N`^llK-oCXs}f-}zfw(S_mC&s!6~9ewyZ%cbtcZ%p(0T_JpFhu^li7PrW}aJ<=f z<1qbZ8l$i#y!+aDYSlRb^Gf%%L(SF8)CZ22O>Wjc$SRH4v}<#%+$W@W@-i!$mnvJ=Y#+UC?L}E7)2mpfXq5I>b||YcAq3Z}G?z`4(|6S&Szs!8WJgJfUn~ zaY(t&4I(m57;l8nu5P@_u+|(>IwqS?tRCzlQeLfrsK0PYCgB+KyJ6VOZN*En7xK;w z-4+dYsLgkbu-MEIDfC1L4q`EV&2G!HN_MAFhx_Q++D0>ir$a3XxW&va@Y^eN%U?Q38VEe z_UyZ($bC-WN@R*1=-@SBi2U*vi}Lak`#K3YZZcyF;(vr-!xf*W!j2?lMs5k56>Zix z>~G8r_v4lBcmRsJlp0Zsq-TFdHx7!KK9)Py>$*JK(;4RzH3g^k-XyO=4rULQz7#UT z3Wf8@Ppyw>LC4vz_)B1KjuR9?1+{1c`@q5Q5X#OCuQ?|K2OpcQP04kh2M4ttm~nIt zrWY`Q3Y2Q!Wr0aowx7MbtM+YpF@-ZnIEp>+@a>U-P-t_B+2QS}m*C99jaEV)N=Z&P zS(2k}=YF&BCc16rPUnqwj#jSnt#7VP$KTI&jwG+COM7w0zdpxn!_xFI=j2<4zLCZ8 z%6nQF-=8Mi*5n&29(LiJ1*_$zdlN|qDA zgRFtwUsxC-&TBAK*}vfe-5!u!KPF~~3n48pv`h;GOc!ey4o1b&VfQbdT7|FVtdE`8 zyjL0S<`Vopaz=NeV)M({6Z6|vrpTrjBOu+rJd&STW6*q|l^(=dCwjJ0CaIULql7Hq2V6~Mzmw1tjJ_-vravc z?h|$v3HFRjz}J`a;4TOF1JjNkY&XEk)h>FH-yMSD%#iLPOk**zM-m)Z4*7V9x-;p2 zV(1#`M+_O8e?Nir<(_R1)Sa1*m}+5&!(J)IK4!e(cQx6%tK0pQ!@gEf9DwdT{-=GCN~Pv}0%e{Ukzj5dF8@Xt}c43f1Uwn_*#5g>c3-62qY}QL7mS#gM2x zX%C2m>F1|>7mQzSUu>1-+BZYr;!h+kMP}&$To(N{m^CyQd(6{XmRFW-Lr6P0xb<}g zdS7z5;48^+0DvnCM?1iCwz7nKlL(3^G|3C27)l7B9q<7FTDqYDC~toZ73hWW!4b7V zADi!kfH<@^$Vt^o$tu7Qsmkgg89Rw$fCK)_H@z)%97NP&lH zgZ6mgwCmk&FbKE@q55lsoULquh9oiu2vvkCDk&I;;({R{9d@7=8I6S_jZA(~&|2Cc zUn(^K4hDyWgeZonD3ZuNU}YE#23CTAArJ)`LV*%Sq@qFYL;7eK|4 zh`?P=lou(8stp3s#(}@cX`|l8p|s9l4tCpn^b{%@jG#53wE55gV2F~EhJq4A0RjX6 zZciJvvij4SNcp8AO;2zrDgdmks01bu{$@d;8VCR7?_XL_>}ltyU?he@3L<-BjDs;m zs`&3t1MoqV-+cyAFuPrQapTcgFfFJ(&%fK4BCKrxwAq!>2S*6lv)D!djzoL^i3czg3PGe$ zQABUdE)|Vj5l7=ur-76)O5O_UUNEQvR0D%ifI(ET3Q8~)bu>y9qoJ&%{2PT08Aq!G z6#n<9cB#-bDm7(@7aF3brl5*JLlmG8b&P@r8ltM8hEi66VIdGD6>p6_s@-*f>)Rr< zK@dfyzj|!(C@PjjCeX?YM?{lCD1Qyv;|Lf#Dr#3YWi?fpIz&Yos-gjd{Sn+Aqa%h) zp{4mQtFn?J1hQw2_J*6#I8n58!x2zE7;pg5XK!G4Vc@js(4xFH#X zqLRq=Boba5w95_LrQ9njpw_Q)fm@Qi_YC*I81!z%{i-`d)ojU<^5BB3Mv?b-dh5ijz5RIeNjXo3~j&tRcwFswB2@hA5^u6D8ZE>aEJ=%=V}ABz`Hx_AG6ik-Gi*G;J@pk zwYx{b5xa?K7ZebH$6?5S*XfVE`M=Pek6ize0{;>CpX~ZauK!4Z{|Nj~cKv^oi~X-hZVZw3zb}OLKy9JjTTFWxXZEr% zH3IkoQUKTcE%NecJ!}DHE))QOgJ<_cH#~jnJFSz2im)_0umc1bD#Ki?+ zq;D_txM1j^oKc`a&&tP-Sxv{(gbL;$*b9LOzt?>oCQYM5d>9lS9lg_L-ERZV4vTeFF_E0pPS%Q{-@1Ni=zYT=TI})sNuAP8zot_b>9yg> Date: Sat, 13 Jul 2024 17:06:18 +0200 Subject: [PATCH 04/13] Fix a bug + Add a chance for multiple bubbles to spawn --- src/object/player.cpp | 73 ++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 02e7cb7e412..f3e22706ba4 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -418,6 +418,25 @@ Player::update(float dt_sec) } } + if(m_active_bubbles.size() > 0) + { + for (auto& bubble : m_active_bubbles) + { + bubble.second.y -= dt_sec * 30.0f; + + // Small horizontal oscillation + bubble.second.x += std::sin(bubble.second.y * 0.1f) * dt_sec * 5.0f; + } + + m_active_bubbles.erase(std::remove_if(m_active_bubbles.begin(), m_active_bubbles.end(), + [&](const std::pair& bubble) + { + Rectf bubble_box = Rectf(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f); + bool is_out_of_water = Sector::get().is_free_of_tiles(bubble_box, true, Tile::WATER); + return is_out_of_water; + }), m_active_bubbles.end()); + } + // Skip if in multiplayer respawn if (is_dead() && m_target && Sector::get().get_object_count([this](const Player& p) { return !p.is_dead() && !p.is_dying() && !p.is_winning() && &p != this; })) { @@ -524,7 +543,6 @@ Player::update(float dt_sec) if (m_bubble_timer.check()) { glm::vec2 beak_local_offset(30.f, 0.0f); - float sprite_angle_rad = glm::radians(m_sprite->get_angle()); // Calculate the offsets based on the sprite angle @@ -548,40 +566,37 @@ Player::update(float dt_sec) beak_position = player_center + glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); } - int random_index = graphicsRandom.rand(0, 3); - SurfacePtr bubble_surface = m_bubble_particles[random_index]; + int num_bubbles = graphicsRandom.rand(1, 3); - glm::vec2 bubble_pos; - if (std::abs(sprite_angle_rad) >= 5.0f) // Facing left + for (int i = 0; i < num_bubbles; ++i) { - bubble_pos = beak_position - glm::vec2(offset_x, offset_y); - } - else // Facing right (including straight up or down) - { - bubble_pos = beak_position + glm::vec2(offset_x, offset_y); - } + int random_index = graphicsRandom.rand(0, 3); + SurfacePtr bubble_surface = m_bubble_particles[random_index]; - m_active_bubbles.push_back({ bubble_surface, bubble_pos }); + glm::vec2 bubble_pos; + if (std::abs(sprite_angle_rad) >= 5.0f) // Facing left + { + bubble_pos = beak_position - glm::vec2(offset_x, offset_y); + } + else // Facing right (including straight up or down) + { + bubble_pos = beak_position + glm::vec2(offset_x, offset_y); + } - // Restart the timer for the next wave of bubbles - m_bubble_timer.start(0.8f + graphicsRandom.randf(0.0f, 0.6f)); - } + if (num_bubbles > 1) + { + float burst_offset_x = graphicsRandom.randf(-5.0f, 5.0f); + float burst_offset_y = graphicsRandom.randf(-5.0f, 5.0f); + bubble_pos.x += burst_offset_x; + bubble_pos.y += burst_offset_y; + } - for (auto& bubble : m_active_bubbles) - { - bubble.second.y -= dt_sec * 30.0f; + m_active_bubbles.push_back({ bubble_surface, bubble_pos }); + } - // Small horizontal oscillation - bubble.second.x += std::sin(bubble.second.y * 0.1f) * dt_sec * 5.0f; + // Restart the timer for the next wave of bubbles + m_bubble_timer.start(0.8f + graphicsRandom.randf(0.0f, 0.6f)); } - - m_active_bubbles.erase(std::remove_if(m_active_bubbles.begin(), m_active_bubbles.end(), - [&](const std::pair& bubble) - { - Rectf bubble_box = Rectf(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f); - bool is_out_of_water = Sector::get().is_free_of_tiles(bubble_box, true, Tile::WATER); - return is_out_of_water; - }), m_active_bubbles.end()); } else { @@ -2315,7 +2330,7 @@ Player::draw(DrawingContext& context) for (auto bubble_sprite : m_active_bubbles) { - context.color().draw_surface(bubble_sprite.first, bubble_sprite.second, LAYER_OBJECTS); + context.color().draw_surface(bubble_sprite.first, bubble_sprite.second, LAYER_TILES - 5); } m_sprite->set_color(m_stone ? Color(1.f, 1.f, 1.f) : power_color); } From 6605fa6927863f66e518f5307d0df0a96173ceb8 Mon Sep 17 00:00:00 2001 From: bruhmoent <69918580+bruhmoent@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:09:21 +0200 Subject: [PATCH 05/13] Code style --- src/object/player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index f3e22706ba4..17f9204450d 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -418,7 +418,7 @@ Player::update(float dt_sec) } } - if(m_active_bubbles.size() > 0) + if (m_active_bubbles.size() > 0) { for (auto& bubble : m_active_bubbles) { From d324f56809e56e6ded4cef419f569a22a8cb0f19 Mon Sep 17 00:00:00 2001 From: bruhmoent Date: Sat, 13 Jul 2024 20:37:12 +0200 Subject: [PATCH 06/13] Bug fixes + suggestions --- src/object/player.cpp | 28 ++++++++++++---------------- src/object/player.hpp | 7 +++++-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 17f9204450d..242ed582c4f 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -418,23 +418,20 @@ Player::update(float dt_sec) } } - if (m_active_bubbles.size() > 0) + if (!m_active_bubbles.empty()) { for (auto& bubble : m_active_bubbles) { bubble.second.y -= dt_sec * 30.0f; - - // Small horizontal oscillation bubble.second.x += std::sin(bubble.second.y * 0.1f) * dt_sec * 5.0f; } - m_active_bubbles.erase(std::remove_if(m_active_bubbles.begin(), m_active_bubbles.end(), - [&](const std::pair& bubble) + m_active_bubbles.remove_if([&](const std::pair& bubble) { - Rectf bubble_box = Rectf(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f); + Rectf bubble_box(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f); bool is_out_of_water = Sector::get().is_free_of_tiles(bubble_box, true, Tile::WATER); return is_out_of_water; - }), m_active_bubbles.end()); + }); } // Skip if in multiplayer respawn @@ -543,23 +540,22 @@ Player::update(float dt_sec) if (m_bubble_timer.check()) { glm::vec2 beak_local_offset(30.f, 0.0f); - float sprite_angle_rad = glm::radians(m_sprite->get_angle()); // Calculate the offsets based on the sprite angle - float offset_x = std::cos(sprite_angle_rad) * 10.0f; - float offset_y = std::sin(sprite_angle_rad) * 10.0f; + float offset_x = std::cos(m_swimming_angle) * 10.0f; + float offset_y = std::sin(m_swimming_angle) * 10.0f; // Rotate the beak offset based on the sprite's angle - float rotated_beak_offset_x = beak_local_offset.x * std::cos(sprite_angle_rad) - beak_local_offset.y * std::sin(sprite_angle_rad); - float rotated_beak_offset_y = beak_local_offset.x * std::sin(sprite_angle_rad) + beak_local_offset.y * std::cos(sprite_angle_rad); + float rotated_beak_offset_x = beak_local_offset.x * std::cos(m_swimming_angle) - beak_local_offset.y * std::sin(m_swimming_angle); + float rotated_beak_offset_y = beak_local_offset.x * std::sin(m_swimming_angle) + beak_local_offset.y * std::cos(m_swimming_angle); glm::vec2 player_center = m_col.m_bbox.get_middle(); glm::vec2 beak_position; // Determine direction based on the radians - if (std::abs(sprite_angle_rad) >= 5.0f) // Facing left + if (m_swimming_angle > M_PI_2 && m_swimming_angle < 3.0 * M_PI_2) // Facing left { - beak_position = player_center - glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); + beak_position = player_center + glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); } else // Facing right (including straight up or down) { @@ -574,9 +570,9 @@ Player::update(float dt_sec) SurfacePtr bubble_surface = m_bubble_particles[random_index]; glm::vec2 bubble_pos; - if (std::abs(sprite_angle_rad) >= 5.0f) // Facing left + if (m_swimming_angle > M_PI_2 && m_swimming_angle < 3.0 * M_PI_2) // Facing left { - bubble_pos = beak_position - glm::vec2(offset_x, offset_y); + bubble_pos = beak_position + glm::vec2(offset_x, offset_y); } else // Facing right (including straight up or down) { diff --git a/src/object/player.hpp b/src/object/player.hpp index 356b6edc5e4..96625fed38c 100644 --- a/src/object/player.hpp +++ b/src/object/player.hpp @@ -28,6 +28,9 @@ #include "video/layer.hpp" #include "video/surface_ptr.hpp" +#include +#include + class BadGuy; class Climbable; class Controller; @@ -552,9 +555,9 @@ class Player final : public MovingObject SurfacePtr m_airarrow; /**< arrow indicating Tux' position when he's above the camera */ - SurfacePtr m_bubble_particles[4]; /**< bubble particles for swimming */ + std::array m_bubble_particles; /**< bubble particles for swimming */ Timer m_bubble_timer; /**< timer for spawning bubble particles */ - std::vector> m_active_bubbles; /**< active bubble particles */ + std::list> m_active_bubbles; /**< active bubble particles */ Vector m_floor_normal; From ec30ddb2e489c3a4272cfcce6f984ffc1d4799de Mon Sep 17 00:00:00 2001 From: bruhmoent <69918580+bruhmoent@users.noreply.github.com> Date: Sat, 13 Jul 2024 21:37:35 +0200 Subject: [PATCH 07/13] Fix builds --- src/object/player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 242ed582c4f..3d181f92fd6 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -553,7 +553,7 @@ Player::update(float dt_sec) glm::vec2 beak_position; // Determine direction based on the radians - if (m_swimming_angle > M_PI_2 && m_swimming_angle < 3.0 * M_PI_2) // Facing left + if (m_swimming_angle > static_cast(M_PI_2) && m_swimming_angle < 3.0f * static_cast(M_PI_2)) // Facing left { beak_position = player_center + glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); } @@ -570,7 +570,7 @@ Player::update(float dt_sec) SurfacePtr bubble_surface = m_bubble_particles[random_index]; glm::vec2 bubble_pos; - if (m_swimming_angle > M_PI_2 && m_swimming_angle < 3.0 * M_PI_2) // Facing left + if (m_swimming_angle > static_cast(M_PI_2) && m_swimming_angle < 3.0f * static_cast(M_PI_2)) // Facing left { bubble_pos = beak_position + glm::vec2(offset_x, offset_y); } From 913b0871ce378745c56ccbcd25cb06143c2eb884 Mon Sep 17 00:00:00 2001 From: bruhmoent Date: Sun, 14 Jul 2024 14:25:18 +0200 Subject: [PATCH 08/13] Adjustments --- src/object/player.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 3d181f92fd6..d83ce2002f3 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -540,6 +540,7 @@ Player::update(float dt_sec) if (m_bubble_timer.check()) { glm::vec2 beak_local_offset(30.f, 0.0f); + float big_offset_x = is_big() ? 4.0f : 0.0f; // Calculate the offsets based on the sprite angle float offset_x = std::cos(m_swimming_angle) * 10.0f; @@ -555,11 +556,11 @@ Player::update(float dt_sec) // Determine direction based on the radians if (m_swimming_angle > static_cast(M_PI_2) && m_swimming_angle < 3.0f * static_cast(M_PI_2)) // Facing left { - beak_position = player_center + glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); + beak_position = player_center + glm::vec2(rotated_beak_offset_x - big_offset_x * 2, rotated_beak_offset_y); } else // Facing right (including straight up or down) { - beak_position = player_center + glm::vec2(rotated_beak_offset_x, rotated_beak_offset_y); + beak_position = player_center + glm::vec2(rotated_beak_offset_x - 4.0f + big_offset_x, rotated_beak_offset_y); } int num_bubbles = graphicsRandom.rand(1, 3); @@ -572,11 +573,11 @@ Player::update(float dt_sec) glm::vec2 bubble_pos; if (m_swimming_angle > static_cast(M_PI_2) && m_swimming_angle < 3.0f * static_cast(M_PI_2)) // Facing left { - bubble_pos = beak_position + glm::vec2(offset_x, offset_y); + bubble_pos = beak_position + glm::vec2(offset_x - big_offset_x * 2, offset_y); } else // Facing right (including straight up or down) { - bubble_pos = beak_position + glm::vec2(offset_x, offset_y); + bubble_pos = beak_position + glm::vec2(offset_x - 4.0f + big_offset_x, offset_y); } if (num_bubbles > 1) From bf3104b50fcc09c2ac3b22e2d36bba1090a61320 Mon Sep 17 00:00:00 2001 From: bruhmoent Date: Tue, 16 Jul 2024 13:50:27 +0200 Subject: [PATCH 09/13] Use .sprite + code style fixes --- data/images/particles/air_bubble-0.png | Bin 0 -> 5300 bytes data/images/particles/air_bubble.sprite | 23 ++++++++ src/object/player.cpp | 71 +++++++++++++----------- src/object/player.hpp | 4 +- 4 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 data/images/particles/air_bubble-0.png create mode 100644 data/images/particles/air_bubble.sprite diff --git a/data/images/particles/air_bubble-0.png b/data/images/particles/air_bubble-0.png new file mode 100644 index 0000000000000000000000000000000000000000..368f9bed0132159fa88faf81ef33ff7955cc4ac3 GIT binary patch literal 5300 zcmeHLdsGuw8Xw;BP^c@u3KD}x{>0n`;l&rz%j zK6VwgB3iZ9b``}}#YIqSb+^7ER8&v#mKT5afQZP(6XpL~JZGMdE}3W6=d> zjD^_=9D?kZ?ymT_;xa2>-Zfd$;#_5q`OUd}(Sf+d*TT$KhE%S(nD%s|NFWFrFsn3X z<&cnrvz|Qg=!caJ{=DGZNaG&OqLuz|-L|bKR@F-HSkKjLOexwkh(ER@`~3LymiEdu z+~RY69zRa|CVlXwu&^KOx1TRPxpzsx;{I#${cdKZ+7;`v)_q|#M(%GSk=X2_e@sOl zY?r9lZsH{v6h;3Y)bo^nfwlBmP(@G4SDSQ4{~qTD$LMcj-2S6CSD$S4b$=L@c6rVC z>mJJ|Esoh98&|%w&28S%+|RboB!O|2YT2_b8hV{)$9qLSs77nAN3@gMW&tz-I)22_lmUgfx)-w6SbLh%gO^s z?mV#^yOgs~H2$J{d*jEOYm_UzOGX6Sy|U|ORP8mbJwA>aaivH2N>;Le?4Qz~sW(3@ zf0$S~b$iKFc}0*ZfT}(+462%SJbaB?-SQT_IXLL-0Q{);@mk*e$*m{0m8S^f{Jo*voUnw>_s&eU!7C<>!xtRLfQ@ z|MP&nzN0n|5v)6w`L}5yhHt{k7KKDb4IC%gvPFNsV#whQkr88>9}NGfuZpvB<)qph zH%^^fT3Pg9EewDEe!GAS8q7JaCiKGDYs{z(FJ+b+k^C* z%9_Hcz$@_!Up0P0{%~m2(@^HickkjWQ!@0}&$`inDLtZ>mhAJL5*rZJ*8Y8T>m6hJ zWnZ#t_sK~=avt|B^gl57=MM+Bwm(|()6kdEem_=2qPg{hAjqwhNJz*~CM3Mhe30*} zXO=5c8%Bj3o|-kLOznA>bz?~tJTWME`m(6L_3^~;owswT)_!6lH8Ur~>q5z#u(EMJ z{qiP<=6J=--Lw_uX^n?-W{nG| zW0ykSL5ZoF&(@w8fnS%^-Mij=XY#_lf={1Y9!GjDb&DytFKV5a^mUxPhG<>=bbk03 zBO)7H8k@Z*FKI8ov*S3qdQ2~Wxk#fu^QiFy&Gxg0seUy(s(PpYAtiWR^^CzSI}R0; z)%brur4w%;Hvupy@|X#b9D?f@%FT5-cqUO zk9xqYxRptatN2C@~SXN=08ZFeLGA%nc#!qgC0f7;t z5tiL(Fj-)`g6-gi!80?>W3wC(TCZSFP-UK(GXLXQ+RW!*CCOaH3V+Xy3*7B5KAO`yZ0G^P~mvZ<*j!?#PwFj#z zRj0Md(xD>IlV?ZFJOP)_Ga9>CSm>nuH~!vgVNrt%ftQI{NGpY6N%@$G4tI5GHdrmL zKCKpvnR3K!(BeE0l*6;DO^Q;L(P_iTm`51R4hshDiqxW=IJ1>9I51k2hZ!&<2*d)I z1zqqofxkJRF8g6teiaCC*U8@n{aP{Vy%=T z77D~14GxBUnN}ddq5&}~aiK`32v7-#!8Iy|N(-pOm`E%X3AG$tED>cPHfjJl$YzIh- zFx3H=9rIu}Z~}!PG)bvR(x6~7+$@IDQB*8>N4dabNz`HJ0ApIF;yP+B9?9c5hH~D! zz`tV3(vddP|HktM+Q||}(KeFOr&H;gX&6eso9AudPNq!I?JP7^p!|(Z{Q@UXxQj?N`ihnVs(&~H16?e#qImPFQI z(OQu2QjSD};~bEST8<2l9?9WLKuXF4e2G+mcV)MbIBi2HEG`e^061q*T#j>Q4eQ9i z$gX&s4r7vw54tVW2PJAD9~KB;v5@UNZI+zJblBIsl`}m^rGj1ckTX3BRx*j0Wi^`( z1V(k$>2==x7o3ZIM~?oMxhu?Roj{rkKo#rgberj&?%x7*GK?iq%w!?o6}l_LDT~WR z1Y&m1feQ}Ym%Ppkvm;BGLi!zlIOtX zon8Mox%}R|a$_d&cb^TsP#5*kr-GMpZ_SvLL`Vl!K$XYFR8@f)Uvuh23k3Q1W^n5KzgP8a82s~bEabM{vv&qqf|N;V KiC@O&mi!0mK$SKC literal 0 HcmV?d00001 diff --git a/data/images/particles/air_bubble.sprite b/data/images/particles/air_bubble.sprite new file mode 100644 index 00000000000..4c51ad33204 --- /dev/null +++ b/data/images/particles/air_bubble.sprite @@ -0,0 +1,23 @@ +(supertux-sprite + (action + (name "normal") + (fps 15) + (loops 1) + (images "air_bubble-0.png" + "air_bubble-1.png" + "air_bubble-2.png" + "air_bubble-3.png" + "air_bubble-4.png" + ) + ) + (action + (name "small") + (fps 15) + (loops 1) + (images "air_bubble-0.png" + "air_bubble-1.png" + "air_bubble-2.png" + "air_bubble-3.png" + ) + ) +) diff --git a/src/object/player.cpp b/src/object/player.cpp index d83ce2002f3..89ed63fca53 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -239,6 +239,7 @@ Player::Player(PlayerStatus& player_status, const std::string& name_, int player m_target_sliding_angle(0.0f), m_sliding_rotation_timer(), m_is_slidejump_falling(false), + m_bubbles_sprite(SpriteManager::current()->create("images/particles/air_bubble.sprite")), m_was_crawling_before_slide(false) { m_name = name_; @@ -254,11 +255,7 @@ Player::Player(PlayerStatus& player_status, const std::string& name_, int player SoundManager::current()->preload("sounds/invincible_start.ogg"); SoundManager::current()->preload("sounds/splash.wav"); SoundManager::current()->preload("sounds/grow.wav"); - m_bubble_particles[0] = Surface::from_file("images/particles/air_bubble-1.png"); - m_bubble_particles[1] = Surface::from_file("images/particles/air_bubble-2.png"); - m_bubble_particles[2] = Surface::from_file("images/particles/air_bubble-3.png"); - m_bubble_particles[3] = Surface::from_file("images/particles/air_bubble-4.png"); - m_bubble_timer.start(3.0f + (rand() % 2)); + m_bubble_timer.start(3.0f + graphicsRandom.randf(2)); m_col.set_size(TUX_WIDTH, is_big() ? BIG_TUX_HEIGHT : SMALL_TUX_HEIGHT); @@ -422,11 +419,11 @@ Player::update(float dt_sec) { for (auto& bubble : m_active_bubbles) { - bubble.second.y -= dt_sec * 30.0f; + bubble.second.y -= dt_sec * 40.0f; bubble.second.x += std::sin(bubble.second.y * 0.1f) * dt_sec * 5.0f; } - m_active_bubbles.remove_if([&](const std::pair& bubble) + m_active_bubbles.remove_if([&](const std::pair& bubble) { Rectf bubble_box(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f); bool is_out_of_water = Sector::get().is_free_of_tiles(bubble_box, true, Tile::WATER); @@ -539,7 +536,7 @@ Player::update(float dt_sec) if (m_bubble_timer.check()) { - glm::vec2 beak_local_offset(30.f, 0.0f); + Vector beak_local_offset(30.f, 0.0f); float big_offset_x = is_big() ? 4.0f : 0.0f; // Calculate the offsets based on the sprite angle @@ -550,45 +547,53 @@ Player::update(float dt_sec) float rotated_beak_offset_x = beak_local_offset.x * std::cos(m_swimming_angle) - beak_local_offset.y * std::sin(m_swimming_angle); float rotated_beak_offset_y = beak_local_offset.x * std::sin(m_swimming_angle) + beak_local_offset.y * std::cos(m_swimming_angle); - glm::vec2 player_center = m_col.m_bbox.get_middle(); - glm::vec2 beak_position; + Vector player_center = m_col.m_bbox.get_middle(); + Vector beak_position; // Determine direction based on the radians - if (m_swimming_angle > static_cast(M_PI_2) && m_swimming_angle < 3.0f * static_cast(M_PI_2)) // Facing left + if (m_swimming_angle > static_cast(math::PI_2) && m_swimming_angle < 3.0f * static_cast(math::PI_2)) // Facing left { - beak_position = player_center + glm::vec2(rotated_beak_offset_x - big_offset_x * 2, rotated_beak_offset_y); + beak_position = player_center + Vector(rotated_beak_offset_x - big_offset_x * 2, rotated_beak_offset_y); } else // Facing right (including straight up or down) { - beak_position = player_center + glm::vec2(rotated_beak_offset_x - 4.0f + big_offset_x, rotated_beak_offset_y); + beak_position = player_center + Vector(rotated_beak_offset_x - 4.0f + big_offset_x, rotated_beak_offset_y); } int num_bubbles = graphicsRandom.rand(1, 3); - for (int i = 0; i < num_bubbles; ++i) - { - int random_index = graphicsRandom.rand(0, 3); - SurfacePtr bubble_surface = m_bubble_particles[random_index]; + std::optional> bubble_surfaces = m_bubbles_sprite->get_action_surfaces("normal"); - glm::vec2 bubble_pos; - if (m_swimming_angle > static_cast(M_PI_2) && m_swimming_angle < 3.0f * static_cast(M_PI_2)) // Facing left - { - bubble_pos = beak_position + glm::vec2(offset_x - big_offset_x * 2, offset_y); - } - else // Facing right (including straight up or down) - { - bubble_pos = beak_position + glm::vec2(offset_x - 4.0f + big_offset_x, offset_y); - } + if (bubble_surfaces) + { + const std::vector& surfaces = bubble_surfaces.value(); + int surfaces_size = surfaces.size(); - if (num_bubbles > 1) + for (int i = 0; i < num_bubbles; ++i) { - float burst_offset_x = graphicsRandom.randf(-5.0f, 5.0f); - float burst_offset_y = graphicsRandom.randf(-5.0f, 5.0f); - bubble_pos.x += burst_offset_x; - bubble_pos.y += burst_offset_y; + int random_index = graphicsRandom.rand(1, surfaces_size - 1); + SurfacePtr bubble_surface = surfaces.at(random_index); + + Vector bubble_pos(0.f, 0.f); + if (m_swimming_angle > static_cast(math::PI_2) && m_swimming_angle < 3.0f * static_cast(math::PI_2)) // Facing left + { + bubble_pos = beak_position + Vector(offset_x - big_offset_x * 2, offset_y); + } + else // Facing right (including straight up or down) + { + bubble_pos = beak_position + Vector(offset_x - 4.0f + big_offset_x, offset_y); + } + + if (num_bubbles > 1) + { + float burst_offset_x = graphicsRandom.randf(-5.0f, 5.0f); + float burst_offset_y = graphicsRandom.randf(-5.0f, 5.0f); + bubble_pos.x += burst_offset_x; + bubble_pos.y += burst_offset_y; + } + + m_active_bubbles.push_back({ bubble_surface, bubble_pos }); } - - m_active_bubbles.push_back({ bubble_surface, bubble_pos }); } // Restart the timer for the next wave of bubbles diff --git a/src/object/player.hpp b/src/object/player.hpp index 96625fed38c..6f3c8d2ea42 100644 --- a/src/object/player.hpp +++ b/src/object/player.hpp @@ -555,9 +555,9 @@ class Player final : public MovingObject SurfacePtr m_airarrow; /**< arrow indicating Tux' position when he's above the camera */ - std::array m_bubble_particles; /**< bubble particles for swimming */ + SpritePtr m_bubbles_sprite; /**< bubble particles sprite for swimming */ Timer m_bubble_timer; /**< timer for spawning bubble particles */ - std::list> m_active_bubbles; /**< active bubble particles */ + std::list> m_active_bubbles; /**< active bubble particles */ Vector m_floor_normal; From 2dd2afa6cb7e690ad5e773922191326e020000e6 Mon Sep 17 00:00:00 2001 From: bruhmoent <69918580+bruhmoent@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:02:52 +0200 Subject: [PATCH 10/13] Fix builds --- src/object/player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 89ed63fca53..0de916e9585 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -226,6 +226,7 @@ Player::Player(PlayerStatus& player_status, const std::string& name_, int player m_swimming_accel_modifier(100.f), m_water_jump(false), m_airarrow(Surface::from_file("images/engine/hud/airarrow.png")), + m_bubbles_sprite(SpriteManager::current()->create("images/particles/air_bubble.sprite")), m_floor_normal(0.0f, 0.0f), m_ghost_mode(false), m_unduck_hurt_timer(), @@ -239,7 +240,6 @@ Player::Player(PlayerStatus& player_status, const std::string& name_, int player m_target_sliding_angle(0.0f), m_sliding_rotation_timer(), m_is_slidejump_falling(false), - m_bubbles_sprite(SpriteManager::current()->create("images/particles/air_bubble.sprite")), m_was_crawling_before_slide(false) { m_name = name_; @@ -567,7 +567,7 @@ Player::update(float dt_sec) if (bubble_surfaces) { const std::vector& surfaces = bubble_surfaces.value(); - int surfaces_size = surfaces.size(); + int surfaces_size = static_cast(surfaces.size()); for (int i = 0; i < num_bubbles; ++i) { From 32e9108530d48993a561dcb03e92e685e7783cf8 Mon Sep 17 00:00:00 2001 From: bruhmoent <69918580+bruhmoent@users.noreply.github.com> Date: Wed, 17 Jul 2024 20:36:17 +0200 Subject: [PATCH 11/13] Consistency --- src/object/player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index 0de916e9585..f2546ea45b2 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -551,7 +551,7 @@ Player::update(float dt_sec) Vector beak_position; // Determine direction based on the radians - if (m_swimming_angle > static_cast(math::PI_2) && m_swimming_angle < 3.0f * static_cast(math::PI_2)) // Facing left + if (std::abs(m_swimming_angle) > static_cast(math::PI_2)) // Facing left { beak_position = player_center + Vector(rotated_beak_offset_x - big_offset_x * 2, rotated_beak_offset_y); } @@ -575,7 +575,7 @@ Player::update(float dt_sec) SurfacePtr bubble_surface = surfaces.at(random_index); Vector bubble_pos(0.f, 0.f); - if (m_swimming_angle > static_cast(math::PI_2) && m_swimming_angle < 3.0f * static_cast(math::PI_2)) // Facing left + if (std::abs(m_swimming_angle) > static_cast(math::PI_2)) // Facing left { bubble_pos = beak_position + Vector(offset_x - big_offset_x * 2, offset_y); } From 292959edcb40f4cd145e4f5b8dde5476a8f0a596 Mon Sep 17 00:00:00 2001 From: bruhmoent <69918580+bruhmoent@users.noreply.github.com> Date: Wed, 17 Jul 2024 23:26:19 +0200 Subject: [PATCH 12/13] Unnecessary include --- src/object/player.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/object/player.hpp b/src/object/player.hpp index 6f3c8d2ea42..af38ec11fd1 100644 --- a/src/object/player.hpp +++ b/src/object/player.hpp @@ -28,7 +28,6 @@ #include "video/layer.hpp" #include "video/surface_ptr.hpp" -#include #include class BadGuy; From 9e01a5ffe870dd448952081899e68b5589786943 Mon Sep 17 00:00:00 2001 From: bruhmoent Date: Fri, 11 Oct 2024 17:17:42 +0200 Subject: [PATCH 13/13] Use animated sprites + destroy on solids --- src/object/player.cpp | 65 ++++++++++++++++++++++--------------------- src/object/player.hpp | 3 +- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/object/player.cpp b/src/object/player.cpp index f2546ea45b2..30f55b1aa64 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -144,6 +144,7 @@ const float SWIM_BOOST_SPEED = 600.f; const float SWIM_TO_BOOST_ACCEL = 15.f; const float TURN_MAGNITUDE = 0.15f; const float TURN_MAGNITUDE_BOOST = 0.2f; +const std::array BUBBLE_ACTIONS = { "normal", "small" }; /* Buttjump variables */ @@ -423,11 +424,12 @@ Player::update(float dt_sec) bubble.second.x += std::sin(bubble.second.y * 0.1f) * dt_sec * 5.0f; } - m_active_bubbles.remove_if([&](const std::pair& bubble) + m_active_bubbles.remove_if([&](const std::pair& bubble) { Rectf bubble_box(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f); bool is_out_of_water = Sector::get().is_free_of_tiles(bubble_box, true, Tile::WATER); - return is_out_of_water; + bool hits_solid = !Sector::get().is_free_of_tiles(bubble_box, false, Tile::SOLID); + return is_out_of_water || hits_solid; }); } @@ -562,37 +564,36 @@ Player::update(float dt_sec) int num_bubbles = graphicsRandom.rand(1, 3); - std::optional> bubble_surfaces = m_bubbles_sprite->get_action_surfaces("normal"); - - if (bubble_surfaces) + for (int i = 0; i < num_bubbles; ++i) { - const std::vector& surfaces = bubble_surfaces.value(); - int surfaces_size = static_cast(surfaces.size()); + int random_action_index = graphicsRandom.rand(0, 2); + std::string selected_action = BUBBLE_ACTIONS[random_action_index]; + + SpritePtr bubble_sprite = m_bubbles_sprite->clone(); + bubble_sprite->set_animation_loops(1); + bubble_sprite->set_action(selected_action); + + Vector bubble_pos(0.f, 0.f); + if (std::abs(m_swimming_angle) > static_cast(math::PI_2)) // Facing left + { + bubble_pos = beak_position + Vector(offset_x - big_offset_x * 2, offset_y); + } + else // Facing right (including straight up or down) + { + bubble_pos = beak_position + Vector(offset_x - 4.0f + big_offset_x, offset_y); + } + + if (num_bubbles > 1) + { + float burst_offset_x = graphicsRandom.randf(-5.0f, 5.0f); + float burst_offset_y = graphicsRandom.randf(-5.0f, 5.0f); + bubble_pos.x += burst_offset_x; + bubble_pos.y += burst_offset_y; + } - for (int i = 0; i < num_bubbles; ++i) + if (bubble_pos.y > -1) { - int random_index = graphicsRandom.rand(1, surfaces_size - 1); - SurfacePtr bubble_surface = surfaces.at(random_index); - - Vector bubble_pos(0.f, 0.f); - if (std::abs(m_swimming_angle) > static_cast(math::PI_2)) // Facing left - { - bubble_pos = beak_position + Vector(offset_x - big_offset_x * 2, offset_y); - } - else // Facing right (including straight up or down) - { - bubble_pos = beak_position + Vector(offset_x - 4.0f + big_offset_x, offset_y); - } - - if (num_bubbles > 1) - { - float burst_offset_x = graphicsRandom.randf(-5.0f, 5.0f); - float burst_offset_y = graphicsRandom.randf(-5.0f, 5.0f); - bubble_pos.x += burst_offset_x; - bubble_pos.y += burst_offset_y; - } - - m_active_bubbles.push_back({ bubble_surface, bubble_pos }); + m_active_bubbles.emplace_back(std::make_pair(std::move(bubble_sprite), bubble_pos)); } } @@ -2330,9 +2331,9 @@ Player::draw(DrawingContext& context) get_bonus() == EARTH_BONUS ? Color(1.f, 0.9f, 0.6f) : Color(1.f, 1.f, 1.f)); - for (auto bubble_sprite : m_active_bubbles) + for (auto& bubble_sprite : m_active_bubbles) { - context.color().draw_surface(bubble_sprite.first, bubble_sprite.second, LAYER_TILES - 5); + bubble_sprite.first->draw(context.color(), bubble_sprite.second, LAYER_TILES - 5); } m_sprite->set_color(m_stone ? Color(1.f, 1.f, 1.f) : power_color); } diff --git a/src/object/player.hpp b/src/object/player.hpp index af38ec11fd1..89bea47913f 100644 --- a/src/object/player.hpp +++ b/src/object/player.hpp @@ -28,6 +28,7 @@ #include "video/layer.hpp" #include "video/surface_ptr.hpp" +#include #include class BadGuy; @@ -556,7 +557,7 @@ class Player final : public MovingObject SpritePtr m_bubbles_sprite; /**< bubble particles sprite for swimming */ Timer m_bubble_timer; /**< timer for spawning bubble particles */ - std::list> m_active_bubbles; /**< active bubble particles */ + std::list> m_active_bubbles; /**< active bubble particles */ Vector m_floor_normal;