Compare commits
8
Commits
cd1d9ef0e4
...
dda388868c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dda388868c | ||
|
|
fb7bfd6bf3 | ||
|
|
5395225cc2 | ||
|
|
200e05f2dc | ||
|
|
404c320973 | ||
|
|
d6c52cecda | ||
|
|
25bd7a85a1 | ||
|
|
0f393af931 |
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -eo pipefail
|
||||||
|
|
||||||
# Find the odin binary
|
# Find the odin binary
|
||||||
export PATH="$HOME/.odin:$PATH"
|
export PATH="$HOME/.odin:$PATH"
|
||||||
@@ -9,26 +9,72 @@ SRC_DIR="src"
|
|||||||
OUT_DIR="out"
|
OUT_DIR="out"
|
||||||
OUT_NAME="pong"
|
OUT_NAME="pong"
|
||||||
|
|
||||||
|
# flags for every odin build; unix-style errors (file:line:col: msg) are what the vis error picker parses
|
||||||
|
ODIN_FLAGS=(-error-pos-style:unix)
|
||||||
|
|
||||||
|
# ---- ctags ------------------------------------------------------------
|
||||||
|
# tags = project tags (-t, vis runs it on every .odin save)
|
||||||
|
# + stdlib tags (-T, once, or after an Odin update), shared by all projects
|
||||||
|
TAGS_FILE="tags"
|
||||||
|
STD_TAGS="${XDG_CACHE_HOME:-$HOME/.cache}/odin-tags/std.tags"
|
||||||
|
|
||||||
|
# --languages=Odin keeps vendor's bundled C/JS out of the index;
|
||||||
|
# --fields=+n adds the line:N field the vis pickers jump to
|
||||||
|
CTAGS_OPTS=(--languages=Odin --fields=+n)
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
BUILD_ONLY=0
|
BUILD_ONLY=0
|
||||||
OPTIMIZED=0
|
OPTIMIZED=0
|
||||||
WEB=0
|
WEB=0
|
||||||
CLEAN=0
|
CLEAN=0
|
||||||
DEBUGGER=0
|
DEBUGGER=0
|
||||||
VALGRIND=0
|
VALGRIND=0
|
||||||
|
TAGS=""
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage: $0 [-b] [-o] [-w] [-d] [-v] [-c] [-h]"
|
echo "Usage: $0 [-b] [-o] [-w] [-d] [-v] [-c] [-t] [-T] [-h]"
|
||||||
echo " -b build only, don't run"
|
echo " -b build only, don't run"
|
||||||
echo " -o optimized release build (-o:speed, no debug info)"
|
echo " -o optimized release build (-o:speed, no debug info)"
|
||||||
echo " -w web/WASM build (js_wasm32 target)"
|
echo " -w web/WASM build (js_wasm32 target)"
|
||||||
echo " -d build then launch in debugger instead of running"
|
echo " -d build then launch in debugger instead of running"
|
||||||
echo " -v build then run under Valgrind"
|
echo " -v build then run under Valgrind"
|
||||||
echo " -c clean build artifacts"
|
echo " -c clean build artifacts"
|
||||||
|
echo " -t re-index $SRC_DIR and merge it with the stdlib tags"
|
||||||
|
echo " -T index Odin core + vendor (once, or after an Odin update)"
|
||||||
echo " -h show this help"
|
echo " -h show this help"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
while getopts "bowdvch" opt; do
|
# stdlib tags, header lines stripped so the merged file keeps only one set
|
||||||
|
generate_std_tags() {
|
||||||
|
local root
|
||||||
|
root="$(odin root 2>/dev/null || echo "$HOME/.odin")"
|
||||||
|
root="${root%/}"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$STD_TAGS")"
|
||||||
|
ctags -R -f - "${CTAGS_OPTS[@]}" "$root/core" "$root/vendor" | grep -v '^!_TAG_' > "$STD_TAGS.tmp"
|
||||||
|
mv -f "$STD_TAGS.tmp" "$STD_TAGS"
|
||||||
|
echo "Stdlib tags written -> $STD_TAGS"
|
||||||
|
}
|
||||||
|
|
||||||
|
# project tags merged with the stdlib tags; both inputs are byte-sorted
|
||||||
|
generate_tags() {
|
||||||
|
if [ ! -f "$STD_TAGS" ]; then
|
||||||
|
echo "No stdlib tags; run $0 -T first" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# $$ keeps two overlapping on-save runs from sharing temp files
|
||||||
|
local src_tags="$TAGS_FILE.src.$$"
|
||||||
|
local merged="$TAGS_FILE.tmp.$$"
|
||||||
|
|
||||||
|
ctags -R -f "$src_tags" "${CTAGS_OPTS[@]}" "$SRC_DIR"
|
||||||
|
LC_ALL=C sort -m -u "$src_tags" "$STD_TAGS" > "$merged"
|
||||||
|
mv -f "$merged" "$TAGS_FILE"
|
||||||
|
rm -f "$src_tags"
|
||||||
|
}
|
||||||
|
|
||||||
|
while getopts "bowdvctTh" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
b) BUILD_ONLY=1 ;;
|
b) BUILD_ONLY=1 ;;
|
||||||
o) OPTIMIZED=1 ;;
|
o) OPTIMIZED=1 ;;
|
||||||
@@ -36,11 +82,18 @@ while getopts "bowdvch" opt; do
|
|||||||
d) DEBUGGER=1 ;;
|
d) DEBUGGER=1 ;;
|
||||||
v) VALGRIND=1 ;;
|
v) VALGRIND=1 ;;
|
||||||
c) CLEAN=1 ;;
|
c) CLEAN=1 ;;
|
||||||
|
t) TAGS=src ;;
|
||||||
|
T) TAGS=std ;;
|
||||||
h) usage ;;
|
h) usage ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
case "$TAGS" in
|
||||||
|
src) generate_tags; exit 0 ;;
|
||||||
|
std) generate_std_tags; exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
mkdir -p "$OUT_DIR"
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
if [ "$CLEAN" -eq 1 ]; then
|
if [ "$CLEAN" -eq 1 ]; then
|
||||||
@@ -51,7 +104,7 @@ fi
|
|||||||
|
|
||||||
if [ "$WEB" -eq 1 ]; then
|
if [ "$WEB" -eq 1 ]; then
|
||||||
echo "Building web (WASM)..."
|
echo "Building web (WASM)..."
|
||||||
odin build "$SRC_DIR" \
|
odin build "$SRC_DIR" "${ODIN_FLAGS[@]}" \
|
||||||
-target:js_wasm32 \
|
-target:js_wasm32 \
|
||||||
-out:"$OUT_DIR/$OUT_NAME.wasm" \
|
-out:"$OUT_DIR/$OUT_NAME.wasm" \
|
||||||
$( [ "$OPTIMIZED" -eq 1 ] && echo "-o:speed" || echo "-debug" )
|
$( [ "$OPTIMIZED" -eq 1 ] && echo "-o:speed" || echo "-debug" )
|
||||||
@@ -67,15 +120,16 @@ fi
|
|||||||
|
|
||||||
if [ "$OPTIMIZED" -eq 1 ]; then
|
if [ "$OPTIMIZED" -eq 1 ]; then
|
||||||
echo "Building optimized ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
echo "Building optimized ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
||||||
odin build "$SRC_DIR" \
|
odin build "$SRC_DIR" "${ODIN_FLAGS[@]}" \
|
||||||
-out:"$OUT_DIR/$OUT_NAME" \
|
-out:"$OUT_DIR/$OUT_NAME" \
|
||||||
-o:speed u
|
-o:speed \
|
||||||
-no-bounds-check
|
-no-bounds-check
|
||||||
else
|
else
|
||||||
echo "Building debug ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
echo "Building debug ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
||||||
odin build "$SRC_DIR" \
|
odin build "$SRC_DIR" "${ODIN_FLAGS[@]}" \
|
||||||
-out:"$OUT_DIR/$OUT_NAME" \
|
-out:"$OUT_DIR/$OUT_NAME" \
|
||||||
-define:DEBUG_MODE=true
|
-debug \
|
||||||
|
-define:DEBUG_MODE=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Build complete."
|
echo "Build complete."
|
||||||
@@ -87,6 +141,7 @@ if [ "$BUILD_ONLY" -eq 0 ]; then
|
|||||||
lldb "$OUT_DIR/$OUT_NAME"
|
lldb "$OUT_DIR/$OUT_NAME"
|
||||||
|
|
||||||
elif [ "$VALGRIND" -eq 1 ]; then
|
elif [ "$VALGRIND" -eq 1 ]; then
|
||||||
|
VG_STATUS=0
|
||||||
valgrind \
|
valgrind \
|
||||||
--leak-check=full \
|
--leak-check=full \
|
||||||
--show-leak-kinds=definite,possible \
|
--show-leak-kinds=definite,possible \
|
||||||
@@ -94,11 +149,13 @@ if [ "$BUILD_ONLY" -eq 0 ]; then
|
|||||||
--errors-for-leak-kinds=definite,possible \
|
--errors-for-leak-kinds=definite,possible \
|
||||||
--error-exitcode=1 \
|
--error-exitcode=1 \
|
||||||
--log-file="$OUT_DIR/valgrind.log" \
|
--log-file="$OUT_DIR/valgrind.log" \
|
||||||
"./$OUT_DIR/$OUT_NAME"
|
"./$OUT_DIR/$OUT_NAME" || VG_STATUS=$?
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Valgrind report written to $OUT_DIR/valgrind.log"
|
echo "Valgrind report written to $OUT_DIR/valgrind.log"
|
||||||
|
[ "$VG_STATUS" -ne 0 ] && echo "Valgrind reported errors (exit $VG_STATUS)"
|
||||||
|
exit "$VG_STATUS"
|
||||||
else
|
else
|
||||||
"./$OUT_DIR/$OUT_NAME"
|
"./$OUT_DIR/$OUT_NAME"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"folders":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"path": "."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"build_systems":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Run Build Script",
|
|
||||||
"cmd": ["/bin/bash", "build.sh"],
|
|
||||||
"working_dir": "$project_path",
|
|
||||||
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
|
|
||||||
"variants":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Run with Arguments...",
|
|
||||||
"cmd": ["/bin/bash", "build.sh", "$select_args"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Build Only (-b)",
|
|
||||||
"cmd": ["/bin/bash", "build.sh", "-b"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Optimized (-o)",
|
|
||||||
"cmd": ["/bin/bash", "build.sh", "-o"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Debug in LLDB (-d)",
|
|
||||||
"cmd": ["/bin/bash", "build.sh", "-d"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Valgrind (-v)",
|
|
||||||
"cmd": ["/bin/bash", "build.sh", "-v"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Clean (-c)",
|
|
||||||
"cmd": ["/bin/bash", "build.sh", "-c"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
+11
-4
@@ -21,9 +21,13 @@ init_ball :: proc() -> Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serve_ball :: proc(ball: ^Entity) {
|
serve_ball :: proc(ball: ^Entity, serve_dir: u8) {
|
||||||
|
dir_x: f32 = 0.0
|
||||||
// Randomize initial direction: -1.0 or 1.0
|
// Randomize initial direction: -1.0 or 1.0
|
||||||
dir_x := f32(rand.float32() > 0.5 ? 1 : -1)
|
if serve_dir == 0 do dir_x = f32(rand.float32() > 0.5 ? 1 : -1)
|
||||||
|
if serve_dir == 1 do dir_x = 1.0
|
||||||
|
if serve_dir == 2 do dir_x = -1.0
|
||||||
|
|
||||||
dir_y := rand.choice([]f32{-0.1, 0.1}) // Slightly angled launch
|
dir_y := rand.choice([]f32{-0.1, 0.1}) // Slightly angled launch
|
||||||
ball.vel = rl.Vector2Normalize({dir_x, dir_y}) * ball.speed
|
ball.vel = rl.Vector2Normalize({dir_x, dir_y}) * ball.speed
|
||||||
}
|
}
|
||||||
@@ -32,6 +36,8 @@ reset_ball :: proc(ball: ^Entity) {
|
|||||||
ball.pos = rl.Vector2{(VIEWPORT_W / 2), (VIEWPORT_H / 2)}
|
ball.pos = rl.Vector2{(VIEWPORT_W / 2), (VIEWPORT_H / 2)}
|
||||||
ball.vel = rl.Vector2{0,0}
|
ball.vel = rl.Vector2{0,0}
|
||||||
ball.speed = 300
|
ball.speed = 300
|
||||||
|
ball.trail_positions = {}
|
||||||
|
ball.trail_count = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
update_ball :: proc(ball: ^Entity, level: ^Level, dt: f32) {
|
update_ball :: proc(ball: ^Entity, level: ^Level, dt: f32) {
|
||||||
@@ -45,7 +51,7 @@ update_ball :: proc(ball: ^Entity, level: ^Level, dt: f32) {
|
|||||||
level.serve_timer -= dt
|
level.serve_timer -= dt
|
||||||
|
|
||||||
if level.serve_timer <= 0 {
|
if level.serve_timer <= 0 {
|
||||||
serve_ball(ball)
|
serve_ball(ball, level.serve_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -86,12 +92,14 @@ update_ball :: proc(ball: ^Entity, level: ^Level, dt: f32) {
|
|||||||
increment_score(&level.score_player, level.score_win, true, ball)
|
increment_score(&level.score_player, level.score_win, true, ball)
|
||||||
reset_ball(ball)
|
reset_ball(ball)
|
||||||
level.serve_timer = level.wait_time
|
level.serve_timer = level.wait_time
|
||||||
|
level.serve_dir = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if ball.pos.x <= field_bounds.x + ball.size.x {
|
if ball.pos.x <= field_bounds.x + ball.size.x {
|
||||||
increment_score(&level.score_enemy, level.score_win, false, ball)
|
increment_score(&level.score_enemy, level.score_win, false, ball)
|
||||||
reset_ball(ball)
|
reset_ball(ball)
|
||||||
level.serve_timer = level.wait_time
|
level.serve_timer = level.wait_time
|
||||||
|
level.serve_dir = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
ball.pos += ball.vel * dt
|
ball.pos += ball.vel * dt
|
||||||
@@ -113,7 +121,6 @@ increment_score :: proc(score_side: ^int, max_score: int, player: bool, ball: ^E
|
|||||||
score_side^ += 1
|
score_side^ += 1
|
||||||
if score_side^ == max_score {
|
if score_side^ == max_score {
|
||||||
emit_event(Game_Over_Data{player})
|
emit_event(Game_Over_Data{player})
|
||||||
ball.trail_count = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ update_opponent :: proc(self: ^Entity, ball: Entity, bounds: rl.Rectangle, dt: f
|
|||||||
// center of ball - center of paddle gives the target
|
// center of ball - center of paddle gives the target
|
||||||
self.target_y = (ball.pos.y + (ball.size.y / 2)) - (self.size.y / 2) + error_offset
|
self.target_y = (ball.pos.y + (ball.size.y / 2)) - (self.size.y / 2) + error_offset
|
||||||
|
|
||||||
|
// TODO: make this and the deadzone change with difficulty
|
||||||
self.reaction_timer = 0.15
|
self.reaction_timer = 0.15
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+30
-3
@@ -78,15 +78,38 @@ process_events :: proc(game: ^Game) {
|
|||||||
case .Game_Pause:
|
case .Game_Pause:
|
||||||
push_scene(game, make_pause_overlay())
|
push_scene(game, make_pause_overlay())
|
||||||
|
|
||||||
case .Game_Resume, .Game_Back:
|
case .Game_Resume:
|
||||||
pop_scene(game)
|
pop_scene(game)
|
||||||
|
|
||||||
case .Game_Settings:
|
case .Game_Settings:
|
||||||
push_scene(game, make_settings_menu(&game.audio, RESOLUTIONS[game.res_index].label))
|
parent: Maybe(Scene_Type) = nil
|
||||||
|
if active := get_active_scene(game); active != nil {
|
||||||
|
parent = active.type
|
||||||
|
}
|
||||||
|
|
||||||
|
pop_scene(game)
|
||||||
|
push_scene(game, make_settings_menu(parent, &game.audio, RESOLUTIONS[game.res_index].label))
|
||||||
|
|
||||||
case .Game_Main_Menu:
|
case .Game_Main_Menu:
|
||||||
set_root_scene(game, make_main_menu())
|
set_root_scene(game, make_main_menu())
|
||||||
audio_fade_out(&game.audio, duration = 1.0)
|
audio_fade_out(&game.audio, duration = 1.0)
|
||||||
|
|
||||||
|
case .Game_Back:
|
||||||
|
// Read parent from active scene
|
||||||
|
if active := get_active_scene(game); active != nil {
|
||||||
|
if parent, ok := active.parent.?; ok {
|
||||||
|
pop_scene(game)
|
||||||
|
|
||||||
|
#partial switch parent {
|
||||||
|
case .Pause_Menu: push_scene(game, make_pause_overlay())
|
||||||
|
case .Main_Menu: push_scene(game, make_main_menu())
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// still pop when no parent linked ie resuming Gameplay
|
||||||
|
pop_scene(game)
|
||||||
|
|
||||||
case .Game_Quit:
|
case .Game_Quit:
|
||||||
game.should_close = true
|
game.should_close = true
|
||||||
@@ -97,4 +120,8 @@ process_events :: proc(game: ^Game) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event_manager_quit :: proc(game: ^Game) {
|
||||||
|
delete(GLOBAL_EVENTS)
|
||||||
|
}
|
||||||
|
|||||||
+8
-10
@@ -1,4 +1,4 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:math/rand"
|
import "core:math/rand"
|
||||||
@@ -137,18 +137,17 @@ game_render :: proc(game: ^Game) {
|
|||||||
rl.ClearBackground(rl.BLACK)
|
rl.ClearBackground(rl.BLACK)
|
||||||
|
|
||||||
rl.BeginTextureMode(game.viewport)
|
rl.BeginTextureMode(game.viewport)
|
||||||
|
|
||||||
rl.DrawTexture(game.bg_texture, 0, 0, rl.WHITE)
|
rl.DrawTexture(game.bg_texture, 0, 0, rl.WHITE)
|
||||||
|
|
||||||
// World/Scene objects rendered through 2D camera space
|
// World/Scene objects rendered through 2D camera space
|
||||||
rl.BeginMode2D(game.camera)
|
rl.BeginMode2D(game.camera)
|
||||||
|
// Render all active items on the stack from bottom to top
|
||||||
// @note: fine for now but probably not a good idea...
|
for &scene in game.scene_stack {
|
||||||
// Render all scenes in the stack from bottom to top
|
scene_render(&scene)
|
||||||
for i in 0 ..< len(game.scene_stack) {
|
|
||||||
scene_render(&game.scene_stack[i])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rl.EndMode2D()
|
rl.EndMode2D()
|
||||||
|
|
||||||
rl.EndTextureMode()
|
rl.EndTextureMode()
|
||||||
|
|
||||||
render_game_to_window(game)
|
render_game_to_window(game)
|
||||||
@@ -160,13 +159,12 @@ game_render :: proc(game: ^Game) {
|
|||||||
|
|
||||||
game_quit :: proc(game: ^Game) {
|
game_quit :: proc(game: ^Game) {
|
||||||
audio_manager_quit(&game.audio)
|
audio_manager_quit(&game.audio)
|
||||||
|
scene_manager_quit(game)
|
||||||
|
event_manager_quit(game)
|
||||||
|
|
||||||
rl.UnloadShader(game.crt_shader)
|
rl.UnloadShader(game.crt_shader)
|
||||||
rl.UnloadTexture(game.bg_texture)
|
rl.UnloadTexture(game.bg_texture)
|
||||||
rl.UnloadRenderTexture(game.viewport)
|
rl.UnloadRenderTexture(game.viewport)
|
||||||
delete(GLOBAL_EVENTS)
|
|
||||||
delete(game.scene_stack)
|
|
||||||
|
|
||||||
rl.CloseWindow()
|
rl.CloseWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ Level :: struct {
|
|||||||
player: Entity,
|
player: Entity,
|
||||||
opponent: Entity,
|
opponent: Entity,
|
||||||
ball: Entity,
|
ball: Entity,
|
||||||
|
serve_dir: u8,
|
||||||
serve_timer: f32,
|
serve_timer: f32,
|
||||||
wait_time: f32,
|
wait_time: f32,
|
||||||
score_player: int,
|
score_player: int,
|
||||||
@@ -55,7 +56,7 @@ init_level :: proc() -> Level {
|
|||||||
ball = init_ball(),
|
ball = init_ball(),
|
||||||
serve_timer = SERVE_WAIT_TIME,
|
serve_timer = SERVE_WAIT_TIME,
|
||||||
wait_time = SERVE_WAIT_TIME,
|
wait_time = SERVE_WAIT_TIME,
|
||||||
score_win = 1,
|
score_win = 3,
|
||||||
score_player = 0,
|
score_player = 0,
|
||||||
score_enemy = 0,
|
score_enemy = 0,
|
||||||
particles = make([dynamic]Particle),
|
particles = make([dynamic]Particle),
|
||||||
|
|||||||
+21
-2
@@ -16,6 +16,7 @@ Scene_Type :: enum {
|
|||||||
|
|
||||||
Scene :: struct {
|
Scene :: struct {
|
||||||
type: Scene_Type,
|
type: Scene_Type,
|
||||||
|
parent: Maybe(Scene_Type),
|
||||||
level: Maybe(Level),
|
level: Maybe(Level),
|
||||||
canvas: Maybe(Canvas),
|
canvas: Maybe(Canvas),
|
||||||
winner_is_player: bool,
|
winner_is_player: bool,
|
||||||
@@ -26,7 +27,7 @@ push_scene :: proc(game: ^Game, scene: Scene) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pop_scene :: proc(game: ^Game) {
|
pop_scene :: proc(game: ^Game) {
|
||||||
if len(game.scene_stack) > 1 {
|
if len(game.scene_stack) > 0 {
|
||||||
top_index := len(game.scene_stack) - 1
|
top_index := len(game.scene_stack) - 1
|
||||||
destroy_scene(&game.scene_stack[top_index])
|
destroy_scene(&game.scene_stack[top_index])
|
||||||
pop(&game.scene_stack)
|
pop(&game.scene_stack)
|
||||||
@@ -101,6 +102,13 @@ scene_render :: proc(scene: ^Scene) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scene_manager_quit :: proc(game: ^Game) {
|
||||||
|
for &scene in game.scene_stack {
|
||||||
|
destroy_scene(&scene)
|
||||||
|
}
|
||||||
|
delete(game.scene_stack)
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
// UI Layout & Styling Constants
|
// UI Layout & Styling Constants
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
@@ -220,6 +228,7 @@ make_main_menu :: proc() -> Scene {
|
|||||||
|
|
||||||
return Scene{
|
return Scene{
|
||||||
type = .Main_Menu,
|
type = .Main_Menu,
|
||||||
|
parent = nil,
|
||||||
canvas = Canvas{
|
canvas = Canvas{
|
||||||
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
||||||
padding = UI_TITLE_PADDING,
|
padding = UI_TITLE_PADDING,
|
||||||
@@ -289,6 +298,7 @@ make_pause_overlay :: proc() -> Scene {
|
|||||||
|
|
||||||
return Scene{
|
return Scene{
|
||||||
type = .Pause_Menu,
|
type = .Pause_Menu,
|
||||||
|
parent = nil,
|
||||||
canvas = Canvas{
|
canvas = Canvas{
|
||||||
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
||||||
padding = UI_TITLE_PADDING,
|
padding = UI_TITLE_PADDING,
|
||||||
@@ -356,7 +366,7 @@ update_pause_menu :: proc(scene: ^Scene, input: Input_Event) {
|
|||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
// Settings Menu
|
// Settings Menu
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
make_settings_menu :: proc(audio: ^Audio_Manager, current_res_label: cstring) -> Scene {
|
make_settings_menu :: proc(parent: Maybe(Scene_Type), audio: ^Audio_Manager, current_res_label: cstring) -> Scene {
|
||||||
ui_items:[8]UI_Item = {
|
ui_items:[8]UI_Item = {
|
||||||
0 = UI_Button{"Back"},
|
0 = UI_Button{"Back"},
|
||||||
1 = UI_Selector{
|
1 = UI_Selector{
|
||||||
@@ -377,6 +387,7 @@ make_settings_menu :: proc(audio: ^Audio_Manager, current_res_label: cstring) ->
|
|||||||
|
|
||||||
return Scene{
|
return Scene{
|
||||||
type = .Settings_Menu,
|
type = .Settings_Menu,
|
||||||
|
parent = parent,
|
||||||
canvas = Canvas{
|
canvas = Canvas{
|
||||||
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
||||||
padding = UI_TITLE_PADDING,
|
padding = UI_TITLE_PADDING,
|
||||||
@@ -398,6 +409,12 @@ render_settings_menu :: proc(scene: Scene) {
|
|||||||
menu, menu_ok := canvas.menu.?
|
menu, menu_ok := canvas.menu.?
|
||||||
assert(menu_ok, "render_settings_menu: Canvas is missing a UI_Menu component")
|
assert(menu_ok, "render_settings_menu: Canvas is missing a UI_Menu component")
|
||||||
|
|
||||||
|
|
||||||
|
if scene.parent != .Main_Menu {
|
||||||
|
// Draw dark overlay so gameplay/background stays visible behind Settings
|
||||||
|
rl.DrawRectangleRec(canvas.bounds, rl.Color{0, 0, 0, 200})
|
||||||
|
}
|
||||||
|
|
||||||
t_text: cstring = "SETTINGS"
|
t_text: cstring = "SETTINGS"
|
||||||
t_width := rl.MeasureText(t_text, UI_TITLE_FONT_SIZE)
|
t_width := rl.MeasureText(t_text, UI_TITLE_FONT_SIZE)
|
||||||
t_x := i32(canvas.bounds.width * 0.5) - (t_width / 2)
|
t_x := i32(canvas.bounds.width * 0.5) - (t_width / 2)
|
||||||
@@ -465,6 +482,7 @@ make_game_over_overlay :: proc(winner_is_player: bool) -> Scene {
|
|||||||
|
|
||||||
return Scene{
|
return Scene{
|
||||||
type = .Game_Over,
|
type = .Game_Over,
|
||||||
|
parent = nil,
|
||||||
winner_is_player = winner_is_player,
|
winner_is_player = winner_is_player,
|
||||||
canvas = Canvas{
|
canvas = Canvas{
|
||||||
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
bounds = rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H},
|
||||||
@@ -530,6 +548,7 @@ update_game_over :: proc(scene: ^Scene, input: Input_Event) {
|
|||||||
make_gameplay_scene :: proc() -> Scene {
|
make_gameplay_scene :: proc() -> Scene {
|
||||||
return Scene{
|
return Scene{
|
||||||
type = .Gameplay,
|
type = .Gameplay,
|
||||||
|
parent = nil,
|
||||||
level = init_level(),
|
level = init_level(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user