Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added data/images/powerups/counter/counter-0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/powerups/counter/counter-9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions data/images/powerups/counter/counter.sprite
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
(supertux-sprite
(action
(name "default")
(fps 1.14) ;; 16 frames per 14 seconds
(images "counter-0.png"
"counter-1.png"
"counter-2.png"
"counter-3.png"
"counter-4.png"
"counter-5.png"
"counter-6.png"
"counter-7.png"
"counter-8.png"
"counter-9.png"
"counter-10.png"
"counter-11.png"
"counter-12.png"
"counter-13.png"
"counter-14.png"
"counter-15.png")))
39 changes: 38 additions & 1 deletion src/object/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "trigger/climbable.hpp"
#include "trigger/trigger_base.hpp"
#include "video/surface.hpp"
#include "supertux/counter.hpp"

#define SWIMMING

Expand Down Expand Up @@ -248,7 +249,8 @@ 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_was_crawling_before_slide(false)
m_was_crawling_before_slide(false),
m_counters()
{
m_name = name_;
m_idle_timer.start(static_cast<float>(TIME_UNTIL_IDLE) / 1000.0f);
Expand Down Expand Up @@ -293,6 +295,7 @@ Player::set_winning()
if (!is_winning()) {
m_winning = true;
m_invincible_timer.start(10000.0f);
set_invincible(false, COUNTER_STAR);
}
}

Expand Down Expand Up @@ -380,6 +383,29 @@ Player::trigger_sequence(Sequence seq, const SequenceData* data)
GameSession::current()->start_sequence(this, seq, data);
}

void
Player::set_invincible(bool invincible, CounterType type, float time_left)
{
for (int i = 0; i < m_counters.size(); i++)
{
if (m_counters[i].get_type() == type)
{
if (!invincible)
{
std::swap(m_counters[i], m_counters.back());
m_counters.pop_back();
}
else
m_counters[i].update_counter(invincible, time_left);

return;
}
}

if (invincible)
m_counters.push_back(Counter(type));
}

void
Player::update(float dt_sec)
{
Expand Down Expand Up @@ -782,6 +808,11 @@ Player::update(float dt_sec)
// when invincible, spawn particles
if (m_invincible_timer.started())
{
if (m_invincible_timer.get_timeleft() <= TUX_INVINCIBLE_TIME)
{
set_invincible(true, COUNTER_STAR, m_invincible_timer.get_timeleft());
}

if (graphicsRandom.rand(0, 2) == 0)
{
float px = graphicsRandom.randf(m_col.m_bbox.get_left() + 0, m_col.m_bbox.get_right() - 0);
Expand All @@ -800,6 +831,10 @@ Player::update(float dt_sec)
"dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS + 1 + 5);
}
}
else
{
set_invincible(false, COUNTER_STAR);
}

if (m_growing) {
if (m_sprite->animation_done())
Expand Down Expand Up @@ -2576,6 +2611,7 @@ Player::kill(bool completely)
m_post_damage_safety_timer.stop();
m_temp_safety_timer.stop();
m_invincible_timer.stop();
set_invincible(false, COUNTER_STAR);
m_physic.set_acceleration(0, 0);
m_physic.set_velocity(0, -700);
set_bonus(BONUS_NONE, true);
Expand Down Expand Up @@ -3084,6 +3120,7 @@ Player::multiplayer_prepare_spawn()
m_post_damage_safety_timer.stop();
m_temp_safety_timer.stop();
m_invincible_timer.stop();
set_invincible(false, COUNTER_STAR);
m_physic.set_acceleration(0, -9999);
m_physic.set_velocity(0, -9999);
m_dying = true;
Expand Down
4 changes: 4 additions & 0 deletions src/object/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "supertux/timer.hpp"
#include "video/layer.hpp"
#include "video/surface_ptr.hpp"
#include "supertux/counter.hpp"

#include <array>
#include <list>
Expand Down Expand Up @@ -515,6 +516,8 @@ class Player final : public MovingSprite

void stop_rolling(bool violent = true);

void set_invincible(bool invincible, CounterType type, float time_left = 0.f);

private:
int m_id;
std::unique_ptr<UID> m_target; /**< (Multiplayer) If not null, then the player does not exist in game and is offering the player to spawn at that player's position */
Expand Down Expand Up @@ -580,6 +583,7 @@ class Player final : public MovingSprite
public:
bool m_does_buttjump;
Timer m_invincible_timer;
std::vector<Counter> m_counters;

private:
Timer m_skidding_timer;
Expand Down
78 changes: 78 additions & 0 deletions src/supertux/counter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "supertux/counter.hpp"

#include "sprite/sprite_manager.hpp"
#include <algorithm>
#include "math/util.hpp"

static const CounterDef COUNTER_DEFS[COUNTER_COUNT] =
{
/* COUNTER_STAR */
{
14.f,
16,
"images/powerups/star/star.sprite",
"images/powerups/counter/counter.sprite",
"images/particles/sparkle.sprite"
}
};

const CounterDef& Counter::get_def(CounterType type)
{
return COUNTER_DEFS[type];
}

Counter::Counter(CounterType type) :
m_type(type),
m_counter_on(false),
m_total_time(get_def(type).total_time),
m_remaining_time(m_total_time),
m_num_parts(get_def(type).num_parts),
m_counter_idx(0),
m_effect_angle(-math::PI_2)
{

}

void
Counter::update_counter(bool counter, float remaining_time)
{
m_counter_on = counter;
m_remaining_time = remaining_time;

if (!m_counter_on)
{
m_remaining_time = 0.f;
return;
}

m_counter_idx = std::clamp(static_cast<int>(m_num_parts - m_remaining_time * m_num_parts / m_total_time),
0,
static_cast<int>(m_num_parts - 1));

float step_deg = 360.f / static_cast<float>(m_num_parts);
m_effect_angle = -math::PI_2 + math::radians(step_deg * static_cast<float>(m_counter_idx));
}

CounterType
Counter::get_type() const
{
return m_type;
}

bool
Counter::is_counter_on() const
{
return m_counter_on;
}

int
Counter::counter_stage() const
{
return m_counter_idx;
}

float
Counter::effect_angle() const
{
return m_effect_angle;
}
48 changes: 48 additions & 0 deletions src/supertux/counter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

#include <cstddef>
#include <vector>
#include <string>
#include "sprite/sprite.hpp"

enum CounterType {
COUNTER_STAR = 0, /*!< @description Invencible Star counter. */
/* Space for future clock types */

COUNTER_COUNT
};

struct CounterDef {
float total_time;
int num_parts; // number of segments in counter sprite
std::string center_sprite_path;
std::string counter_sprite_path;
std::string effect_sprite_path;
};

class Counter
{
public:
Counter(CounterType type);
void update_counter(bool counter, float remaining_time = 0.f);

CounterType get_type() const;
bool is_counter_on() const;
int counter_stage() const;
float effect_angle() const;

static const CounterDef& get_def(CounterType type);

public:
bool m_counter_on;

float m_total_time;
float m_remaining_time;

private:
CounterType m_type;

size_t m_num_parts;
int m_counter_idx;
float m_effect_angle;
};
73 changes: 73 additions & 0 deletions src/supertux/player_status_hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
#include "supertux/player_status_hud.hpp"

#include <iostream>
#include <cmath>

#include "gui/menu_manager.hpp"
#include "object/display_effect.hpp"
#include "sprite/sprite_manager.hpp"
#include "object/player.hpp"
#include "supertux/debug.hpp"
#include "supertux/game_object.hpp"
#include "supertux/level.hpp"
Expand All @@ -30,10 +32,14 @@
#include "supertux/screen_manager.hpp"
#include "supertux/game_session.hpp"
#include "supertux/sector.hpp"
#include "supertux/counter.hpp"
#include "supertux/globals.hpp"
#include "video/drawing_context.hpp"
#include "video/surface.hpp"
#include "editor/editor.hpp"
#include "worldmap/worldmap_sector.hpp"
#include "math/util.hpp"
#include "math/anchor_point.hpp"

static const int DISPLAYED_STAT_UNSET = -1;
static constexpr float ITEM_POCKET_TIME = 6.f;
Expand All @@ -47,6 +53,7 @@ PlayerStatusHUD::PlayerStatusHUD(PlayerStatus& player_status, bool show_tuxdolls
m_stat_value(show_tuxdolls ? m_player_status.tuxdolls : m_player_status.coins),
m_bonus_sprites(),
m_item_pocket_border(Surface::from_file("images/engine/hud/item_pocket.png")),
m_counters(),
m_item_pocket_fade()
{
m_player_status.set_hud_hint(this);
Expand All @@ -56,6 +63,17 @@ PlayerStatusHUD::PlayerStatusHUD(PlayerStatus& player_status, bool show_tuxdolls
m_bonus_sprites[BONUS_ICE] = SpriteManager::current()->create("images/powerups/iceflower/iceflower.sprite");
m_bonus_sprites[BONUS_AIR] = SpriteManager::current()->create("images/powerups/airflower/airflower.sprite");
m_bonus_sprites[BONUS_EARTH] = SpriteManager::current()->create("images/powerups/earthflower/earthflower.sprite");

m_counters.resize(COUNTER_COUNT);

for (int type = 0; type < COUNTER_COUNT; type++)
{
const CounterDef& def = Counter::get_def(static_cast<CounterType>(type));
m_counters[type].center_sprite = SpriteManager::current()->create(def.center_sprite_path);
m_counters[type].counter_sprite = SpriteManager::current()->create(def.counter_sprite_path);
m_counters[type].counter_parts = m_counters[type].counter_sprite->get_action_surfaces("default").value_or(std::vector<SurfacePtr>());
m_counters[type].effect_sprite = SpriteManager::current()->create(def.effect_sprite_path);
}
}

void
Expand Down Expand Up @@ -157,5 +175,60 @@ PlayerStatusHUD::draw(DrawingContext& context)
}
}

if (Sector::current()) // draw only if in level
{
for (auto& player : Sector::current()->get_players())
{
const std::vector<Counter>& counters = player->m_counters;

for (int i = 0; i < counters.size(); i++)
{
const Counter& counter = counters[i];
int type = counter.get_type();

if (!counter.is_counter_on() || !m_counters[type].counter_sprite)
continue;

float xpos = static_cast<float>((m_counters[type].counter_sprite->get_width() + 10) * (i + 1));
float ypos = static_cast<float>(m_counters[type].counter_sprite->get_height() * player->get_id());
Vector pos(BORDER_X + xpos, BORDER_Y + ypos);

context.color().draw_surface(m_counters[type].counter_parts[counter.counter_stage()], pos, LAYER_HUD);

if (!m_counters[type].center_sprite)
continue;

Vector center_sprite_pos = pos + Vector(21.f, 20.f);
m_counters[type].center_sprite->draw(context.color(), center_sprite_pos, LAYER_HUD);

if (!m_counters[type].effect_sprite)
continue;

Vector center_sprite_center = center_sprite_pos + Vector(
m_counters[type].center_sprite->get_width() * 0.5f,
m_counters[type].center_sprite->get_height() * 0.5f
);

float angle = counter.effect_angle();
Vector dir(std::cos(angle), std::sin(angle));
const float radius = 29.f;
Vector anchor_off = get_anchor_pos(m_counters[type].effect_sprite->get_current_hitbox(), ANCHOR_MIDDLE);

float pulse = 0.55f + 0.45f * std::sin(g_game_time * 3.0f);
context.set_alpha(pulse);

Vector base_pos = center_sprite_center + (dir * radius) - anchor_off;

for (int k = -2; k <= 2; ++k)
{
Vector draw_pos = base_pos + (dir * (k * 2.0f));
m_counters[type].effect_sprite->draw(context.color(), draw_pos, LAYER_HUD + 1);
}
context.set_alpha(1.0f);
}
}
}


context.pop_transform();
}
Loading
Loading