- add coins to ending
- add animation frames to sprites - track best quest time - hue shift effect on coin
45
config.json
|
@ -16,7 +16,7 @@
|
|||
"checkpoint distance": 0.1,
|
||||
"clock hud scale": [0.15, 0.04],
|
||||
"clock hud translation": [1.6, 0.92],
|
||||
"clock hud large scale": [1.0, 0.35],
|
||||
"clock hud large scale": [0.65, 0.2],
|
||||
"clock hud large translation": [0.0, 0.5],
|
||||
"clock hud foreground": [255.0, 255.0, 255.0, 255.0],
|
||||
"clock hud background": [0.0, 0.0, 0.0, 60.0],
|
||||
|
@ -51,7 +51,17 @@
|
|||
"qr texture": "resource/qr.png",
|
||||
"qr translation": [1.49, -0.7],
|
||||
"qr scale": [0.205, 0.225],
|
||||
"end screen timeout": 40.0
|
||||
"end screen timeout": 40.0,
|
||||
"end screen timeout": 10040.0,
|
||||
"enemy sprite scale": 0.024691358,
|
||||
"quest best text": "BEST ",
|
||||
"quest best scale": [0.31, 0.04],
|
||||
"quest best translation": [-1.42, 0.92],
|
||||
"quest best dimensions": [160.0, 19.0],
|
||||
"quest best foreground": [255.0, 255.0, 255.0, 255.0],
|
||||
"quest best background": [0.0, 0.0, 0.0, 60.0],
|
||||
"hue shift": 10.0,
|
||||
"hue shift frequency": 0.025
|
||||
},
|
||||
|
||||
"configuration":
|
||||
|
@ -111,6 +121,12 @@
|
|||
}
|
||||
},
|
||||
|
||||
"texture":
|
||||
{
|
||||
"coin": ["resource/coin/coin-0.png"],
|
||||
"flame": ["resource/flame/flame-1.png", "resource/flame/flame-2.png"]
|
||||
},
|
||||
|
||||
"curve":
|
||||
{
|
||||
"bezier resolution": 60
|
||||
|
@ -276,15 +292,21 @@
|
|||
"name": "RESUME ARCADE",
|
||||
"time limit": 30.0,
|
||||
"checkpoint addition": 10.0,
|
||||
"checkpoint addition advanced": 15.0,
|
||||
"level addition": 20.0,
|
||||
"bank bonus": 5.0
|
||||
"level addition advanced": 30.0,
|
||||
"bank bonus": 5.0,
|
||||
"advanced": 14
|
||||
},
|
||||
{
|
||||
"name": "ARCADE",
|
||||
"time limit": 30.0,
|
||||
"checkpoint addition": 10.0,
|
||||
"checkpoint addition advanced": 15.0,
|
||||
"level addition": 20.0,
|
||||
"bank bonus": 5.0
|
||||
"level addition advanced": 30.0,
|
||||
"bank bonus": 5.0,
|
||||
"advanced": 14
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -311,18 +333,25 @@
|
|||
"main": "resource/azu main theme_amp.ogg",
|
||||
"menu": "resource/azu menu music_amp.ogg",
|
||||
"take": "resource/Coin_.wav",
|
||||
"checkpoint": "resource/arrive_0.wav"
|
||||
"checkpoint": "resource/arrive_0.wav",
|
||||
"bong": "resource/bong.ogg"
|
||||
},
|
||||
"volume": {
|
||||
"restart": 0.5,
|
||||
"restart": 0.3,
|
||||
"teleport": 0.7,
|
||||
"walk": 0.5,
|
||||
"reverse": 0.46,
|
||||
"walk": 0.55,
|
||||
"reverse": 0.5,
|
||||
"main": 1.0,
|
||||
"menu": 1.0,
|
||||
"take": 0.5,
|
||||
"checkpoint": 0.5
|
||||
},
|
||||
"fade": 2.0
|
||||
},
|
||||
|
||||
"ending":
|
||||
{
|
||||
"coin range": [-0.5, 0.5],
|
||||
"coin y": 0.07
|
||||
}
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 5.3 KiB |
|
@ -4,6 +4,7 @@
|
|||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
<style>
|
||||
@import url("../../../lib/fonts/rounded-mplus/2m-medium/style.css");
|
||||
@import url("../../../lib/fonts/rounded-mplus/2p-thin/style.css ");
|
||||
|
|
2
lib/sb
|
@ -1 +1 @@
|
|||
Subproject commit 0a42df9a0029164448d600ec8270268382c4a438
|
||||
Subproject commit a5f55be257eee0886c4fdd9287c9d91c9de22d8d
|
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 250 B |
After Width: | Height: | Size: 164 B |
|
@ -423,7 +423,7 @@
|
|||
["fish", 0.41, -0.03, 80.0, 4.73],
|
||||
|
||||
["slicer", 0.135, 2.5, 30.0],
|
||||
["slicer", 0.57, 5.0, 100.0],
|
||||
["slicer", 0.57, 5.0, 100.0, 0.6, 2.65],
|
||||
|
||||
["projector", [64, 120, 0.0], 5.5, 6.5, 0.6],
|
||||
["projector", [800, 120, 0.0], 5.5, 6.5, 0.6],
|
||||
|
@ -432,8 +432,8 @@
|
|||
],
|
||||
"checkpoints": [
|
||||
{
|
||||
"position": 0.245,
|
||||
"angle": 4.4
|
||||
"position": 0.34,
|
||||
"angle": 5.2
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -482,7 +482,7 @@
|
|||
[ 432.0, 34.0], [ 784.0, 98.0], [ 738.0, 372.0], [ 432.0, 451.0], [ 95.0, 367.0], [ 105.0, 86.0],
|
||||
[ 432.0, 34.0], [ 964.25, 49.13], [ 794.44, 464.65], [ 432.0, 451.0], [ 1.8, 450.48], [ -61.82, 30.06],
|
||||
[ 432.0, 34.0], [ 1065.74, 32.26], [ 896.07, 452.75], [ 432.0, 451.0], [ 4.34, 466.9], [ -228.42, 53.41],
|
||||
[ 432.0, 34.0], [ 432.0, 34.0], [ 432.0, 261.0], [ 432.0, 261.0]
|
||||
[ 432.0, 34.0], [ 432.0, 34.0], [ 433.0, 261.0], [ 433.0, 261.0]
|
||||
],
|
||||
"enemies": [
|
||||
["projector", [64, 64, 0.0], 6.00000, 7.0, 0.6],
|
||||
|
@ -515,7 +515,7 @@
|
|||
["fish", 0.6, 0.025, 120, 0.0],
|
||||
|
||||
["fish", 0.499, 0.025, 40, 0.0],
|
||||
["fish", 0.499, 0.025, 60, 0.79],
|
||||
["fish", 0.499, 0.025, 60, 0.79, 3.14],
|
||||
["fish", 0.499, 0.025, 80, 1.57],
|
||||
["fish", 0.499, 0.025, 100, 2.36],
|
||||
["fish", 0.499, 0.025, 120, 3.14],
|
||||
|
@ -589,8 +589,8 @@
|
|||
],
|
||||
"checkpoints": [
|
||||
{
|
||||
"position": 0.528,
|
||||
"angle": 0.5
|
||||
"position": 0.6661,
|
||||
"angle": 0.0
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -633,7 +633,7 @@
|
|||
["slicer", 0.83, 8.00000, 90.50000],
|
||||
["slicer", 0.426, 5.00000, 70.50000],
|
||||
["slicer", 0.43, 5.00000, 70.50000],
|
||||
["slicer", 0.434, 5.00000, 70.50000],
|
||||
["slicer", 0.434, 5.00000, 70.50000, 0.075, 3.14],
|
||||
["slicer", 0.4535, 2.00000, 32.5],
|
||||
["fish", 0.4535, -0.1, 30.00000, 0.0],
|
||||
["fish", 0.95, -0.05, 25.00000, 0.0],
|
||||
|
@ -791,7 +791,7 @@
|
|||
["flame", [0.8175, 0.59, 0.0], 0.15, 1.5707963267948966, 2.3],
|
||||
["flame", [1.17, 0.59, 0.0], 0.15, -1.5707963267948966, 2.3],
|
||||
|
||||
["flame", [1.15, 0.2, 0.0], 0.15, -1.5707963267948966, 2.3],
|
||||
["flame", [1.15, 0.2, 0.0], 0.15, -1.5707963267948966, 2.3, 0.5, 3.14],
|
||||
|
||||
["flame", [0.9875, -0.075, 0.0], 0.15, 3.141592653589793, 2.3],
|
||||
|
||||
|
@ -833,7 +833,7 @@
|
|||
|
||||
"checkpoints": [
|
||||
{
|
||||
"position": 0.4175,
|
||||
"position": 0.636,
|
||||
"angle": 4.0
|
||||
}
|
||||
]
|
||||
|
|
After Width: | Height: | Size: 297 B |
After Width: | Height: | Size: 290 B |
After Width: | Height: | Size: 301 B |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 189 B |
After Width: | Height: | Size: 4.8 KiB |
175
src/Cakefoot.cpp
|
@ -46,6 +46,7 @@ Cakefoot::Cakefoot()
|
|||
{"quest time", 0.0f},
|
||||
{"quest coin", false},
|
||||
{"quest bank", 0},
|
||||
{"quest best", 0.0},
|
||||
{"arcade level", 1},
|
||||
{"arcade checkpoint", 0.0f},
|
||||
{"arcade difficulty", 0},
|
||||
|
@ -173,6 +174,10 @@ Cakefoot::Cakefoot()
|
|||
/* Initialize scoreboard content */
|
||||
refresh_scoreboard();
|
||||
|
||||
/* Start tracking hue rotation */
|
||||
shift_hue_animation.frame_length(configuration()("display", "hue shift frequency"));
|
||||
shift_hue_animation.play();
|
||||
|
||||
if (!use_play_button)
|
||||
{
|
||||
/* Load title screen */
|
||||
|
@ -809,6 +814,20 @@ void Cakefoot::set_up_hud()
|
|||
qr_code.texture(configuration()("display", "qr texture").get<std::string>());
|
||||
qr_code.translate(configuration()("display", "qr translation"));
|
||||
qr_code.scale(configuration()("display", "qr scale"));
|
||||
|
||||
/* Style the quest best time indicator */
|
||||
label.at("quest best").foreground(configuration()("display", "quest best foreground").get<glm::vec4>());
|
||||
label.at("quest best").background(configuration()("display", "quest best background").get<glm::vec4>());
|
||||
label.at("quest best").untransform();
|
||||
label.at("quest best").translate(configuration()("display", "quest best translation"));
|
||||
label.at("quest best").scale(configuration()("display", "quest best scale"));
|
||||
label.at("quest best").dimensions(configuration()("display", "quest best dimensions"));
|
||||
if (configuration()("progress", "quest best") > 0.0f)
|
||||
{
|
||||
label.at("quest best").content(configuration()("display", "quest best text").get<std::string>() +
|
||||
format_clock(configuration()("progress", "quest best")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Cakefoot::load_vbo()
|
||||
|
@ -1033,10 +1052,23 @@ void Cakefoot::load_level(int index)
|
|||
}
|
||||
}
|
||||
|
||||
/* If the level is the end screen, reset the player's current level to the beginning. Otherwise, if the level is not the title screen, save it as
|
||||
* the current level in the player's progress. Also save the newly assigned current level in the level select index. */
|
||||
if (static_cast<std::size_t>(index) == configuration()("levels").size() - 1)
|
||||
/* If the level is the end screen, reset the player's current level to the beginning and load ending screen coin list. Otherwise, if the level is not
|
||||
* the title screen, save it as the current level in the player's progress. Also save the newly assigned current level in the level select index. */
|
||||
if (end_screen())
|
||||
{
|
||||
/* Load ending coins */
|
||||
std::string coin_texture;
|
||||
ending_coins.clear();
|
||||
glm::vec2 coin_range = configuration()("ending", "coin range").get<glm::vec2>();
|
||||
float coin_step = (coin_range.y - coin_range.x) / (bank() - 1);
|
||||
for (std::size_t ii = 0; ii < bank(); ii++)
|
||||
{
|
||||
Flame coin {
|
||||
field, glm::vec3{coin_range.x + coin_step * ii, configuration()("ending", "coin y").get<float>(), 0.0f}, 0.0f, 0.0f, -1.0f, bank() < max_bank()
|
||||
};
|
||||
ending_coins.push_back(coin);
|
||||
}
|
||||
|
||||
configuration()["progress"]["current level"] = 1;
|
||||
|
||||
/* Update save progress */
|
||||
|
@ -1061,12 +1093,23 @@ void Cakefoot::load_level(int index)
|
|||
}
|
||||
|
||||
/* In quest mode, unlock higher difficulty at the end of the game: increase difficulty if there is a higher difficulty than the current one.
|
||||
* Then reset the max level because it will now refer to max level of the next difficulty. */
|
||||
if (quest() && profile_index < static_cast<int>(configuration()("character", "profile").size()) - 1)
|
||||
* Then reset the max level because it will now refer to max level of the next difficulty. Save the time if it is better than the existing
|
||||
* record. */
|
||||
if (quest())
|
||||
{
|
||||
configuration()["progress"]["max difficulty"] = ++profile_index;
|
||||
configuration()["progress"]["max level"] = 1;
|
||||
character.profile(configuration()("character", "profile", profile_index, "name"));
|
||||
if (profile_index < static_cast<int>(configuration()("character", "profile").size()) - 1)
|
||||
{
|
||||
configuration()["progress"]["max difficulty"] = ++profile_index;
|
||||
configuration()["progress"]["max level"] = 1;
|
||||
character.profile(configuration()("character", "profile", profile_index, "name"));
|
||||
}
|
||||
|
||||
float best = configuration()("progress", "quest best");
|
||||
if (best <= 0.0f || run_timer.elapsed() < best)
|
||||
{
|
||||
configuration()["progress"]["quest best"] = run_timer.elapsed();
|
||||
label.at("quest best").content(configuration()("display", "quest best text").get<std::string>() + " " + format_clock(run_timer.elapsed()));
|
||||
}
|
||||
}
|
||||
|
||||
write_progress();
|
||||
|
@ -1133,7 +1176,7 @@ void Cakefoot::load_level(int index)
|
|||
}
|
||||
|
||||
/* If it's the title or end level, stop the run timer. */
|
||||
if (index == 0 || static_cast<std::size_t>(index) == _configuration("levels").size() - 1)
|
||||
if (index == 0 || end_screen())
|
||||
{
|
||||
run_timer.off();
|
||||
|
||||
|
@ -1304,18 +1347,25 @@ float Cakefoot::limit() const
|
|||
{
|
||||
const nlohmann::json& levels {configuration()("levels")};
|
||||
for (auto level = levels.begin() + 1; level != levels.begin() + level_index + 1; level++) {
|
||||
std::string level_addition = "level addition";
|
||||
std::string checkpoint_addition = "checkpoint addition";
|
||||
if (level >= levels.begin() + configuration()("challenge", challenge_index, "advanced").get<int>())
|
||||
{
|
||||
level_addition += " advanced";
|
||||
checkpoint_addition += " advanced";
|
||||
}
|
||||
if (level < levels.begin() + level_index)
|
||||
{
|
||||
limit += configuration()("challenge", challenge_index, "level addition").get<float>();
|
||||
limit += configuration()("challenge", challenge_index, level_addition).get<float>();
|
||||
if (level->contains("checkpoints"))
|
||||
limit += configuration()("challenge", challenge_index, "checkpoint addition").get<float>() * level->at("checkpoints").size();
|
||||
limit += configuration()("challenge", challenge_index, checkpoint_addition).get<float>() * level->at("checkpoints").size();
|
||||
}
|
||||
else if (level->contains("checkpoints"))
|
||||
{
|
||||
for (const nlohmann::json& checkpoint : level->at("checkpoints"))
|
||||
{
|
||||
if (checkpoint.at("position").get<float>() <= character.checkpoint())
|
||||
limit += configuration()("challenge", challenge_index, "checkpoint addition").get<float>();
|
||||
limit += configuration()("challenge", challenge_index, checkpoint_addition).get<float>();
|
||||
} } }
|
||||
|
||||
/* Add bank bonus */
|
||||
|
@ -1342,11 +1392,31 @@ bool Cakefoot::level_select() const
|
|||
return !arcade() && configuration()("challenge", challenge_index, "name") == "LEVEL SELECT";
|
||||
}
|
||||
|
||||
bool Cakefoot::end_screen(std::optional<std::size_t> index) const
|
||||
{
|
||||
if (!index.has_value())
|
||||
{
|
||||
index = level_index;
|
||||
}
|
||||
return static_cast<std::size_t>(index.value()) == _configuration("levels").size() - 1;
|
||||
}
|
||||
|
||||
bool Cakefoot::resuming() const
|
||||
{
|
||||
return configuration()("challenge", challenge_index, "name") == "RESUME QUEST" || configuration()("challenge", challenge_index, "name") == "RESUME ARCADE";
|
||||
}
|
||||
|
||||
std::size_t Cakefoot::bank() const
|
||||
{
|
||||
std::string mode = quest() ? "quest bank" : "arcade bank";
|
||||
return configuration()("progress", mode).get<std::size_t>();
|
||||
}
|
||||
|
||||
std::size_t Cakefoot::max_bank() const
|
||||
{
|
||||
return configuration()("levels").size() - 2;
|
||||
}
|
||||
|
||||
void Cakefoot::collect_coin(bool add_to_bank)
|
||||
{
|
||||
if (!coin_collected)
|
||||
|
@ -1394,6 +1464,11 @@ void Cakefoot::submit_score()
|
|||
load_level(0);
|
||||
}
|
||||
|
||||
void Cakefoot::shift_hue()
|
||||
{
|
||||
rotating_hue.shift_hue(configuration()("display", "hue shift").get<float>());
|
||||
}
|
||||
|
||||
std::string Cakefoot::format_clock(float amount)
|
||||
{
|
||||
int minutes = int(amount) / 60;
|
||||
|
@ -1739,6 +1814,7 @@ void Cakefoot::update(float timestamp)
|
|||
/* Keep animation time updated */
|
||||
game_over_animation.update(timestamp);
|
||||
submit_score_animation.update(timestamp);
|
||||
shift_hue_animation.update(timestamp);
|
||||
|
||||
/* Transformation for looking at the center of the field of play from the camera position. */
|
||||
view = glm::lookAt(camera_position, {0.0f, 0.0f, 0.0f}, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||
|
@ -1755,6 +1831,9 @@ void Cakefoot::update(float timestamp)
|
|||
}
|
||||
projection = glm::perspective(fov, window_box().aspect(), 0.1f, 100.0f);
|
||||
|
||||
/* Transformation that applies the rotation state of the entire scene */
|
||||
glm::mat4 rotation_matrix = glm::rotate(glm::mat4(1), rotation.x, {0.0f, 1.0f, 0.0f}) * glm::rotate(glm::mat4(1), rotation.y, {1.0f, 0.0f, 0.0f});
|
||||
|
||||
/* Clear screen to configured color */
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
@ -1786,7 +1865,8 @@ void Cakefoot::update(float timestamp)
|
|||
}
|
||||
|
||||
/* End run if there is a time limit and the time limit is passed. Queue end level to load after a delay. */
|
||||
bool game_over_active = arcade() && run_timer.elapsed() > extended_limit && static_cast<std::size_t>(level_index) < configuration()("levels").size() - 1;
|
||||
bool game_over_active = arcade() && level_index > 0 &&
|
||||
run_timer.elapsed() > extended_limit && static_cast<std::size_t>(level_index) < configuration()("levels").size() - 1;
|
||||
if (game_over_active && !game_over_animation.playing())
|
||||
{
|
||||
/* Play once with a delay to let the game over screen display temporarily before the end level is loaded. */
|
||||
|
@ -1837,9 +1917,9 @@ void Cakefoot::update(float timestamp)
|
|||
/* Collect any previously taken coins */
|
||||
collect_coin();
|
||||
|
||||
/* Load next level */
|
||||
/* Load next level, or reload current level if in level select mode */
|
||||
audio.at("teleport").play();
|
||||
load_level(level_index + 1);
|
||||
load_level(level_select() ? level_index : level_index + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1888,6 +1968,25 @@ void Cakefoot::update(float timestamp)
|
|||
}
|
||||
}
|
||||
|
||||
/* Collide with ending screen coins */
|
||||
if (end_screen())
|
||||
{
|
||||
ending_coins.erase(
|
||||
std::remove_if(
|
||||
ending_coins.begin(),
|
||||
ending_coins.end(),
|
||||
[&](Flame& coin)
|
||||
{
|
||||
if (coin.collide(character.box(), character.sprite(), {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}))
|
||||
{
|
||||
coin.camo() ? audio.at("take").play() : audio.at("bong").play();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
ending_coins.end());
|
||||
}
|
||||
|
||||
/* Respawn */
|
||||
if (!character.resting() && enemy_collision)
|
||||
{
|
||||
|
@ -1905,9 +2004,6 @@ void Cakefoot::update(float timestamp)
|
|||
}
|
||||
}
|
||||
|
||||
/* Transformation that applies the rotation state of the entire scene */
|
||||
glm::mat4 rotation_matrix = glm::rotate(glm::mat4(1), rotation.x, {0.0f, 1.0f, 0.0f}) * glm::rotate(glm::mat4(1), rotation.y, {1.0f, 0.0f, 0.0f});
|
||||
|
||||
/* Plane position vertices will be used for everything before the curve */
|
||||
sb::Plane::position->bind("vertex_position", shader_program);
|
||||
|
||||
|
@ -1920,6 +2016,7 @@ void Cakefoot::update(float timestamp)
|
|||
playing_field.attributes("color")->disable();
|
||||
|
||||
/* Reset color addition, and draw curve. */
|
||||
glUniform4fv(uniform["color addition"], 1, &glm::vec4(0)[0]);
|
||||
glm::mat4 vp = projection * view * rotation_matrix;
|
||||
glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &vp[0][0]);
|
||||
curve().color.bind("vertex_color", shader_program);
|
||||
|
@ -1960,11 +2057,20 @@ void Cakefoot::update(float timestamp)
|
|||
/* Draw enemies */
|
||||
for (auto& enemy : enemies)
|
||||
{
|
||||
enemy->draw(uniform["mvp"], view * rotation_matrix, projection, uniform["texture enabled"]);
|
||||
enemy->draw(uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"), rotating_hue, uniform.at("color addition"));
|
||||
}
|
||||
|
||||
/* Draw cake */
|
||||
character.draw(curve(), uniform["mvp"], view * rotation_matrix, projection, uniform["texture enabled"]);
|
||||
character.draw(curve(), uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"));
|
||||
|
||||
/* Draw end screen coins */
|
||||
if (end_screen())
|
||||
{
|
||||
for (Flame& coin : ending_coins)
|
||||
{
|
||||
coin.draw(uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"), rotating_hue, uniform.at("color addition"));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if any buttons should be disabled */
|
||||
bool profile_spinner_enabled = configuration()("progress", "max difficulty") > 0 && !resuming();
|
||||
|
@ -2011,10 +2117,7 @@ void Cakefoot::update(float timestamp)
|
|||
glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &label_transformation[0][0]);
|
||||
label.at(name).enable();
|
||||
glDrawArrays(GL_TRIANGLES, 0, label.at(name).attributes("position")->count());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} } } }
|
||||
else
|
||||
{
|
||||
if (unpaused_timer)
|
||||
|
@ -2026,8 +2129,7 @@ void Cakefoot::update(float timestamp)
|
|||
for (std::string name : {"resume", "reset"})
|
||||
{
|
||||
button.at(name).draw(uniform["mvp"], view, projection, uniform["texture enabled"]);
|
||||
}
|
||||
}
|
||||
} }
|
||||
|
||||
/* Draw name entry */
|
||||
if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 && arcade())
|
||||
|
@ -2037,9 +2139,7 @@ void Cakefoot::update(float timestamp)
|
|||
"name " + std::to_string(name_entry_index + 1) + " decrement"})
|
||||
{
|
||||
button.at(button_name).draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} } }
|
||||
|
||||
/* Draw the clock */
|
||||
float amount;
|
||||
|
@ -2100,10 +2200,9 @@ void Cakefoot::update(float timestamp)
|
|||
glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &label_transformation[0][0]);
|
||||
label.at(name).enable();
|
||||
glDrawArrays(GL_TRIANGLES, 0, label.at(name).attributes("position")->count());
|
||||
}
|
||||
}
|
||||
} }
|
||||
|
||||
/* Draw scoreboard and QR on title screen */
|
||||
/* Draw scoreboard, QR, and quest best on title screen */
|
||||
if (level_index == 0)
|
||||
{
|
||||
/* Only draw scoreboard if arcade mode is selected */
|
||||
|
@ -2115,6 +2214,14 @@ void Cakefoot::update(float timestamp)
|
|||
scoreboard.enable();
|
||||
glDrawArrays(GL_TRIANGLES, 0, scoreboard.attributes("position")->count());
|
||||
}
|
||||
else if (quest() && configuration()("progress", "quest best") > 0.0f)
|
||||
{
|
||||
label.at("quest best").texture(0).bind();
|
||||
label_transformation = projection * view * label.at("quest best").transformation();
|
||||
glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &label_transformation[0][0]);
|
||||
label.at("quest best").enable();
|
||||
glDrawArrays(GL_TRIANGLES, 0, label.at("quest best").attributes("position")->count());
|
||||
}
|
||||
|
||||
/* Draw QR */
|
||||
qr_code.draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
|
||||
|
@ -2138,9 +2245,7 @@ void Cakefoot::update(float timestamp)
|
|||
glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &label.at("fps").transformation()[0][0]);
|
||||
label.at("fps").enable();
|
||||
glDrawArrays(GL_TRIANGLES, 0, label.at("fps").attributes("position")->count());
|
||||
}
|
||||
}
|
||||
}
|
||||
} } }
|
||||
else
|
||||
{
|
||||
/* Draw the play button if it is enabled and hasn't been pressed yet */
|
||||
|
|
|
@ -235,7 +235,8 @@ private:
|
|||
{"view", sb::Text(font())},
|
||||
{"game over", sb::Text(font())},
|
||||
{"arcade rank", sb::Text(large_font)},
|
||||
{"arcade distance", sb::Text(large_font)}
|
||||
{"arcade distance", sb::Text(large_font)},
|
||||
{"quest best", sb::Text(font())}
|
||||
};
|
||||
sb::Sprite playing_field, checkpoint_on, checkpoint_off, coin {"resource/coin/coin-0.png", glm::vec2{12.0f / 486.0f}}, qr_code;
|
||||
sb::Timer on_timer, run_timer, unpaused_timer;
|
||||
|
@ -253,6 +254,8 @@ private:
|
|||
ArcadeScores::Score arcade_score;
|
||||
std::string name_entry = "AAA";
|
||||
sb::Text scoreboard {large_font};
|
||||
std::vector<Flame> ending_coins;
|
||||
sb::Color rotating_hue {128, 0, 0, 0};
|
||||
|
||||
/*!
|
||||
* Load sound effects and music into objects that can be used by the SDL mixer library. Use chunk objects for background music instead of
|
||||
|
@ -356,11 +359,27 @@ private:
|
|||
*/
|
||||
bool level_select() const;
|
||||
|
||||
/*!
|
||||
* @param index optional level index
|
||||
* @return True if level is currently the end screen
|
||||
*/
|
||||
bool end_screen(std::optional<std::size_t> index = std::nullopt) const;
|
||||
|
||||
/*!
|
||||
* @return True if resume quest or resume arcade is the current mode
|
||||
*/
|
||||
bool resuming() const;
|
||||
|
||||
/*!
|
||||
* @return Count of coins collected
|
||||
*/
|
||||
std::size_t bank() const;
|
||||
|
||||
/*!
|
||||
* @return Maximum number of coins that can be collected
|
||||
*/
|
||||
std::size_t max_bank() const;
|
||||
|
||||
inline bool skip_resume_quest()
|
||||
{
|
||||
return configuration()("challenge", challenge_index, "name") == "RESUME QUEST" && configuration()("progress", "quest level") == 1 &&
|
||||
|
@ -402,6 +421,14 @@ private:
|
|||
/* Can be used to time out the name entry screen */
|
||||
Animation submit_score_animation {sb::Animation(std::bind(&Cakefoot::submit_score, this))};
|
||||
|
||||
/*!
|
||||
* Shift the hue of the global sb::Color object that tracks the hue shift by the given amount in the configuration.
|
||||
*/
|
||||
void shift_hue();
|
||||
|
||||
/* Shift the hue by the configured amount once per configured amount of seconds */
|
||||
Animation shift_hue_animation {sb::Animation(std::bind(&Cakefoot::shift_hue, this))};
|
||||
|
||||
/*!
|
||||
* Get the arcade time as the amount of time remaining before the limit is reached.
|
||||
*
|
||||
|
|
|
@ -264,9 +264,7 @@ void Character::draw(const Curve& curve, GLuint transformation_uniform, const gl
|
|||
/* Reflect if walking left */
|
||||
if (curve[next_point_index].x < position.x)
|
||||
{
|
||||
glm::mat4 reflection = glm::mat4{1.0f};
|
||||
reflection[0][0] = -1.0f;
|
||||
_sprite.transform(reflection);
|
||||
_sprite.transform(glm::scale(glm::vec3{-1.0f, 1.0f, 1.0f}));
|
||||
}
|
||||
|
||||
_sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
|
|
|
@ -52,6 +52,18 @@ void Enemy::collect_coin()
|
|||
}
|
||||
}
|
||||
|
||||
void Enemy::draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
||||
const sb::Color& rotating_hue, GLuint color_addition_uniform)
|
||||
{
|
||||
sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
if (_coin.has_value() && !coin_taken() && !coin_collected)
|
||||
{
|
||||
glUniform4fv(color_addition_uniform, 1, &rotating_hue.normal()[0]);
|
||||
_coin->draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
glUniform4fv(color_addition_uniform, 1, &glm::vec4(0)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
Slicer::Slicer(const Curve& curve, float relative, float speed, float stray) : curve(curve), relative(relative), speed(speed), stray(stray)
|
||||
{
|
||||
position = start();
|
||||
|
@ -121,15 +133,6 @@ void Slicer::update(const sb::Timer& timer)
|
|||
sprite.translate(curve.get().wrap(position));
|
||||
}
|
||||
|
||||
void Slicer::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
|
||||
{
|
||||
sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
if (_coin.has_value() && !coin_taken() && !coin_collected)
|
||||
{
|
||||
_coin->draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Slicer::center() const
|
||||
{
|
||||
return curve.get().relative(relative);
|
||||
|
@ -232,15 +235,6 @@ void Fish::update(const sb::Timer& timer)
|
|||
box.center(position);
|
||||
}
|
||||
|
||||
void Fish::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
|
||||
{
|
||||
sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
if (_coin.has_value() && !coin_taken() && !coin_collected)
|
||||
{
|
||||
_coin->draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Fish::center() const
|
||||
{
|
||||
return curve.get().relative(relative);
|
||||
|
@ -303,13 +297,16 @@ void Projectile::update(const sb::Timer& timer)
|
|||
box.center(position);
|
||||
}
|
||||
|
||||
void Projectile::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
|
||||
void Projectile::draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
||||
const sb::Color& rotating_hue, GLuint color_addition_uniform)
|
||||
{
|
||||
if (_coin.has_value())
|
||||
{
|
||||
if (!coin_taken() && !coin_collected)
|
||||
{
|
||||
glUniform4fv(color_addition_uniform, 1, &rotating_hue.normal()[0]);
|
||||
_coin->draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
glUniform4fv(color_addition_uniform, 1, &glm::vec4(0)[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -386,15 +383,6 @@ void Projector::update(const sb::Timer& timer)
|
|||
}
|
||||
}
|
||||
|
||||
void Projector::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
|
||||
{
|
||||
sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
for (Projectile& projectile : projectiles)
|
||||
{
|
||||
projectile.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
}
|
||||
}
|
||||
|
||||
bool Projector::collide(const sb::Box& box, const sb::Sprite& sprite, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const
|
||||
{
|
||||
for (const Projectile& projectile : projectiles)
|
||||
|
@ -463,16 +451,33 @@ void Projector::release()
|
|||
}
|
||||
}
|
||||
|
||||
Flame::Flame(const sb::Box field, const glm::vec3& position, float speed, float angle, float mirror_interval) :
|
||||
field(field), position(position), speed(speed), angle(angle)
|
||||
void Projector::draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
||||
const sb::Color& rotating_hue, GLuint color_addition_uniform)
|
||||
{
|
||||
sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
for (Projectile& projectile : projectiles)
|
||||
{
|
||||
projectile.draw(transformation_uniform, view, projection, texture_flag_uniform, rotating_hue, color_addition_uniform);
|
||||
}
|
||||
}
|
||||
|
||||
Flame::Flame(const sb::Box& field, const glm::vec3& position, float speed, float angle, float mirror_interval, bool camo) :
|
||||
field(field), position(position), speed(speed), angle(angle), _camo(camo)
|
||||
{
|
||||
/* Save initial values */
|
||||
initial_angle = angle;
|
||||
initial_position = position;
|
||||
|
||||
/* Set up animation */
|
||||
sprite.texture("resource/flame/flame-1.png");
|
||||
sprite.texture("resource/flame/flame-2.png");
|
||||
if (!camo)
|
||||
{
|
||||
sprite.texture("resource/flame/flame-1.png");
|
||||
sprite.texture("resource/flame/flame-2.png");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.texture("resource/coin/coin-0.png");
|
||||
}
|
||||
sprite.frames.frame_length(0.3f);
|
||||
sprite.frames.play();
|
||||
|
||||
|
@ -517,15 +522,6 @@ void Flame::update(const sb::Timer& timer)
|
|||
sprite.translate(sb::wrap_point(position, glm::vec3{field.left(), field.bottom(), -1.0f}, glm::vec3{field.right(), field.top(), 1.0f}));
|
||||
}
|
||||
|
||||
void Flame::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
|
||||
{
|
||||
sprite.draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
if (_coin.has_value() && !coin_taken() && !coin_collected)
|
||||
{
|
||||
_coin->draw(transformation_uniform, view, projection, texture_flag_uniform);
|
||||
}
|
||||
}
|
||||
|
||||
void Flame::mirror()
|
||||
{
|
||||
/* Reset the flame position and angle at the end of a cycle so overflow in time and movement don't begin to accumulate and desync the mirror cycle. */
|
||||
|
|
|
@ -17,6 +17,7 @@ class Enemy
|
|||
|
||||
protected:
|
||||
|
||||
sb::Sprite sprite;
|
||||
sb::Box box;
|
||||
|
||||
/* Enemies have an optional challenge coin defined by different parameters depending on the enemy type. */
|
||||
|
@ -63,8 +64,11 @@ public:
|
|||
* @param view view transformation matrix
|
||||
* @param projection projection transformation matrix
|
||||
* @param texture_flag_uniform uniform location in the shader program of the boolean that turns texture drawing on or off
|
||||
* @param rotating_hue color of the hue rotation effect
|
||||
* @param color_addition_uniform uniform location in the shader program of the RGBA value for color addition effect
|
||||
*/
|
||||
virtual void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform) = 0;
|
||||
virtual void draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
||||
const sb::Color& rotating_hue, GLuint color_addition_uniform);
|
||||
|
||||
/*!
|
||||
* Check whether the enemy collides with the given ::Sprite positioned at the given ::Box. The sprite object may be used to perform
|
||||
|
@ -108,7 +112,6 @@ private:
|
|||
std::reference_wrapper<const Curve> curve;
|
||||
float relative = 0.0f, speed = 0.0f, stray = 0.0f, coin_radius = 0.0f, coin_angle = 0.0f;
|
||||
bool toward_end = true;
|
||||
sb::Sprite sprite;
|
||||
|
||||
/*!
|
||||
*/
|
||||
|
@ -153,10 +156,6 @@ public:
|
|||
/*!
|
||||
*/
|
||||
void update(const sb::Timer& timer);
|
||||
|
||||
/*!
|
||||
*/
|
||||
void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform);
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -169,7 +168,6 @@ private:
|
|||
|
||||
std::reference_wrapper<const Curve> curve;
|
||||
float relative = 0.0f, speed = 0.0f, radius = 0.0f, angle = 0.0f, offset = 0.0f, coin_angle = 0.0f, coin_radius = 0.0f;
|
||||
sb::Sprite sprite;
|
||||
std::optional<float> first_update_time;
|
||||
|
||||
/*!
|
||||
|
@ -223,10 +221,6 @@ public:
|
|||
*/
|
||||
void update(const sb::Timer& timer);
|
||||
|
||||
/*!
|
||||
*/
|
||||
void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform);
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -240,7 +234,6 @@ private:
|
|||
|
||||
glm::vec3 position = glm::vec3{0.0f}, target = glm::vec3{0.0f};
|
||||
float speed = 0.0f, angle = 0.0f;
|
||||
sb::Sprite sprite;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -268,7 +261,8 @@ public:
|
|||
|
||||
/*!
|
||||
*/
|
||||
void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform);
|
||||
void draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
||||
const sb::Color& rotating_hue, GLuint color_addition_uniform);
|
||||
|
||||
/*!
|
||||
* @return true if the projectile has left the clip space
|
||||
|
@ -294,7 +288,6 @@ private:
|
|||
glm::vec3 position = glm::vec3{0.0f};
|
||||
float speed = 0.0f, rate = 0.0f;
|
||||
float release_delay;
|
||||
sb::Sprite sprite;
|
||||
sb::Animation animation_charge = sb::Animation(std::bind(&Projector::charge, this)),
|
||||
animation_release = sb::Animation(std::bind(&Projector::release, this));
|
||||
std::vector<Projectile> projectiles;
|
||||
|
@ -348,7 +341,8 @@ public:
|
|||
|
||||
/*!
|
||||
*/
|
||||
void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform);
|
||||
void draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
||||
const sb::Color& rotating_hue, GLuint color_addition_uniform);
|
||||
|
||||
/*!
|
||||
* Check if projectiles collide.
|
||||
|
@ -368,12 +362,11 @@ class Flame : public Enemy
|
|||
|
||||
private:
|
||||
|
||||
const sb::Box field;
|
||||
sb::Box field;
|
||||
glm::vec3 position = glm::vec3{0.0f}, initial_position = glm::vec3{0.0f};
|
||||
float speed = 0.0f, angle = 0.0f, coin_angle = 0.0f, coin_radius = 0.0f, initial_angle = 0.0f;
|
||||
sb::Sprite sprite;
|
||||
sb::Animation animation_mirror = sb::Animation(std::bind(&Flame::mirror, this));
|
||||
bool mirrored = false;
|
||||
bool mirrored = false, _camo = false;
|
||||
|
||||
/*!
|
||||
* Rotate direction 180 degrees.
|
||||
|
@ -393,8 +386,9 @@ public:
|
|||
* @param speed distance in world coordinates the flame will travel per second
|
||||
* @param angle direction the flame will travel
|
||||
* @param mirror_interval optional interval in seconds the direction should mirror, negative values turn off mirroring
|
||||
* @param camo disguise the flame as a coin
|
||||
*/
|
||||
Flame(const sb::Box field, const glm::vec3& position, float speed, float angle, float mirror_interval = -1.0f);
|
||||
Flame(const sb::Box& field, const glm::vec3& position, float speed, float angle, float mirror_interval = -1.0f, bool camo = false);
|
||||
|
||||
/*!
|
||||
* Add a challenge coin to this flame enemy. The sprite will be owned, moved, and drawn by this object.
|
||||
|
@ -412,12 +406,17 @@ public:
|
|||
*/
|
||||
bool collide_coin(sb::Box box, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
||||
|
||||
inline void camo(bool state)
|
||||
{
|
||||
_camo = state;
|
||||
}
|
||||
|
||||
bool camo() const
|
||||
{
|
||||
return _camo;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
void update(const sb::Timer& timer);
|
||||
|
||||
/*!
|
||||
*/
|
||||
void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform);
|
||||
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ void main()
|
|||
* at its start point. Something related to reading the texel at the boundary versus the center. But adjusting the UV into the center of the
|
||||
* texels causes pixels at the borders to display incorrectly. Maybe related to half pixel adjustment https://gamedev.stackexchange.com/a/49585
|
||||
*/
|
||||
output_color = float(texture_enabled) * texture(model_texture, fragment_uv + 0.00001);
|
||||
// output_color = float(texture_enabled) * texture(model_texture, fragment_uv + 0.00001);
|
||||
output_color = float(texture_enabled) * (texture(model_texture, fragment_uv) + color_addition);
|
||||
output_color += (1.0 - float(texture_enabled)) * (ex_color + color_addition);
|
||||
}
|
||||
|
|