Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scriptable earthquake effect to Camera #2612

Merged
merged 2 commits into from
Aug 28, 2023
Merged
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
81 changes: 79 additions & 2 deletions src/object/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <math.h>
#include <physfs.h>

#include "math/random.hpp"
#include "math/util.hpp"
#include "object/player.hpp"
#include "supertux/level.hpp"
Expand Down Expand Up @@ -146,6 +147,11 @@ Camera::Camera(const std::string& name) :
m_shakespeed(),
m_shakedepth_x(),
m_shakedepth_y(),
m_earthquake(false),
m_earthquake_strength(),
m_earthquake_delay(),
m_earthquake_last_offset(0.f),
m_earthquake_delay_timer(),
m_scroll_from(0.0f, 0.0f),
m_scroll_goal(0.0f, 0.0f),
m_scroll_to_pos(),
Expand Down Expand Up @@ -179,6 +185,11 @@ Camera::Camera(const ReaderMapping& reader) :
m_shakespeed(),
m_shakedepth_x(),
m_shakedepth_y(),
m_earthquake(false),
m_earthquake_strength(),
m_earthquake_delay(),
m_earthquake_last_offset(0.f),
m_earthquake_delay_timer(),
m_scroll_from(0.0f, 0.0f),
m_scroll_goal(0.0f, 0.0f),
m_scroll_to_pos(),
Expand Down Expand Up @@ -303,6 +314,36 @@ Camera::shake(float duration, float x, float y)
m_shakespeed = math::PI_2 / duration;
}

void
Camera::start_earthquake(float strength, float delay)
{
if (strength <= 0.f)
{
log_warning << "Invalid earthquake strength value provided. Setting to 3." << std::endl;
strength = 3.f;
}
if (delay <= 0.f)
{
log_warning << "Invalid earthquake delay value provided. Setting to 0.05." << std::endl;
delay = 0.05f;
}

m_earthquake = true;
m_earthquake_strength = strength;
m_earthquake_delay = delay;
}

void
Camera::stop_earthquake()
{
m_translation.y -= m_earthquake_last_offset;
m_cached_translation.y -= m_earthquake_last_offset;

m_earthquake = false;
m_earthquake_last_offset = 0.f;
m_earthquake_delay_timer.stop();
}

void
Camera::scroll_to(const Vector& goal, float scrolltime)
{
Expand Down Expand Up @@ -360,7 +401,8 @@ Camera::update(float dt_sec)
}

update_scale(dt_sec);
shake();
update_shake();
update_earthquake();
}

void
Expand All @@ -383,18 +425,24 @@ Camera::keep_in_bounds(Vector& translation_)
float width = d_sector->get_width();
float height = d_sector->get_height();

// Remove any earthquake offset from the translation.
translation_.y -= m_earthquake_last_offset;

// Don't scroll before the start or after the level's end.
translation_.x = math::clamp(translation_.x, 0.0f, width - static_cast<float>(m_screen_size.width));
translation_.y = math::clamp(translation_.y, 0.0f, height - static_cast<float>(m_screen_size.height));

// Add any earthquake offset we may have removed earlier.
translation_.y += m_earthquake_last_offset;

if (height < static_cast<float>(m_screen_size.height))
translation_.y = height / 2.0f - static_cast<float>(m_screen_size.height) / 2.0f;
if (width < static_cast<float>(m_screen_size.width))
translation_.x = width / 2.0f - static_cast<float>(m_screen_size.width) / 2.0f;
}

void
Camera::shake()
Camera::update_shake()
{
if (m_shaketimer.started()) {

Expand All @@ -412,6 +460,35 @@ Camera::shake()
}
}

void
Camera::update_earthquake()
{
if (!m_earthquake)
return;

if (m_earthquake_delay_timer.check())
{
if (m_earthquake_last_offset == 0.f)
{
m_earthquake_last_offset = m_earthquake_strength * graphicsRandom.randf(-2, 2);
m_translation.y += m_earthquake_last_offset;
m_cached_translation.y += m_earthquake_last_offset;
}
else
{
m_translation.y -= m_earthquake_last_offset;
m_cached_translation.y -= m_earthquake_last_offset;
m_earthquake_last_offset = 0.f;
}

m_earthquake_delay_timer.start(m_earthquake_delay + static_cast<float>(graphicsRandom.rand(0, 1)));
Vankata453 marked this conversation as resolved.
Show resolved Hide resolved
}
else if (!m_earthquake_delay_timer.started())
{
m_earthquake_delay_timer.start(m_earthquake_delay + static_cast<float>(graphicsRandom.rand(0, 1)));
Vankata453 marked this conversation as resolved.
Show resolved Hide resolved
}
}

void
Camera::update_scroll_normal(float dt_sec)
{
Expand Down
16 changes: 15 additions & 1 deletion src/object/camera.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ class Camera final : public GameObject,
/** shake camera in a direction 1 time */
void shake(float duration, float x, float y);

/** Shake the camera vertically with a specified average strength, at a certain minimal delay, until stopped. */
void start_earthquake(float strength, float delay);
void stop_earthquake();

/** scroll the upper left edge of the camera in scrolltime seconds
to the position goal */
void scroll_to(const Vector& goal, float scrolltime);
Expand Down Expand Up @@ -124,9 +128,12 @@ class Camera final : public GameObject,
void update_scroll_normal_multiplayer(float dt_sec);
void update_scroll_autoscroll(float dt_sec);
void update_scroll_to(float dt_sec);

void update_scale(float dt_sec);
void update_shake();
void update_earthquake();

void keep_in_bounds(Vector& vector);
void shake();

private:
Mode m_mode;
Expand All @@ -149,6 +156,13 @@ class Camera final : public GameObject,
float m_shakedepth_x;
float m_shakedepth_y;

// Earthquake
bool m_earthquake;
float m_earthquake_strength,
m_earthquake_delay,
m_earthquake_last_offset;
Timer m_earthquake_delay_timer;

// scrollto mode
Vector m_scroll_from;
Vector m_scroll_goal;
Expand Down
20 changes: 18 additions & 2 deletions src/scripting/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,27 @@ Camera::reload_config()
}

void
Camera::shake(float speed, float x, float y)
Camera::shake(float duration, float x, float y)
{
SCRIPT_GUARD_VOID;
BIND_SECTOR(::Sector::get());
object.shake(speed, x, y);
object.shake(duration, x, y);
}

void
Camera::start_earthquake(float strength, float delay)
{
SCRIPT_GUARD_VOID;
BIND_SECTOR(::Sector::get());
object.start_earthquake(strength, delay);
}

void
Camera::stop_earthquake()
{
SCRIPT_GUARD_VOID;
BIND_SECTOR(::Sector::get());
object.stop_earthquake();
}

void
Expand Down
16 changes: 13 additions & 3 deletions src/scripting/camera.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,22 @@ class Camera final
void reload_config();

/**
* Moves camera to the given coordinates in ""time"" seconds, returning quickly to the original position afterwards.
* @param float $speed
* Shakes the camera in a certain direction only 1 time.
* @param float $duration
* @param float $x
* @param float $y
*/
void shake(float speed, float x, float y);
void shake(float duration, float x, float y);
/**
* Starts "earthquake" mode, which shakes the camera vertically with a specified average ""strength"", at a certain minimal ""delay"", until stopped.
* @param float $strength
* @param float $delay
*/
void start_earthquake(float strength, float delay);
/**
* Stops "earthquake" mode.
*/
void stop_earthquake();
/**
* Moves the camera to the specified absolute position. The origin is at the top left.
* @param float $x
Expand Down
74 changes: 74 additions & 0 deletions src/scripting/wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,66 @@ static SQInteger Camera_shake_wrapper(HSQUIRRELVM vm)

}

static SQInteger Camera_start_earthquake_wrapper(HSQUIRRELVM vm)
{
SQUserPointer data;
if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) {
sq_throwerror(vm, _SC("'start_earthquake' called without instance"));
return SQ_ERROR;
}
scripting::Camera* _this = reinterpret_cast<scripting::Camera*> (data);

SQFloat arg0;
if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
sq_throwerror(vm, _SC("Argument 1 not a float"));
return SQ_ERROR;
}
SQFloat arg1;
if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
sq_throwerror(vm, _SC("Argument 2 not a float"));
return SQ_ERROR;
}

try {
_this->start_earthquake(arg0, arg1);

return 0;

} catch(std::exception& e) {
sq_throwerror(vm, e.what());
return SQ_ERROR;
} catch(...) {
sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_earthquake'"));
return SQ_ERROR;
}

}

static SQInteger Camera_stop_earthquake_wrapper(HSQUIRRELVM vm)
{
SQUserPointer data;
if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) {
sq_throwerror(vm, _SC("'stop_earthquake' called without instance"));
return SQ_ERROR;
}
scripting::Camera* _this = reinterpret_cast<scripting::Camera*> (data);


try {
_this->stop_earthquake();

return 0;

} catch(std::exception& e) {
sq_throwerror(vm, e.what());
return SQ_ERROR;
} catch(...) {
sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_earthquake'"));
return SQ_ERROR;
}

}

static SQInteger Camera_set_pos_wrapper(HSQUIRRELVM vm)
{
SQUserPointer data;
Expand Down Expand Up @@ -14123,6 +14183,20 @@ void register_supertux_wrapper(HSQUIRRELVM v)
throw SquirrelError(v, "Couldn't register function 'shake'");
}

sq_pushstring(v, "start_earthquake", -1);
sq_newclosure(v, &Camera_start_earthquake_wrapper, 0);
sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, ".b|nb|n");
if(SQ_FAILED(sq_createslot(v, -3))) {
throw SquirrelError(v, "Couldn't register function 'start_earthquake'");
}

sq_pushstring(v, "stop_earthquake", -1);
sq_newclosure(v, &Camera_stop_earthquake_wrapper, 0);
sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, ".");
if(SQ_FAILED(sq_createslot(v, -3))) {
throw SquirrelError(v, "Couldn't register function 'stop_earthquake'");
}

sq_pushstring(v, "set_pos", -1);
sq_newclosure(v, &Camera_set_pos_wrapper, 0);
sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, ".b|nb|n");
Expand Down
Loading