223 lines
6.4 KiB
C++
223 lines
6.4 KiB
C++
#include "Character.hpp"
|
|
|
|
Character::Character(const Configuration& configuration, std::map<std::string, sb::audio::Chunk>& audio) :
|
|
configuration(configuration), audio(audio) {}
|
|
|
|
void Character::profile(const std::string& name)
|
|
{
|
|
/* Store the name of the profile so Character::profile() will access the profile with this name */
|
|
_profile = name;
|
|
|
|
/* Reload the texture */
|
|
_sprite.clear_textures();
|
|
_sprite.texture(profile()["animation frames"][0]);
|
|
_sprite.scale(size);
|
|
}
|
|
|
|
const nlohmann::json& Character::profile() const
|
|
{
|
|
for (const nlohmann::json& profile : configuration("character", "profile"))
|
|
{
|
|
if (profile["name"] == _profile)
|
|
{
|
|
return profile;
|
|
}
|
|
}
|
|
|
|
/* Raise if the profile does not exist */
|
|
std::stringstream message;
|
|
message << "The profile " << _profile << " was not found in the configuration";
|
|
throw std::runtime_error(message.str());
|
|
}
|
|
|
|
void Character::box_size(float size)
|
|
{
|
|
_box.size(size * 2.0f * this->size);
|
|
}
|
|
|
|
void Character::load()
|
|
{
|
|
_sprite.load();
|
|
}
|
|
|
|
const Box& Character::box() const
|
|
{
|
|
return _box;
|
|
}
|
|
|
|
const sb::Sprite& Character::sprite() const
|
|
{
|
|
return _sprite;
|
|
}
|
|
|
|
void Character::beginning(const Curve& curve)
|
|
{
|
|
checkpoint(0.0f);
|
|
spawn(curve);
|
|
}
|
|
|
|
void Character::spawn(const Curve& curve)
|
|
{
|
|
next_point_index = curve.index(checkpoint());
|
|
position = curve[next_point_index];
|
|
speed = 0.0f;
|
|
accelerating = false;
|
|
_resting = true;
|
|
audio.at("walk").stop();
|
|
audio.at("reverse").stop();
|
|
}
|
|
|
|
void Character::checkpoint(float checkpoint)
|
|
{
|
|
_checkpoint = checkpoint;
|
|
}
|
|
|
|
float Character::checkpoint() const
|
|
{
|
|
return _checkpoint;
|
|
}
|
|
|
|
bool Character::at_end(const Curve& curve) const
|
|
{
|
|
return next_point_index > curve.length() - 1;
|
|
}
|
|
|
|
bool Character::resting() const
|
|
{
|
|
return _resting;
|
|
}
|
|
|
|
float Character::relative(const Curve& curve) const
|
|
{
|
|
return float(next_point_index) / curve.length();
|
|
}
|
|
|
|
void Character::update(const Curve& curve, const sb::Timer& timer, bool muted)
|
|
{
|
|
if (timer.frame() > 0.0f)
|
|
{
|
|
/* Adjust speed based on acceleration state and character profile. */
|
|
if (accelerating)
|
|
{
|
|
_resting = false;
|
|
|
|
/* Apply delta time to the speed increase. */
|
|
speed += timer.delta(profile()["speed increment"].get<float>()) + glm::abs(speed) * profile()["increment mod"].get<float>();
|
|
}
|
|
else
|
|
{
|
|
/* Apply delta time to the speed decrease. */
|
|
speed -= timer.delta(profile()["speed decrement"].get<float>()) + glm::abs(speed) * profile()["decrement mod"].get<float>();
|
|
}
|
|
|
|
/* Clamp speed, applying delta time to the limits */
|
|
float max_speed = timer.delta(profile()["max speed"].get<float>());
|
|
float min_speed = timer.delta(profile()["min speed"].get<float>());
|
|
speed = std::clamp(speed, min_speed, max_speed);
|
|
|
|
/* Calculate volume based on speed relative to max speed */
|
|
int volume;
|
|
if (speed >= 0.0f)
|
|
{
|
|
/* Only play walking forward effect. */
|
|
audio.at("reverse").stop();
|
|
if (!audio.at("walk").playing())
|
|
{
|
|
audio.at("walk").play(0.0f, walk_channel);
|
|
}
|
|
|
|
if (!muted)
|
|
{
|
|
/* Get louder closer to max speed using an exponential scale */
|
|
volume = std::round(std::pow(speed / max_speed, 3.0f) * static_cast<float>(MIX_MAX_VOLUME));
|
|
audio.at("walk").channel_volume(volume);
|
|
}
|
|
else
|
|
{
|
|
audio.at("walk").stop();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Only play walking backward effect. */
|
|
audio.at("walk").stop();
|
|
if (!audio.at("reverse").playing())
|
|
{
|
|
audio.at("reverse").play(0.0f, reverse_channel);
|
|
}
|
|
|
|
if (!muted)
|
|
{
|
|
/* Get louder closer to min speed using an exponential scale */
|
|
volume = std::round(std::pow(speed / min_speed, 3.0f) * static_cast<float>(MIX_MAX_VOLUME));
|
|
audio.at("reverse").channel_volume(volume);
|
|
}
|
|
else
|
|
{
|
|
audio.at("reverse").stop();
|
|
}
|
|
}
|
|
|
|
/* Move along unwrapped curve vertices */
|
|
float distance_remaining = std::abs(speed), distance = 0.0f;
|
|
glm::vec3 next_point, step;
|
|
while (distance_remaining)
|
|
{
|
|
if (speed < 0.0f && (relative(curve) <= 0.0f || (resting() && relative(curve) <= checkpoint())))
|
|
{
|
|
_resting = true;
|
|
audio.at("walk").stop();
|
|
audio.at("reverse").stop();
|
|
speed = 0.0f;
|
|
break;
|
|
}
|
|
else if (speed > 0.0f && next_point_index > curve.length() - 1)
|
|
{
|
|
speed = 0.0f;
|
|
break;
|
|
}
|
|
if (speed > 0.0f)
|
|
{
|
|
next_point = curve[next_point_index];
|
|
}
|
|
else
|
|
{
|
|
next_point = curve[next_point_index - 1];
|
|
}
|
|
distance = glm::distance(position, next_point);
|
|
if (distance < distance_remaining)
|
|
{
|
|
distance_remaining -= distance;
|
|
position = next_point;
|
|
next_point_index += speed < 0.0f ? -1 : 1;
|
|
}
|
|
else
|
|
{
|
|
step = glm::vec3{sb::Segment(position, next_point).step(distance_remaining), 0.0f};
|
|
position += step;
|
|
distance_remaining = 0;
|
|
}
|
|
}
|
|
|
|
/* Update collision box location. */
|
|
_box.south(position);
|
|
}
|
|
}
|
|
|
|
void Character::draw(const Curve& curve, GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
|
|
{
|
|
glm::vec2 translation = sb::wrap_point(glm::vec3{_box.center(), 0.0f}, {-curve.aspect, -1.0f, -1.0f}, {curve.aspect, 1.0f, 1.0f});
|
|
_sprite.translate(glm::vec3{translation, 0.0f});
|
|
_sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
|
|
|
/* Draw hitbox if requested */
|
|
if (configuration("display", "hitbox"))
|
|
{
|
|
sb::Sprite hitbox;
|
|
hitbox.scale(glm::vec3{_box.w / 2.0f});
|
|
hitbox.translate(glm::vec3{translation, 0.0f});
|
|
glUniform1i(texture_flag_uniform, false);
|
|
hitbox.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
|
}
|
|
}
|