flash time limit warning, clickable social media buttons, update css layout

This commit is contained in:
ohsqueezy 2024-01-02 23:51:22 -08:00
parent 91081b7900
commit 9aa9149781
13 changed files with 187 additions and 66 deletions

View File

@ -199,8 +199,8 @@ Cakefoot-linux_debug.x86_64 : $(LINUX_DEBUG_OBJ)
EMSCRIPTENHOME = $(HOME)/ext/software/emsdk/upstream/emscripten
EMSCRIPTEN_CFLAGS = -Oz -Wall -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="['png', 'jpg']" -s USE_SDL_TTF=2 -s USE_SDL_MIXER=2 \
-I $(SB_LIB_DIR) -I $(SB_SRC_DIR)
EMSCRIPTEN_LFLAGS = -s MIN_WEBGL_VERSION=2 -s EXPORTED_FUNCTIONS="['_main', '_malloc', '_pause_for_ads', '_unpause_for_ads']" -s LLD_REPORT_UNDEFINED \
-s NO_DISABLE_EXCEPTION_CATCHING -s FULL_ES3=1 -lidbfs.js -s ALLOW_MEMORY_GROWTH=1
EMSCRIPTEN_LFLAGS = -s MIN_WEBGL_VERSION=2 -s EXPORTED_FUNCTIONS="['_main', '_malloc', '_pause_for_ads', '_unpause_for_ads']" \
-s LLD_REPORT_UNDEFINED -s NO_DISABLE_EXCEPTION_CATCHING -s FULL_ES3=1 -lidbfs.js -s ALLOW_MEMORY_GROWTH=1
EMSCRIPTEN_PRELOADS = --preload-file "config.json" --preload-file "resource/" --preload-file "src/shaders/" --preload-file "src/config_wasm.json" \
--pre-js "src/pre_js.js"
EMSCRIPTEN_GAME_CONFIGS = config.json src/config_wasm.json resource/levels.json

View File

@ -61,6 +61,14 @@
"social qr translation": [-1.32, -0.55],
"social web translation": [1.42, -0.82],
"social scale": [0.35, 0.11],
"social diskmem texture": "resource/Social_media_diskmem.png",
"social azuria sky texture": "resource/Social_media_azuria_sky.png",
"social single scale": 0.35,
"social single ratio": 0.16333333,
"social diskmem translation": [1.42, -0.75],
"social azuria sky translation": [1.42, -0.87],
"social diskmem url": "https://twitter.com/diskmem",
"social azuria sky url": "https://twitter.com/azuria_sky",
"end screen timeout": 40.0,
"enemy sprite scale": 0.024691358,
"quest best text": "★ ",
@ -75,11 +83,15 @@
"flash darken factor": 2.0,
"fullscreen enabled": true,
"blink frequency": 0.35,
"splash": [
["resource/splash_spacebox.png", [110.0, 161.0, 201.0, 255.0], 2.0]
],
"splash": [],
"default initials": "AAA",
"name entry enabled": true
"name entry enabled": true,
"arcade warning start": 10.0,
"arcade warning frequency": [0.5, 0.075],
"arcade warning color": [0.5, 0.0, 0.0, 1.0],
"auto save translation": [-1.45, -0.65],
"auto save scale": [0.325, 0.15],
"social media click": false
},
"configuration":
@ -166,7 +178,8 @@
"texture":
{
"coin": ["resource/coin/coin-0.png"],
"flame": ["resource/flame/flame-1.png", "resource/flame/flame-2.png"]
"flame": ["resource/flame/flame-1.png", "resource/flame/flame-2.png"],
"auto save": "resource/Autosave.png"
},
"curve":

View File

@ -22,30 +22,25 @@
background: #000;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 85% 1fr 5%;
height: 100%;
font-family: RoundedMPlus2MMedium;
font-size: 18px;
text-align: center;
/* Gap between the screen and text */
grid-gap: 3%;
justify-content: center;
align-items: center;
}
canvas
{
margin: 0px auto;
width: 100%;
max-height: 100%;
aspect-ratio: 16 / 9;
align-self: center;
height: 100%;
display: none;
}
div#message
{
margin: 0px auto;
padding: 0% 1%;
align-self: start;
overflow: scroll;
}
a
@ -53,28 +48,32 @@
color: #999;
}
div#title
{
color: #fff;
font-family: RoundedMPlus2pThin;
margin-bottom: 8px;
font-size: 28px;
}
div#session
{
font-family: NotCourierSans;
font-size: 18px;
text-align: center;
align-self: end;
padding: 0% 1%;
margin-top: 8px;
}
p#loading
{
font-size: 72px;
}
span.bullet
{
color: transparent;
text-shadow: 0 0 0 #444;
}
@media (orientation: portrait)
{
body
{
grid-template-rows: 80% 20%;
}
}
@media (orientation: landscape)
{
body
{
grid-template-rows: 94% 6%;
}
}
</style>
</head>
@ -87,12 +86,9 @@
<canvas id="canvas"></canvas>
<div id="message">
Welcome to Cakefoot early access! Use &#x1F5B1;&#xFE0F;, &#x2328;&#xFE0F; or 🎮 to play. Send 🦋 bugs 🦋 and feedback to <i>mailbox at shampoo.ooo</i>.
Visit the <a href="https://x.com/wondervillenyc/status/1735827245384572940" target="_new">custom arcade cabinet</a> 🕹&#xFE0F;
<br/>
<div id="session"></div>
Cakefoot by <a href="https://ohsqueezy.itch.io">@ohsqueezy</a>👾&#xFE0F; <span class="bullet">🔸</span> Use &#x1F5B1;&#xFE0F;, &#x2328;&#xFE0F;
or 🎮 to play <span class="bullet">🔸</span> <a href="#" onclick="document.getElementById('canvas').requestFullscreen()">Go fullscreen</a>
<span class="bullet">🔸</span> Visit the <a href="https://x.com/wondervillenyc/status/1735827245384572940" target="_new">arcade cabinet</a> 🕹&#xFE0F;
</div>
<script>

BIN
resource/Autosave.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -180,8 +180,9 @@ Cakefoot::Cakefoot(std::initializer_list<std::string> configuration_merge) : Gam
shift_hue_animation.frame_length(configuration()("display", "hue shift frequency"));
shift_hue_animation.play();
/* Start blink animation */
/* Start screen effect animations */
blink_animation.play();
warning_animation.play();
if (!use_play_button)
{
@ -779,6 +780,49 @@ void Cakefoot::set_up_buttons()
button.at("fullscreen").on_state_change([&](bool state){
display.toggle_fullscreen();
});
/* Set up social buttons */
sb::Texture diskmem_texture {configuration()("display", "social diskmem texture").get<std::string>()};
diskmem_texture.load();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
sb::Plane diskmem_plane;
diskmem_plane.texture(diskmem_texture);
button.at("diskmem") = sb::Pad<>{diskmem_plane, configuration()("display", "social diskmem translation"), configuration()("display", "social single scale"),
configuration()("display", "social single ratio")};
button.at("diskmem").on_state_change([&](bool state){
#if defined(EMSCRIPTEN) && !defined(__COOLMATH__)
EM_ASM(
{
Object.assign(document.createElement('a'), {
target: '_blank',
rel: 'noopener noreferrer',
href: UTF8ToString($0),
}).click();
}, configuration()("display", "social diskmem url").get<std::string>().c_str());
#endif
});
sb::Texture azuria_sky_texture {configuration()("display", "social azuria sky texture").get<std::string>()};
azuria_sky_texture.load();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
sb::Plane azuria_sky_plane;
azuria_sky_plane.texture(azuria_sky_texture);
button.at("azuria sky") = sb::Pad<>{
azuria_sky_plane, configuration()("display", "social azuria sky translation"), configuration()("display", "social single scale"),
configuration()("display", "social single ratio")};
button.at("azuria sky").on_state_change([&](bool state){
#if defined(EMSCRIPTEN) && !defined(__COOLMATH__)
EM_ASM(
{
Object.assign(document.createElement('a'), {
target: '_blank',
rel: 'noopener noreferrer',
href: UTF8ToString($0),
}).click();
}, configuration()("display", "social azuria sky url").get<std::string>().c_str());
#endif
});
}
void Cakefoot::toggle_challenge()
@ -932,6 +976,10 @@ void Cakefoot::set_up_hud()
social.translate(configuration()("display", social_translation));
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.translate(configuration()("display", "auto save translation"));
/* 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>());
@ -1368,6 +1416,7 @@ void Cakefoot::load_level(int index)
if (index == 0 || end_screen())
{
run_timer.off();
arcade_limit_warning = false;
/* In arcade mode, reset the clock on the title screen */
if (arcade() && index == 0)
@ -1723,6 +1772,25 @@ void Cakefoot::next_splash()
}
}
void Cakefoot::flash_warning()
{
if (arcade() && run_timer && run_timer.elapsed() + configuration()("display", "arcade warning start").get<float>() > limit())
{
arcade_limit_warning = !arcade_limit_warning;
/* Depth into the warning range determines the speed of the warning */
nlohmann::json frequency = configuration()("display", "arcade warning frequency");
float delay = (run_timer.elapsed() + configuration()("display", "arcade warning start").get<float>() - limit()) /
configuration()("display", "arcade warning start").get<float>() * (
frequency[1].get<float>() - frequency[0].get<float>()) + frequency[0].get<float>();
warning_animation.frame_length(delay);
}
else
{
arcade_limit_warning = false;
}
}
std::string Cakefoot::format_clock(float amount)
{
int minutes = int(amount) / 60;
@ -2058,6 +2126,21 @@ void Cakefoot::respond(SDL_Event& event)
else hovering = true;
} } }
/* Collide with social buttons */
else if (level_index == 0 && configuration()("display", "social media click"))
{
for (const std::string& button_name : {"diskmem", "azuria sky"})
{
if (button.at(button_name).collide(mouse_ndc, view, projection))
{
if (event.type == SDL_MOUSEBUTTONDOWN)
{
button.at(button_name).press();
button_pressed = true;
}
else hovering = true;
} } }
/* Rotate scene */
if (event.type == SDL_MOUSEMOTION && left_mouse_pressed && shift_pressed)
{
@ -2094,6 +2177,7 @@ void Cakefoot::respond(SDL_Event& event)
load_level(0);
unpaused_timer.on();
run_timer.reset();
arcade_limit_warning = false;
/* In arcade-only mode, reset the level select to the first level */
if (configuration()("display", "arcade only"))
@ -2361,6 +2445,7 @@ void Cakefoot::update(float timestamp)
blink_animation.update(timestamp);
cooldown_animation.update(timestamp);
splash_animation.update(timestamp);
warning_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});
@ -2402,6 +2487,11 @@ void Cakefoot::update(float timestamp)
glm::vec4 clear = extra_shift.normal() / configuration()("display", "flash darken factor").get<float>() + world_color;
glClearColor(clear.r, clear.g, clear.b, clear.a);
}
else if (arcade_limit_warning)
{
glm::vec4 clear = world_color + configuration()("display", "arcade warning color").get<glm::vec4>();
glClearColor(clear.r, clear.g, clear.b, clear.a);
}
else
{
glClearColor(world_color.r, world_color.g, world_color.b, world_color.a);
@ -2443,6 +2533,9 @@ void Cakefoot::update(float timestamp)
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();
arcade_limit_warning = false;
/* Play once with a delay to let the game over screen display temporarily before the end level is loaded. */
game_over_animation.play_once(configuration()("display", "game over display time"));
@ -2805,7 +2898,7 @@ void Cakefoot::update(float timestamp)
glDrawArrays(GL_TRIANGLES, 0, label.at(name).attributes("position")->count());
} }
/* Draw scoreboard, QR, and quest best on title screen */
/* Draw scoreboard, QR, quest best, social, and auto save icon on title screen */
if (level_index == 0)
{
/* Only draw scoreboard if arcade mode is selected */
@ -2826,7 +2919,7 @@ void Cakefoot::update(float timestamp)
glDrawArrays(GL_TRIANGLES, 0, label.at("quest best").attributes("position")->count());
}
/* Draw QR and social */
/* Draw QR and social. Only draw auto save if QR is not displayed. */
if (configuration()("display", "qr display"))
{
if (configuration()("display", "qr background display"))
@ -2835,7 +2928,19 @@ void Cakefoot::update(float timestamp)
}
qr_code.draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
}
social.draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
else
{
auto_save.draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
if (configuration()("display", "social media click"))
{
button.at("diskmem").draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
button.at("azuria sky").draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
}
}
if (!configuration()("display", "social media click"))
{
social.draw(uniform.at("mvp"), view, projection, uniform.at("texture enabled"));
}
}
/* Draw end screen messages */
@ -2940,7 +3045,6 @@ EM_BOOL respond_to_gamepad_connected(int event_type, const EmscriptenGamepadEven
extern "C"
{
void pause_for_ads()
{
sb::Delegate::post("pause for ads", false);
@ -2950,7 +3054,6 @@ extern "C"
{
sb::Delegate::post("unpause for ads", false);
}
}
#endif

View File

@ -232,7 +232,9 @@ private:
{"name 3", sb::Pad<>()},
{"name 3 increment", sb::Pad<>()},
{"name 3 decrement", sb::Pad<>()},
{"fullscreen", sb::Pad<>()}
{"fullscreen", sb::Pad<>()},
{"diskmem", sb::Pad<>()},
{"azuria sky", sb::Pad<>()}
};
std::map<std::string, std::shared_ptr<TTF_Font>> fonts {
{"medium", font(configuration()("font", "medium", "path").get<std::string>(), configuration()("font", "medium", "size"))},
@ -254,7 +256,8 @@ private:
{"arcade distance", sb::Text(fonts.at("large"))},
{"quest best", sb::Text(fonts.at("glyph"))}
};
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;
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;
glm::vec3 camera_position {0.0f, 0.0f, 2.0f}, subject_position {0.0f, 0.0f, 0.0f};
float zoom = 0.0f;
@ -264,7 +267,7 @@ private:
glm::vec4 world_color {0.2f, 0.2f, 0.2f, 1.0f};
std::map<std::string, sb::audio::Chunk> audio;
Character character {_configuration, audio};
bool use_play_button = false, coin_collected = false, blinking_visible = true;
bool use_play_button = false, coin_collected = false, blinking_visible = true, arcade_limit_warning = false;
ArcadeScores arcade_scores;
ArcadeScores::Score arcade_score;
std::string name_entry;
@ -475,6 +478,14 @@ private:
/* Display splash images in succession until all splash images have been displayed. */
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.
*/
void flash_warning();
/* Test whether or not the arcade time limit warning should be active. */
Animation warning_animation {std::bind(&Cakefoot::flash_warning, this)};
/*!
* Get the arcade time as the amount of time remaining before the limit is reached.
*
@ -570,8 +581,6 @@ EM_BOOL respond_to_gamepad_connected(int event_type, const EmscriptenGamepadEven
extern "C"
{
/*!
* Custom pause event for use with ad APIs that posts a "pause for ads" event using sb::Delegate::post(const std::string&, bool). This function will
* be exported for use in the JavaScript code in* a web build and is not available in other types of builds.
@ -583,7 +592,6 @@ extern "C"
* be exported for use in the JavaScript code in a web build and is not available in other types of builds.
*/
void unpause_for_ads();
}
#endif

View File

@ -8,7 +8,8 @@
["resource/splash_coolmath.png", [36.0, 36.0, 36.0, 255.0], 2.0]
],
"default initials": "CMG",
"name entry enabled": false
"name entry enabled": false,
"social media click": false
},
"keys":

View File

@ -4,7 +4,8 @@
"render driver": "opengles2",
"fluid resize": false,
"use play button": true,
"scoreboard wrap": 3200
"scoreboard wrap": 3200,
"social media click": true
},
"recording":

View File

@ -6,22 +6,20 @@
<meta charset="utf-8">
<style>
body
html, body, canvas
{
margin: 0px;
padding: 0px;
background: #000;
overflow: hidden;
width: 100%;
height: 100%;
}
canvas
{
margin: 0px;
padding: 0px;
width: 100%;
max-width: 100%;
max-height: 100%;
aspect-ratio: 16 / 9;
/* This prevents vertical scrollbars from appearing (more info at
/* https://stackoverflow.com/questions/12989931/body-height-100-displaying-vertical-scrollbar) */
float: left;
}
p#loading
@ -60,7 +58,9 @@
});
/* Fix keyboard input inside an iframe (see https://github.com/emscripten-core/emscripten/issues/5796) */
setInterval(() => window.focus(), 500);
document.getElementById("canvas").addEventListener("click", () => {
window.focus();
});
</script>
</body>
</html>

View File

@ -27,6 +27,7 @@ Module.onRuntimeInitialized = function()
else
{
document.getElementById("loading").remove();
document.getElementById("canvas").style.display = "block";
_main();
}
});

View File

@ -45,8 +45,6 @@ function collectData()
{
return response.json();
}
}).then((response) => {
document.getElementById("session").innerHTML = "Progress is automatically saved to your browser (session ID: " + response["id"] + ")";
});
}
};