add demo mode
This commit is contained in:
parent
341bb50bef
commit
e8ea466f51
22
config.json
22
config.json
|
@ -35,6 +35,10 @@
|
|||
"game over background": [0.0, 0.0, 0.0, 255.0],
|
||||
"game over translation": [0.0, 0.0],
|
||||
"game over scale": [0.25, 0.05],
|
||||
"idle warning foreground": [255.0, 255.0, 255.0, 255.0],
|
||||
"idle warning background": [0.0, 0.0, 0.0, 255.0],
|
||||
"idle warning translation": [0.0, 0.0],
|
||||
"idle warning scale": [0.4, 0.05],
|
||||
"arcade rank scale": [0.5, 0.15],
|
||||
"arcade rank dimensions": [300.0, 80.0],
|
||||
"arcade rank translation": [0.3, 0.6],
|
||||
|
@ -50,7 +54,7 @@
|
|||
"scoreboard translation": [-0.4, 0.835],
|
||||
"scoreboard scale": [1.35, 0.14],
|
||||
"scoreboard wrap": 3000,
|
||||
"qr display": false,
|
||||
"qr display": true,
|
||||
"qr background display": true,
|
||||
"qr background texture": "resource/qr_background.png",
|
||||
"qr texture": "resource/qr.png",
|
||||
|
@ -118,7 +122,8 @@
|
|||
"suppress any key on mods": true,
|
||||
"any key ignore commands": ["left", "right", "up", "down", "pause"],
|
||||
"gamepad pause button index": 9,
|
||||
"gamepad axis cooldown": 0.2
|
||||
"gamepad axis cooldown": 0.2,
|
||||
"gamepad reset button index": 8
|
||||
},
|
||||
|
||||
"keys":
|
||||
|
@ -180,7 +185,8 @@
|
|||
{
|
||||
"coin": ["resource/coin/coin-0.png"],
|
||||
"flame": ["resource/flame/flame-1.png", "resource/flame/flame-2.png"],
|
||||
"auto save": "resource/Autosave.png"
|
||||
"auto save": "resource/Autosave.png",
|
||||
"demo message": "resource/Demo_message.png"
|
||||
},
|
||||
|
||||
"curve":
|
||||
|
@ -440,5 +446,15 @@
|
|||
"thanks translation": [0.3, -0.78],
|
||||
"thanks scale": [1.58, 0.13],
|
||||
"thanks wrap": 1080
|
||||
},
|
||||
|
||||
"demo":
|
||||
{
|
||||
"active": true,
|
||||
"idle timeout": 20.0,
|
||||
"countdown display timeout": 11.0,
|
||||
"countdown message": "IDLE RESET IN ",
|
||||
"message translation": [0.0, 0.83],
|
||||
"message scale": [1.05, 0.14]
|
||||
}
|
||||
}
|
||||
|
|
16
index.html
16
index.html
|
@ -88,9 +88,19 @@
|
|||
|
||||
<!--
|
||||
<div id="message">
|
||||
<div>email list
|
||||
<div>hints (coins, controls, arcade cabinet, launch, game engine and source, merch, etc.)
|
||||
<div>follow
|
||||
<div>Join e-mail list
|
||||
<div>Watch the trailer and wishlist on Steam
|
||||
<div>Controls
|
||||
<div>Hint: collect every coin for a different ending
|
||||
<div>Hint: unlock BUFFALO BEEF CAKE to get a heavyweight character and faster times
|
||||
<div>Hint: there are at least two other cakes, maybe three?
|
||||
<div>Follow on Twitter, Tik Tok, YouTube, Steam and itch.io
|
||||
<div>There is a press kit available
|
||||
<div>Release dates and platforms
|
||||
<div>Ad support and subscriptions coming soon
|
||||
<div>Visit the arcade cabinet
|
||||
<div>Cakefoot is powered by a custom game engine SPACE🪐BOX
|
||||
<div>Buy a 5-pack of Cakefoot pins
|
||||
-->
|
||||
|
||||
<div id="message">
|
||||
|
|
2
lib/sb
2
lib/sb
|
@ -1 +1 @@
|
|||
Subproject commit 0e4f06d77917d1f4349fd0f46f245eb7e63ebfca
|
||||
Subproject commit 27ab95037f3f6d4f2a3674db99dd1946df07521e
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
resource/qr.png
BIN
resource/qr.png
Binary file not shown.
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 41 KiB |
233
src/Cakefoot.cpp
233
src/Cakefoot.cpp
|
@ -98,11 +98,15 @@ Cakefoot::Cakefoot(std::initializer_list<std::string> configuration_merge) : Gam
|
|||
configuration()["progress"]["current challenge"] = 4;
|
||||
}
|
||||
|
||||
/* Set the spinner values to what the player was last playing. */
|
||||
level_select_index = configuration()("progress", "current level");
|
||||
profile_index = configuration()("progress", "current difficulty");
|
||||
challenge_index = configuration()("progress", "current challenge");
|
||||
view_index = configuration()("progress", "current view");
|
||||
/* Set the spinner values to what the player was last playing, unless demo mode is active, in which case leave
|
||||
* the values at the defaults. */
|
||||
if (!configuration()("demo", "active"))
|
||||
{
|
||||
level_select_index = configuration()("progress", "current level");
|
||||
profile_index = configuration()("progress", "current difficulty");
|
||||
challenge_index = configuration()("progress", "current challenge");
|
||||
view_index = configuration()("progress", "current view");
|
||||
}
|
||||
|
||||
/* Initialize name entry */
|
||||
name_entry = configuration()("display", "default initials");
|
||||
|
@ -207,6 +211,9 @@ Cakefoot::Cakefoot(std::initializer_list<std::string> configuration_merge) : Gam
|
|||
/* Switch volume on */
|
||||
button.at("volume").press();
|
||||
|
||||
/* Track idle time */
|
||||
idle_timer.on();
|
||||
|
||||
#if defined(EMSCRIPTEN)
|
||||
/* Pause the game when the browser tab is hidden */
|
||||
if (emscripten_set_visibilitychange_callback(this, false, &respond_to_visibility_change) < 0)
|
||||
|
@ -479,7 +486,8 @@ void Cakefoot::set_up_buttons()
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
sb::Plane pause_plane;
|
||||
pause_plane.texture(pause_texture);
|
||||
button.at("pause") = sb::Pad<>{pause_plane, configuration()("button", "pause translation"), configuration()("button", "pause scale"), 1.0f};
|
||||
button.at("pause") = sb::Pad<>{pause_plane, configuration()("button", "pause translation"),
|
||||
configuration()("button", "pause scale"), 1.0f};
|
||||
button.at("pause").on_state_change([&](bool state){
|
||||
sb::Delegate::post("pause", false);
|
||||
});
|
||||
|
@ -497,7 +505,8 @@ void Cakefoot::set_up_buttons()
|
|||
sb::Plane volume_plane;
|
||||
volume_plane.texture(volume_off_texture);
|
||||
volume_plane.texture(volume_on_texture);
|
||||
button.at("volume") = sb::Pad<>{volume_plane, configuration()("button", "volume translation"), configuration()("button", "volume scale"), 1.0f};
|
||||
button.at("volume") = sb::Pad<>{
|
||||
volume_plane, configuration()("button", "volume translation"), configuration()("button", "volume scale"), 1.0f};
|
||||
button.at("volume").state(original_state);
|
||||
button.at("volume").on_state_change([&](bool state){
|
||||
/* Mute or unmute (to full volume) depending on the state of the button */
|
||||
|
@ -588,12 +597,11 @@ void Cakefoot::set_up_buttons()
|
|||
}
|
||||
});
|
||||
button.at("level decrement").on_state_change([&](bool state){
|
||||
|
||||
/* Only allow level select in level select mode */
|
||||
if (configuration()("challenge", challenge_index, "name") == "LEVEL SELECT")
|
||||
{
|
||||
/* If the level is decreased below 1, wrap to the last level if the current difficulty is complete, otherwise wrap to the max level
|
||||
* unlocked. */
|
||||
/* If the level is decreased below 1, wrap to the last level if the current difficulty is complete, otherwise wrap to
|
||||
* the max level unlocked. */
|
||||
if (--level_select_index < 1)
|
||||
{
|
||||
if (profile_index < configuration()("progress", "max difficulty"))
|
||||
|
@ -841,7 +849,8 @@ void Cakefoot::toggle_challenge()
|
|||
}
|
||||
|
||||
/* In new game modes, set the level select to 1 and leave the difficulty unchanged. */
|
||||
else if (configuration()("challenge", challenge_index, "name") == "ARCADE" || configuration()("challenge", challenge_index, "name") == "NEW QUEST")
|
||||
else if (configuration()("challenge", challenge_index, "name") == "ARCADE" ||
|
||||
configuration()("challenge", challenge_index, "name") == "NEW QUEST")
|
||||
{
|
||||
level_select_index = 1;
|
||||
}
|
||||
|
@ -974,7 +983,8 @@ void Cakefoot::set_up_hud()
|
|||
social.scale(configuration()("display", "social scale"));
|
||||
|
||||
/* Set up auto save icon */
|
||||
auto_save = sb::Sprite {configuration()("texture", "auto save").get<std::string>(), configuration()("display", "auto save scale").get<glm::vec2>(), GL_LINEAR};
|
||||
auto_save = sb::Sprite {configuration()("texture", "auto save").get<std::string>(),
|
||||
configuration()("display", "auto save scale").get<glm::vec2>(), GL_LINEAR};
|
||||
auto_save.translate(configuration()("display", "auto save translation"));
|
||||
|
||||
/* Style the quest best time indicator */
|
||||
|
@ -999,6 +1009,24 @@ void Cakefoot::set_up_hud()
|
|||
thanks.translate(configuration()("ending", "thanks translation"));
|
||||
thanks.scale(configuration()("ending", "thanks scale"));
|
||||
thanks.refresh();
|
||||
|
||||
/* Style the idle warning */
|
||||
label.at("idle warning").content(configuration()("demo", "countdown message"));
|
||||
label.at("idle warning").foreground(configuration()("display", "idle warning foreground").get<glm::vec4>());
|
||||
label.at("idle warning").background(configuration()("display", "idle warning background").get<glm::vec4>());
|
||||
label.at("idle warning").untransform();
|
||||
label.at("idle warning").translate(configuration()("display", "idle warning translation"));
|
||||
label.at("idle warning").scale(configuration()("display", "idle warning scale"));
|
||||
label.at("idle warning").refresh();
|
||||
|
||||
/* Style the demo message */
|
||||
sb::Texture demo_message_texture {configuration()("texture", "demo message").get<std::string>()};
|
||||
demo_message_texture.load();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
demo_message.texture(demo_message_texture);
|
||||
demo_message.translate(configuration()("demo", "message translation"));
|
||||
demo_message.scale(configuration()("demo", "message scale"));
|
||||
}
|
||||
|
||||
void Cakefoot::load_vbo()
|
||||
|
@ -1090,11 +1118,11 @@ void Cakefoot::load_level(int index)
|
|||
submit_score_animation.reset();
|
||||
}
|
||||
|
||||
/* Reset enemies list to empty. Open configuration for the current level. Repopulate list of enemies one by one using the list of enemies in the
|
||||
* configuration. For each enemy, add a challenge coin if the config specifies the coin parameters.
|
||||
/* Reset enemies list to empty. Open configuration for the current level. Repopulate list of enemies one by one using the list
|
||||
* of enemies in the configuration. For each enemy, add a challenge coin if the config specifies the coin parameters.
|
||||
*
|
||||
* Values read from the config are in some cases converted from old 25fps hard-coded per-frame values to per-second values, and hard-coded
|
||||
* 864px by 486px pixel space to relative NDC space.
|
||||
* Values read from the config are in some cases converted from old 25fps hard-coded per-frame values to per-second values,
|
||||
* and hard-coded 864px by 486px pixel space to relative NDC space.
|
||||
*/
|
||||
this->enemies.clear();
|
||||
if (configuration()("levels", index).contains("enemies"))
|
||||
|
@ -1107,7 +1135,8 @@ void Cakefoot::load_level(int index)
|
|||
if (type == "slicer")
|
||||
{
|
||||
std::shared_ptr<Slicer> slicer = std::make_shared<Slicer>(
|
||||
curve(), enemy[1].get<float>(), 2.0f * 25.0f * enemy[2].get<float>() / 486.0f, 2.0f * enemy[3].get<float>() / 486.0f);
|
||||
curve(), enemy[1].get<float>(), 2.0f * 25.0f * enemy[2].get<float>() / 486.0f,
|
||||
2.0f * enemy[3].get<float>() / 486.0f);
|
||||
|
||||
/* Add coin to slicer */
|
||||
if (enemy.size() > 4)
|
||||
|
@ -1176,7 +1205,8 @@ void Cakefoot::load_level(int index)
|
|||
float x = field.left() + shift * margin.x / 2.0f;
|
||||
while (x < field.right())
|
||||
{
|
||||
std::shared_ptr<Flame> flame = std::make_shared<Flame>(field, glm::vec3{x, y, 0.0f}, 0.41152263f, glm::quarter_pi<float>());
|
||||
std::shared_ptr<Flame> flame = std::make_shared<Flame>(
|
||||
field, glm::vec3{x, y, 0.0f}, 0.41152263f, glm::quarter_pi<float>());
|
||||
|
||||
/* Add a challenge coin */
|
||||
if (++count == 15)
|
||||
|
@ -1214,7 +1244,8 @@ void Cakefoot::load_level(int index)
|
|||
for (std::size_t count = 0; x < range.y; ++count)
|
||||
{
|
||||
y = amplitude * glm::sin(period * x) + shift;
|
||||
std::shared_ptr<Flame> flame = std::make_shared<Flame>(field, glm::vec3{x, y, 0.0f}, speed, 3.0f * glm::half_pi<float>(), mirror);
|
||||
std::shared_ptr<Flame> flame = std::make_shared<Flame>(
|
||||
field, glm::vec3{x, y, 0.0f}, speed, 3.0f * glm::half_pi<float>(), mirror);
|
||||
if (enemy.size() > 8 && enemy[8].get<std::size_t>() == count)
|
||||
{
|
||||
flame->coin(coin, enemy[9].get<float>(), enemy[10].get<float>());
|
||||
|
@ -1226,8 +1257,8 @@ void Cakefoot::load_level(int index)
|
|||
}
|
||||
}
|
||||
|
||||
/* If the level is the end screen, reset the player's current level to the beginning and load ending screen coin list. Unlock any new difficulty
|
||||
* or view. Set a list of messages to be displayed on the end screen. */
|
||||
/* If the level is the end screen, reset the player's current level to the beginning and load ending screen coin list. Unlock
|
||||
* any new difficulty or view. Set a list of messages to be displayed on the end screen. */
|
||||
if (end_screen())
|
||||
{
|
||||
/* Load ending coins */
|
||||
|
@ -1238,7 +1269,8 @@ void Cakefoot::load_level(int index)
|
|||
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()
|
||||
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);
|
||||
}
|
||||
|
@ -1249,7 +1281,8 @@ void Cakefoot::load_level(int index)
|
|||
/* Show the end for any run that beats all the levels */
|
||||
if (quest() || (arcade() && configuration()("progress", "arcade level") >= configuration()("levels").size() - 2))
|
||||
{
|
||||
sb::Text message {fonts.at("glyph large"), configuration()("ending", "end text"), configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
sb::Text message {fonts.at("glyph large"), configuration()("ending", "end text"),
|
||||
configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
message.dimensions(configuration()("ending", "messages dimensions"));
|
||||
message.refresh();
|
||||
ending_messages.push_back(message);
|
||||
|
@ -1261,7 +1294,8 @@ void Cakefoot::load_level(int index)
|
|||
if (configuration()("progress", "max view").get<int>() < 1)
|
||||
{
|
||||
configuration()["progress"]["max view"] = 1;
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "unlock mirror"), configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "unlock mirror"),
|
||||
configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
message.dimensions(configuration()("ending", "messages dimensions"));
|
||||
message.refresh();
|
||||
ending_messages.push_back(message);
|
||||
|
@ -1269,7 +1303,8 @@ void Cakefoot::load_level(int index)
|
|||
if (configuration()("progress", "max view").get<int>() < 2 && profile_index >= 1)
|
||||
{
|
||||
configuration()["progress"]["max view"] = 2;
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "unlock warped"), configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "unlock warped"),
|
||||
configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
message.dimensions(configuration()("ending", "messages dimensions"));
|
||||
message.refresh();
|
||||
ending_messages.push_back(message);
|
||||
|
@ -1278,7 +1313,8 @@ void Cakefoot::load_level(int index)
|
|||
{
|
||||
configuration()["progress"]["jackpot"] = 777;
|
||||
character.profile(configuration()("character", "profile", profile_index, "name"));
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "unlock jackpot"), configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "unlock jackpot"),
|
||||
configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
message.dimensions(configuration()("ending", "messages dimensions"));
|
||||
message.refresh();
|
||||
ending_messages.push_back(message);
|
||||
|
@ -1308,9 +1344,9 @@ void Cakefoot::load_level(int index)
|
|||
configuration()["progress"]["current challenge"] = challenge_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. Save the time if it is better than the existing
|
||||
* record. */
|
||||
/* 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. Save
|
||||
* the time if it is better than the existing record. */
|
||||
if (quest())
|
||||
{
|
||||
if (profile_index < static_cast<int>(configuration()("character", "profile").size()) - 1)
|
||||
|
@ -1319,7 +1355,8 @@ void Cakefoot::load_level(int index)
|
|||
{
|
||||
sb::Text message {fonts.at("glyph")};
|
||||
message.foreground(configuration()("ending", "messages foreground").get<glm::vec4>());
|
||||
profile_index == 1 ? message.content(configuration()("ending", "unlock beef")) : message.content(configuration()("ending", "unlock buffalo"));
|
||||
profile_index == 1 ? message.content(configuration()("ending", "unlock beef")) :
|
||||
message.content(configuration()("ending", "unlock buffalo"));
|
||||
message.dimensions(configuration()("ending", "messages dimensions"));
|
||||
message.refresh();
|
||||
ending_messages.push_back(message);
|
||||
|
@ -1334,9 +1371,11 @@ void Cakefoot::load_level(int index)
|
|||
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()));
|
||||
label.at("quest best").content(
|
||||
configuration()("display", "quest best text").get<std::string>() + " " + format_clock(run_timer.elapsed()));
|
||||
label.at("quest best").refresh();
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "new best"), configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
sb::Text message {fonts.at("glyph"), configuration()("ending", "new best"),
|
||||
configuration()("ending", "messages foreground").get<glm::vec4>()};
|
||||
message.dimensions(configuration()("ending", "messages dimensions"));
|
||||
message.refresh();
|
||||
ending_messages.push_back(message);
|
||||
|
@ -1347,12 +1386,13 @@ void Cakefoot::load_level(int index)
|
|||
level_select_index = 1;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
/* 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. */
|
||||
else if (index > 0)
|
||||
{
|
||||
/* Unlock the level if it is a newly reached level */
|
||||
if (configuration()("progress", "max difficulty") == profile_index && configuration()("progress", "max level").get<int>() < index)
|
||||
if (configuration()("progress", "max difficulty") == profile_index &&
|
||||
configuration()("progress", "max level").get<int>() < index)
|
||||
{
|
||||
configuration()["progress"]["max level"] = index;
|
||||
}
|
||||
|
@ -1431,6 +1471,13 @@ void Cakefoot::load_level(int index)
|
|||
run_timer.on();
|
||||
}
|
||||
|
||||
/* In demo mode, reset the challenge to new quest every time the title is loaded */
|
||||
if (configuration()("demo", "active") && index == 0)
|
||||
{
|
||||
challenge_index = 1;
|
||||
level_select_index = 1;
|
||||
}
|
||||
|
||||
/* Refresh HUD elements */
|
||||
set_up_hud();
|
||||
|
||||
|
@ -1622,7 +1669,8 @@ float Cakefoot::limit() const
|
|||
} } }
|
||||
|
||||
/* Add bank bonus */
|
||||
limit += configuration()("progress", "arcade bank").get<int>() * configuration()("challenge", challenge_index, "bank bonus").get<float>();
|
||||
limit += configuration()("progress", "arcade bank").get<int>() * configuration()(
|
||||
"challenge", challenge_index, "bank bonus").get<float>();
|
||||
}
|
||||
|
||||
return limit;
|
||||
|
@ -1656,7 +1704,8 @@ bool Cakefoot::end_screen(std::optional<std::size_t> index) const
|
|||
|
||||
bool Cakefoot::resuming() const
|
||||
{
|
||||
return configuration()("challenge", challenge_index, "name") == "RESUME QUEST" || configuration()("challenge", challenge_index, "name") == "RESUME ARCADE";
|
||||
return configuration()("challenge", challenge_index, "name") == "RESUME QUEST" ||
|
||||
configuration()("challenge", challenge_index, "name") == "RESUME ARCADE";
|
||||
}
|
||||
|
||||
std::size_t Cakefoot::bank() const
|
||||
|
@ -1808,6 +1857,9 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
{
|
||||
Game::respond(event);
|
||||
|
||||
/* Reset the idle timer */
|
||||
idle_timer.reset();
|
||||
|
||||
/* Translate gamepad input to commands */
|
||||
if (event.type == SDL_JOYBUTTONDOWN)
|
||||
{
|
||||
|
@ -1816,6 +1868,12 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
{
|
||||
sb::Delegate::post("pause");
|
||||
}
|
||||
else if (configuration()("demo", "active") && level_index > 0 &&
|
||||
static_cast<std::size_t>(level_index) <= configuration()("levels").size() - 2 &&
|
||||
event.jbutton.button == configuration()("input", "gamepad reset button index"))
|
||||
{
|
||||
sb::Delegate::post("reset");
|
||||
}
|
||||
else if ((!use_play_button || button.at("play").pressed()) && !splash_animation.playing())
|
||||
{
|
||||
sb::Delegate::post("any");
|
||||
|
@ -1863,7 +1921,8 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
glm::vec2 mouse_pixel = event.type == SDL_MOUSEBUTTONDOWN ? glm::vec2{event.button.x, event.button.y} :
|
||||
glm::vec2{event.motion.x, event.motion.y};
|
||||
glm::vec2 mouse_ndc {
|
||||
float(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f, (1.0f - float(mouse_pixel.y) / window_box().height()) * 2.0f - 1.0f
|
||||
float(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f,
|
||||
(1.0f - float(mouse_pixel.y) / window_box().height()) * 2.0f - 1.0f
|
||||
};
|
||||
|
||||
/* Track whether cursor should display */
|
||||
|
@ -1879,7 +1938,10 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
bool level_enabled = button.at("level decrement").enabled();
|
||||
bool profile_enabled = button.at("profile decrement").enabled();
|
||||
bool view_enabled = button.at("view decrement").enabled();
|
||||
if (sb::Delegate::compare(event, "down"))
|
||||
|
||||
/* Prevent navigating into menus in demo and arcade-only modes */
|
||||
if (sb::Delegate::compare(event, "down") && !configuration()("display", "arcade only") &&
|
||||
!configuration()("demo", "active"))
|
||||
{
|
||||
if (selected == "start")
|
||||
{
|
||||
|
@ -1927,7 +1989,10 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
selected = "start";
|
||||
}
|
||||
}
|
||||
else if (sb::Delegate::compare(event, "up"))
|
||||
|
||||
/* Prevent navigating into menus in demo and arcade-only modes */
|
||||
else if (sb::Delegate::compare(event, "up") && !configuration()("display", "arcade only") &&
|
||||
!configuration()("demo", "active"))
|
||||
{
|
||||
if (selected == "start")
|
||||
{
|
||||
|
@ -1979,6 +2044,8 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
selected = "start";
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute menu action */
|
||||
else if (sb::Delegate::compare(event, "any"))
|
||||
{
|
||||
if (!selected.has_value())
|
||||
|
@ -1993,7 +2060,8 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
}
|
||||
|
||||
/* Custom keys for name entry. */
|
||||
else if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 && arcade() && configuration()("display", "name entry enabled"))
|
||||
else if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 && arcade() &&
|
||||
configuration()("display", "name entry enabled"))
|
||||
{
|
||||
if (sb::Delegate::compare(event, "up"))
|
||||
{
|
||||
|
@ -2051,7 +2119,8 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
else if (event.type == SDL_MOUSEBUTTONUP || sb::Delegate::compare_cancel(event, "any"))
|
||||
{
|
||||
/* End character acceleration */
|
||||
if (sb::Delegate::compare_cancel(event, "any") || (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT))
|
||||
if (sb::Delegate::compare_cancel(event, "any") ||
|
||||
(event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT))
|
||||
{
|
||||
character.accelerating = false;
|
||||
}
|
||||
|
@ -2066,8 +2135,8 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
if (level_index == 0)
|
||||
{
|
||||
for (const std::string& name : {
|
||||
"start", "level increment", "level decrement", "profile increment", "profile decrement", "challenge increment",
|
||||
"challenge decrement", "view increment", "view decrement"})
|
||||
"start", "level increment", "level decrement", "profile increment", "profile decrement",
|
||||
"challenge increment", "challenge decrement", "view increment", "view decrement"})
|
||||
{
|
||||
if (!configuration()("display", "arcade only") || name == "start")
|
||||
{
|
||||
|
@ -2078,7 +2147,8 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
{
|
||||
button.at(name).press();
|
||||
|
||||
/* Cancel hover on the start button because the button will be removed from the screen after the press. */
|
||||
/* Cancel hover on the start button because the button will be removed from the screen after the
|
||||
* press. */
|
||||
if (name == "start") hovering = false;
|
||||
} } } } }
|
||||
|
||||
|
@ -2109,7 +2179,8 @@ void Cakefoot::respond(SDL_Event& event)
|
|||
} } }
|
||||
|
||||
/* Collide with name entry in arcade mode on end screen */
|
||||
else if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 && arcade() && configuration()("display", "name entry enabled"))
|
||||
else if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 &&
|
||||
arcade() && configuration()("display", "name entry enabled"))
|
||||
{
|
||||
for (const std::string& button_name : {std::string("name 1"), std::string("name 2"), std::string("name 3"),
|
||||
"name " + std::to_string(name_entry_index + 1) + " increment",
|
||||
|
@ -2459,7 +2530,8 @@ void Cakefoot::update(float timestamp)
|
|||
}
|
||||
else
|
||||
{
|
||||
fov = 2.0f * glm::atan(((1.0f / (window_box().width() * (9.0f / 16.0f))) * window_box().height()) / camera_position.z) + zoom;
|
||||
fov = 2.0f * glm::atan(((1.0f / (window_box().width() * (9.0f / 16.0f))) * window_box().height()) / camera_position.z) +
|
||||
zoom;
|
||||
}
|
||||
projection = glm::perspective(fov, window_box().aspect(), 0.1f, 100.0f);
|
||||
|
||||
|
@ -2518,13 +2590,22 @@ void Cakefoot::update(float timestamp)
|
|||
configuration()["progress"]["quest time"].get_ref<nlohmann::json::number_float_t&>() += run_timer.frame();
|
||||
}
|
||||
unpaused_timer.update(timestamp);
|
||||
idle_timer.update(timestamp);
|
||||
|
||||
/* In demo mode, reset if playing and idle timeout elapsed */
|
||||
if (level_index > 0 && configuration()("demo", "active") &&
|
||||
idle_timer.elapsed() > configuration()("demo", "idle timeout"))
|
||||
{
|
||||
sb::Delegate::post("reset");
|
||||
}
|
||||
|
||||
/* Arcade scoring */
|
||||
auto& maximum_distance = configuration()["progress"]["arcade max distance"].get_ref<nlohmann::json::number_integer_t&>();
|
||||
float extended_limit = limit();
|
||||
if (arcade())
|
||||
{
|
||||
/* Check if maximum distance increased. Using auto as the type handles differences between integer types in different compilers. */
|
||||
/* Check if maximum distance increased. Using auto as the type handles differences between integer types in
|
||||
* different compilers. */
|
||||
if (distance() > maximum_distance)
|
||||
{
|
||||
maximum_distance = distance();
|
||||
|
@ -2532,7 +2613,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() && level_index > 0 &&
|
||||
run_timer.elapsed() > extended_limit && static_cast<std::size_t>(level_index) < configuration()("levels").size() - 1;
|
||||
run_timer.elapsed() > extended_limit &&
|
||||
static_cast<std::size_t>(level_index) < configuration()("levels").size() - 1;
|
||||
if (game_over_active && !game_over_animation.playing())
|
||||
{
|
||||
run_timer.off();
|
||||
|
@ -2549,10 +2631,11 @@ void Cakefoot::update(float timestamp)
|
|||
/* Freeze screen while game over display is active. */
|
||||
if (!game_over_animation.playing())
|
||||
{
|
||||
/* Update character, along the curve, using the timer to determine movement since last frame, and update enemies. Check for collison
|
||||
* as enemies are updated. */
|
||||
character.update(curve(), unpaused_timer, !button.at("volume").pressed(),
|
||||
level_index ? std::nullopt : std::optional<float>(configuration()("character", "idle speed").get<float>()));
|
||||
/* Update character, along the curve, using the timer to determine movement since last frame, and update enemies.
|
||||
* Check for collison as enemies are updated. */
|
||||
character.update(
|
||||
curve(), unpaused_timer, !button.at("volume").pressed(),
|
||||
level_index ? std::nullopt : std::optional<float>(configuration()("character", "idle speed").get<float>()));
|
||||
if (character.at_end(curve()))
|
||||
{
|
||||
/* On the ending screen, submit the score and name entry. */
|
||||
|
@ -2591,7 +2674,8 @@ void Cakefoot::update(float timestamp)
|
|||
{
|
||||
for (nlohmann::json checkpoint : configuration()("levels", level_index, "checkpoints"))
|
||||
{
|
||||
if (character.relative(curve()) >= checkpoint["position"].get<float>() && character.checkpoint() < checkpoint["position"].get<float>())
|
||||
if (character.relative(curve()) >= checkpoint["position"].get<float>() &&
|
||||
character.checkpoint() < checkpoint["position"].get<float>())
|
||||
{
|
||||
audio.at("checkpoint").play();
|
||||
character.checkpoint(checkpoint["position"].get<float>());
|
||||
|
@ -2706,7 +2790,8 @@ void Cakefoot::update(float timestamp)
|
|||
sprite = &checkpoint_on;
|
||||
}
|
||||
glm::vec3 position = curve().relative(checkpoint["position"].get<float>());
|
||||
glm::vec2 delta = sb::angle_to_vector(checkpoint["angle"].get<float>(), configuration()("display", "checkpoint distance"));
|
||||
glm::vec2 delta = sb::angle_to_vector(
|
||||
checkpoint["angle"].get<float>(), configuration()("display", "checkpoint distance"));
|
||||
position += glm::vec3{delta.x, delta.y, 0.0f};
|
||||
sprite->translate(curve().wrap(position));
|
||||
sprite->draw(uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"));
|
||||
|
@ -2716,7 +2801,8 @@ void Cakefoot::update(float timestamp)
|
|||
/* Draw enemies */
|
||||
for (auto& enemy : enemies)
|
||||
{
|
||||
enemy->draw(uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"), rotating_hue, uniform.at("color addition"));
|
||||
enemy->draw(uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"), rotating_hue,
|
||||
uniform.at("color addition"));
|
||||
if (!flash_animation.playing())
|
||||
{
|
||||
glUniform4fv(uniform.at("color addition"), 1, &glm::vec4(0)[0]);
|
||||
|
@ -2731,7 +2817,8 @@ void Cakefoot::update(float timestamp)
|
|||
{
|
||||
for (Flame& coin : ending_coins)
|
||||
{
|
||||
coin.draw(uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"), rotating_hue, uniform.at("color addition"));
|
||||
coin.draw(uniform.at("mvp"), view * rotation_matrix, projection, uniform.at("texture enabled"), rotating_hue,
|
||||
uniform.at("color addition"));
|
||||
if (!flash_animation.playing())
|
||||
{
|
||||
glUniform4fv(uniform.at("color addition"), 1, &glm::vec4(0)[0]);
|
||||
|
@ -2779,8 +2866,8 @@ void Cakefoot::update(float timestamp)
|
|||
{
|
||||
/* Draw spinner buttons */
|
||||
for (const std::string& name : {
|
||||
"level decrement", "level increment", "profile decrement", "profile increment", "challenge decrement", "challenge increment",
|
||||
"view decrement", "view increment"
|
||||
"level decrement", "level increment", "profile decrement", "profile increment", "challenge decrement",
|
||||
"challenge increment", "view decrement", "view increment"
|
||||
})
|
||||
{
|
||||
if (selected != name || blinking_visible)
|
||||
|
@ -2868,7 +2955,8 @@ void Cakefoot::update(float timestamp)
|
|||
if (level_index > 0 && static_cast<std::size_t>(level_index) < _configuration("levels").size() - 1)
|
||||
{
|
||||
std::stringstream level_indicator;
|
||||
level_indicator << std::setw(2) << std::setfill('0') << level_index << "/" << std::setw(2) << _configuration("levels").size() - 2;
|
||||
level_indicator << std::setw(2) << std::setfill('0') << level_index << "/" << std::setw(2) <<
|
||||
_configuration("levels").size() - 2;
|
||||
label.at("level").content(level_indicator.str());
|
||||
label.at("level").refresh();
|
||||
label.at("level").texture(0).bind();
|
||||
|
@ -2888,6 +2976,28 @@ void Cakefoot::update(float timestamp)
|
|||
glDrawArrays(GL_TRIANGLES, 0, label.at("game over").attributes("position")->count());
|
||||
}
|
||||
|
||||
/* Draw idle warning */
|
||||
if (level_index > 0 && configuration()("demo", "active") &&
|
||||
idle_timer.elapsed() > configuration()("demo", "countdown display timeout"))
|
||||
{
|
||||
std::stringstream idle_warning_message;
|
||||
int remaining = std::ceil(configuration()("demo", "idle timeout").get<float>() - idle_timer.elapsed());
|
||||
idle_warning_message << configuration()("demo", "countdown message").get<std::string>() << remaining;
|
||||
label.at("idle warning").content(idle_warning_message.str());
|
||||
label.at("idle warning").refresh();
|
||||
label.at("idle warning").texture(0).bind();
|
||||
label_transformation = projection * view * label.at("idle warning").transformation();
|
||||
glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &label_transformation[0][0]);
|
||||
label.at("idle warning").enable();
|
||||
glDrawArrays(GL_TRIANGLES, 0, label.at("idle warning").attributes("position")->count());
|
||||
}
|
||||
|
||||
/* Draw demo message */
|
||||
if (level_index == 0 && configuration()("demo", "active"))
|
||||
{
|
||||
demo_message.draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
|
||||
}
|
||||
|
||||
/* Draw arcade results */
|
||||
if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 && arcade())
|
||||
{
|
||||
|
@ -2946,7 +3056,8 @@ void Cakefoot::update(float timestamp)
|
|||
}
|
||||
|
||||
/* Draw end screen messages */
|
||||
if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 && !configuration()("display", "arcade only"))
|
||||
if (static_cast<std::size_t>(level_index) == configuration()("levels").size() - 1 &&
|
||||
!configuration()("display", "arcade only"))
|
||||
{
|
||||
float y = configuration()("ending", "messages y").get<float>();
|
||||
for (std::size_t message_ii = 0; message_ii < ending_messages.size(); message_ii++)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* _ _
|
||||
* c/a`k-e'f`o^o~t-, | a single-button action game | by @ooofoam
|
||||
* / _< | wow a living cake the sweet | play online: https://shampoo.ooo/cakefoot
|
||||
* c/a`k-e'f`o^o~t-, | a single-button action game | by @dankd0tgame
|
||||
* / _< | wow a living cake the sweet | play online: https://cakefoot.dank.game
|
||||
* _> `~_/ | taste of victory | open source: https://open.shampoo.ooo/shampoo/cakefoot
|
||||
*/
|
||||
|
||||
|
@ -201,8 +201,8 @@ private:
|
|||
|
||||
/* Member vars */
|
||||
std::shared_ptr<SDL_Cursor> poke, grab;
|
||||
int previous_frames_per_second = 0, curve_index = 0, curve_byte_count = 0, level_index = 0, level_select_index = 1, profile_index = 0,
|
||||
challenge_index = 0, view_index = 0, name_entry_index = 0, splash_index = 0;
|
||||
int previous_frames_per_second = 0, curve_index = 0, curve_byte_count = 0, level_index = 0, level_select_index = 1,
|
||||
profile_index = 0, challenge_index = 0, view_index = 0, name_entry_index = 0, splash_index = 0;
|
||||
std::map<std::string, GLuint> uniform;
|
||||
GLuint shader_program;
|
||||
glm::mat4 view {1.0f}, projection {1.0f};
|
||||
|
@ -241,7 +241,8 @@ private:
|
|||
{"small", font(configuration()("font", "small", "path").get<std::string>(), configuration()("font", "small", "size"))},
|
||||
{"large", font(configuration()("font", "large", "path").get<std::string>(), configuration()("font", "large", "size"))},
|
||||
{"glyph", font(configuration()("font", "glyph", "path").get<std::string>(), configuration()("font", "glyph", "size"))},
|
||||
{"glyph large", font(configuration()("font", "glyph large", "path").get<std::string>(), configuration()("font", "glyph large", "size"))}
|
||||
{"glyph large", font(configuration()("font", "glyph large", "path").get<std::string>(),
|
||||
configuration()("font", "glyph large", "size"))}
|
||||
};
|
||||
std::map<std::string, sb::Text> label = {
|
||||
{"fps", sb::Text(font())},
|
||||
|
@ -254,11 +255,12 @@ private:
|
|||
{"game over", sb::Text(font())},
|
||||
{"arcade rank", sb::Text(fonts.at("large"))},
|
||||
{"arcade distance", sb::Text(fonts.at("large"))},
|
||||
{"quest best", sb::Text(fonts.at("glyph"))}
|
||||
{"quest best", sb::Text(fonts.at("glyph"))},
|
||||
{"idle warning", sb::Text(font())}
|
||||
};
|
||||
sb::Sprite playing_field, checkpoint_on, checkpoint_off, coin {"resource/coin/coin-0.png", glm::vec2{12.0f / 486.0f}, GL_LINEAR}, qr_code, qr_code_bg, social,
|
||||
auto_save;
|
||||
sb::Timer on_timer, run_timer, unpaused_timer;
|
||||
sb::Sprite playing_field, checkpoint_on, checkpoint_off, qr_code, qr_code_bg, social, auto_save, demo_message,
|
||||
coin {"resource/coin/coin-0.png", glm::vec2{12.0f / 486.0f}, GL_LINEAR};
|
||||
sb::Timer on_timer, run_timer, unpaused_timer, idle_timer;
|
||||
glm::vec3 camera_position {0.0f, 0.0f, 2.0f}, subject_position {0.0f, 0.0f, 0.0f};
|
||||
float zoom = 0.0f;
|
||||
glm::vec2 rotation = {0.0f, 0.0f};
|
||||
|
@ -293,7 +295,8 @@ private:
|
|||
|
||||
/*!
|
||||
* Compile and attach shaders, store locations of uniforms, initialize some GL properties. This must be done after the GL context
|
||||
* is created (currently the context is created in the Game constructor, so it will have been created already when this is called).
|
||||
* is created (currently the context is created in the Game constructor, so it will have been created already when this is
|
||||
* called).
|
||||
*/
|
||||
void initialize_gl();
|
||||
|
||||
|
@ -422,7 +425,8 @@ private:
|
|||
}
|
||||
|
||||
/*!
|
||||
* Remove coin from the enemy and the level. If quest or arcade mode is active and flag is not set, add the coin to the appropriate bank.
|
||||
* Remove coin from the enemy and the level. If quest or arcade mode is active and flag is not set, add the coin to the
|
||||
* appropriate bank.
|
||||
*
|
||||
* @param add_to_bank flag to either add to the bank or not
|
||||
*/
|
||||
|
@ -433,8 +437,8 @@ private:
|
|||
*/
|
||||
void end_game_over_display();
|
||||
|
||||
/* This animation can be used to end the game over state after the time limit is reached. Play once with a delay to let the game over screen
|
||||
* display temporarily before being ended by this animation. */
|
||||
/* This animation can be used to end the game over state after the time limit is reached. Play once with a delay to let the
|
||||
* game over screen display temporarily before being ended by this animation. */
|
||||
Animation game_over_animation {std::bind(&Cakefoot::end_game_over_display, this)};
|
||||
|
||||
/*!
|
||||
|
@ -480,7 +484,8 @@ private:
|
|||
Animation splash_animation {std::bind(&Cakefoot::next_splash, this)};
|
||||
|
||||
/*!
|
||||
* Set arcade time limit warning state at Cakefoot::arcade_limit_warning based on mode, time remaining, and whether the blinking frame is on or off.
|
||||
* Set arcade time limit warning state at Cakefoot::arcade_limit_warning based on mode, time remaining, and whether the
|
||||
* blinking frame is on or off.
|
||||
*/
|
||||
void flash_warning();
|
||||
|
||||
|
|
Loading…
Reference in New Issue