@@ -22,17 +22,20 @@ using bave::Shader;
2222using bave::Texture;
2323
2424Player::Player (Services const & services, std::unique_ptr<IController> controller)
25- : m_services(&services), m_stats(&services.get<Stats>()), m_controller(std::move(controller)) {
25+ : m_services(&services), m_stats(&services.get<Stats>()), m_controller(std::move(controller)), m_shield(services) {
2626 auto const & layout = services.get <Layout>();
2727 ship.transform .position .x = layout.player_x ;
2828
2929 auto const & resources = services.get <Resources>();
3030
31- if (auto const texture = services.get <Resources>().get <Texture>(" images/player_ship.png" )) { ship.set_texture (texture); }
32- ship.set_auto_size (ship_size);
31+ if (auto const texture = services.get <Resources>().get <Texture>(" images/player_ship.png" )) {
32+ ship.set_texture (texture);
33+ ship.set_size (texture->get_size ());
34+ }
3335
3436 if (auto const exhaust = resources.get <ParticleEmitter>(" particles/exhaust.json" )) { m_exhaust = *exhaust; }
3537 m_exhaust.set_position (get_exhaust_position ());
38+ m_exhaust.config .respawn = true ;
3639 m_exhaust.pre_warm ();
3740
3841 if (auto const death = resources.get <ParticleEmitter>(" particles/explode.json" )) { m_death_source = *death; }
@@ -45,7 +48,7 @@ void Player::on_move(PointerMove const& pointer_move) { m_controller->on_move(po
4548
4649void Player::on_tap (PointerTap const & pointer_tap) { m_controller->on_tap (pointer_tap); }
4750
48- void Player::tick (State const & state, Seconds const dt) {
51+ auto Player::tick (State const & state, Seconds const dt) -> bool {
4952 if (m_death) {
5053 m_death->tick (dt);
5154 if (m_death->active_particles () == 0 ) { m_death.reset (); }
@@ -54,39 +57,41 @@ void Player::tick(State const& state, Seconds const dt) {
5457 auto const round_state = IWeaponRound::State{
5558 .targets = state.targets ,
5659 .muzzle_position = get_muzzle_position (),
57- .in_play = !health .is_dead (),
60+ .in_play = !m_health .is_dead (),
5861 };
5962 m_arsenal.tick (round_state, m_controller->is_firing (), dt);
6063
61- if (health.is_dead ()) { return ; }
64+ m_shield.set_position (ship.transform .position );
65+ m_shield.tick (dt);
66+
67+ m_exhaust.tick (dt);
68+
69+ if (m_health.is_dead ()) { return false ; }
6270
6371 auto const y_position = m_controller->tick (dt);
6472 set_y (y_position);
6573
66- auto const hitbox = Rect<>::from_size (hitbox_size, ship.transform .position );
67- for (auto const & target : state.targets ) {
68- if (is_intersecting (target->get_bounds (), hitbox)) {
69- on_death (dt);
70- target->force_death ();
71- return ;
72- }
73- }
74-
7574 m_exhaust.set_position (get_exhaust_position ());
76- m_exhaust.tick (dt);
75+
76+ auto ret = false ;
77+ auto const hitbox = Rect<>::from_size (hitbox_size, ship.transform .position );
78+ for (auto const & target : state.targets ) { ret |= check_hit (*target, hitbox, dt); }
7779
7880 for (auto const & powerup : state.powerups ) {
7981 if (is_intersecting (powerup->get_bounds (), ship.get_bounds ())) {
8082 powerup->activate (*this );
8183 ++m_stats->player .powerups_collected ;
8284 }
8385 }
86+
87+ return ret;
8488}
8589
8690void Player::draw (Shader& shader) const {
87- if (!health. is_dead ()) {
88- m_exhaust. draw (shader);
91+ m_exhaust. draw (shader);
92+ if (!m_health. is_dead ()) {
8993 ship.draw (shader);
94+ m_shield.draw (shader);
9095 }
9196 m_arsenal.draw (shader);
9297 if (m_death) { m_death->draw (shader); }
@@ -103,14 +108,37 @@ void Player::set_controller(std::unique_ptr<IController> controller) {
103108 m_controller = std::move (controller);
104109}
105110
111+ void Player::set_shield (Seconds const ttl) {
112+ m_shield.ttl = ttl;
113+ m_shield.set_position (ship.transform .position );
114+ }
115+
106116void Player::on_death (Seconds const dt) {
107- health = 0 .0f ;
117+ m_health = 0 .0f ;
108118 m_death = m_death_source;
109119 m_death->set_position (ship.transform .position );
110120 m_death->tick (dt);
121+
122+ m_exhaust.config .respawn = false ;
123+
111124 ++m_stats->player .death_count ;
112125}
113126
127+ auto Player::check_hit (IDamageable& out, Rect<> const & hitbox, Seconds const dt) -> bool {
128+ if (m_shield.is_active ()) {
129+ if (is_intersecting (out.get_bounds (), m_shield.get_bounds ())) { out.force_death (); }
130+ return false ;
131+ }
132+
133+ if (is_intersecting (out.get_bounds (), hitbox)) {
134+ out.force_death ();
135+ on_death (dt);
136+ return true ;
137+ }
138+
139+ return false ;
140+ }
141+
114142void Player::do_inspect () {
115143 if constexpr (bave::imgui_v) {
116144 if (ImGui::TreeNodeEx (" Controller" , ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
@@ -126,8 +154,13 @@ void Player::do_inspect() {
126154 m_arsenal.get_weapon ().inspect ();
127155 ImGui::TreePop ();
128156 }
157+ if (ImGui::TreeNodeEx (" Shield" , ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
158+ auto ttl = m_shield.ttl .count ();
159+ if (ImGui::DragFloat (" ttl" , &ttl, 0 .25f , 0 .0f , 60 .0f , " %.2f" )) { m_shield.ttl = Seconds{ttl}; }
160+ ImGui::TreePop ();
161+ }
129162 if (ImGui::TreeNodeEx (" Status" , ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
130- health .inspect ();
163+ m_health .inspect ();
131164 ImGui::TreePop ();
132165 }
133166 }
0 commit comments