Skip to content

Commit 8b9684c

Browse files
feat: add speed and theme settings
1 parent 62f0282 commit 8b9684c

File tree

1 file changed

+202
-12
lines changed

1 file changed

+202
-12
lines changed

games/SimpleSnake.js

Lines changed: 202 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/*
22
@title: SimpleSnake
3-
@description: Just a simple snake game, with kinda good art(?) and some sounds for it.
4-
@author: @wolf-yuan-6115
3+
@author: wolf-yuan-6115
54
@tags: []
65
@addedOn: 2025-08-14
76
*/
@@ -27,6 +26,7 @@ const single_right = "y"
2726
const food = "f"
2827
const wall = "z"
2928
const empty = "e"
29+
const dark_bg = "L"
3030

3131
setLegend(
3232
// Snake head sprites
@@ -396,7 +396,24 @@ setLegend(
396396
................
397397
................
398398
................
399-
................`]
399+
................`],
400+
[dark_bg, bitmap`
401+
LLLLLLLLLLLLLLLL
402+
LLLLLLLLLLLLLLLL
403+
LLLLLLLLLLLLLLLL
404+
LLLLLLLLLLLLLLLL
405+
LLLLLLLLLLLLLLLL
406+
LLLLLLLLLLLLLLLL
407+
LLLLLLLLLLLLLLLL
408+
LLLLLLLLLLLLLLLL
409+
LLLLLLLLLLLLLLLL
410+
LLLLLLLLLLLLLLLL
411+
LLLLLLLLLLLLLLLL
412+
LLLLLLLLLLLLLLLL
413+
LLLLLLLLLLLLLLLL
414+
LLLLLLLLLLLLLLLL
415+
LLLLLLLLLLLLLLLL
416+
LLLLLLLLLLLLLLLL`]
400417
)
401418

402419
setSolids([head_up, head_down, head_left, head_right, single_up, single_down, single_left, single_right, wall])
@@ -436,7 +453,30 @@ const startMelody = tune`
436453
const GAME_STATE = {
437454
START_SCREEN: 0,
438455
PLAYING: 1,
439-
GAME_OVER: 2
456+
GAME_OVER: 2,
457+
SETTINGS: 3
458+
}
459+
460+
// Settings system
461+
const THEMES = {
462+
LIGHT: 0,
463+
DARK: 1
464+
}
465+
466+
const SPEEDS = {
467+
LOW: 0,
468+
MEDIUM: 1,
469+
HIGH: 2
470+
}
471+
472+
let gameSettings = {
473+
theme: THEMES.LIGHT,
474+
speed: SPEEDS.MEDIUM
475+
}
476+
477+
let settingsMenu = {
478+
selectedOption: 0,
479+
options: ['Theme', 'Speed']
440480
}
441481

442482
let gameState = GAME_STATE.START_SCREEN
@@ -468,7 +508,19 @@ const startScreenMap = map`
468508
..........
469509
..........`
470510

471-
let snake = [{ x: 4, y: 5 }]
511+
const settingsScreenMap = map`
512+
..........
513+
..........
514+
..........
515+
..........
516+
..........
517+
..........
518+
..........
519+
..........
520+
..........
521+
..........`
522+
523+
let snake = [{ x: 4, y: 5 }, { x: 3, y: 5 }, { x: 2, y: 5 }]
472524
let direction = { x: 1, y: 0 }
473525
let prevDirection = { x: 1, y: 0 }
474526
let food_pos = { x: 6, y: 3 }
@@ -483,11 +535,101 @@ let gameInterval = null
483535

484536
setMap(startScreenMap)
485537

538+
function getSpeedMultiplier() {
539+
switch(gameSettings.speed) {
540+
case SPEEDS.LOW: return 0.7
541+
case SPEEDS.HIGH: return 1.4
542+
default: return 1.0
543+
}
544+
}
545+
546+
function getThemeName() {
547+
return gameSettings.theme === THEMES.DARK ? "Dark" : "Light"
548+
}
549+
550+
function getSpeedName() {
551+
switch(gameSettings.speed) {
552+
case SPEEDS.LOW: return "Low"
553+
case SPEEDS.HIGH: return "High"
554+
default: return "Medium"
555+
}
556+
}
557+
558+
function applyTheme() {
559+
if (gameSettings.theme === THEMES.DARK) {
560+
// Fill background with dark tiles
561+
for (let x = 0; x < 10; x++) {
562+
for (let y = 0; y < 10; y++) {
563+
const sprites = getTile(x, y)
564+
let hasDarkBg = false
565+
sprites.forEach(sprite => {
566+
if (sprite.type === dark_bg) {
567+
hasDarkBg = true
568+
}
569+
})
570+
if (!hasDarkBg && !sprites.some(s => s.type === wall)) {
571+
addSprite(x, y, dark_bg)
572+
}
573+
}
574+
}
575+
} else {
576+
// Remove dark background tiles
577+
for (let x = 0; x < 10; x++) {
578+
for (let y = 0; y < 10; y++) {
579+
const sprites = getTile(x, y)
580+
sprites.forEach(sprite => {
581+
if (sprite.type === dark_bg) {
582+
sprite.remove()
583+
}
584+
})
585+
}
586+
}
587+
}
588+
}
589+
590+
function clearTileAndReapplyTheme(x, y) {
591+
clearTile(x, y)
592+
if (gameSettings.theme === THEMES.DARK) {
593+
const sprites = getTile(x, y)
594+
const hasWall = sprites.some(s => s.type === wall)
595+
if (!hasWall) {
596+
addSprite(x, y, dark_bg)
597+
}
598+
}
599+
}
600+
601+
function showSettingsScreen() {
602+
gameState = GAME_STATE.SETTINGS
603+
setMap(settingsScreenMap)
604+
applyTheme()
605+
606+
clearText()
607+
addText("SETTINGS", { x: 2, y: 2, color: color`7` })
608+
609+
// Show theme option
610+
const themeColor = settingsMenu.selectedOption === 0 ? color`4` : color`0`
611+
addText(`> Theme: ${getThemeName()}`, { x: 1, y: 5, color: themeColor })
612+
613+
// Show speed option
614+
const speedColor = settingsMenu.selectedOption === 1 ? color`4` : color`0`
615+
addText(`> Speed: ${getSpeedName()}`, { x: 1, y: 7, color: speedColor })
616+
617+
addText("W/S: Navigate", { x: 2, y: 10, color: color`9` })
618+
addText("A/D: Change", { x: 2, y: 11, color: color`9` })
619+
addText("I: Back", { x: 2, y: 12, color: color`9` })
620+
}
621+
486622
function showStartScreen() {
623+
gameState = GAME_STATE.START_SCREEN
624+
setMap(startScreenMap)
625+
applyTheme()
626+
487627
clearText()
488628
addText("Simply just", { x: 4, y: 2, color: color`0` })
489629
addText("SNAKE", { x: 4, y: 4, color: color`7` })
490-
addText("Press I!", { y: 14, color: color`4` })
630+
addText("Press I!", { y: 12, color: color`4` })
631+
addText("Press K for", { y: 13, color: color`6` })
632+
addText("settings!", { y: 14, color: color`6` })
491633

492634
addSprite(2, 5, head_right)
493635
addSprite(1, 5, body_horizontal)
@@ -496,7 +638,8 @@ function showStartScreen() {
496638
}
497639

498640
function calculateSpeed(score) {
499-
const newSpeed = BASE_SPEED - (score * SPEED_DECREASE_PER_POINT)
641+
const baseSpeed = BASE_SPEED * getSpeedMultiplier()
642+
const newSpeed = baseSpeed - (score * SPEED_DECREASE_PER_POINT)
500643
return Math.max(newSpeed, MIN_SPEED)
501644
}
502645

@@ -629,7 +772,7 @@ function moveSnake() {
629772
// Check food collision
630773
if (newHead.x === food_pos.x && newHead.y === food_pos.y) {
631774
score++
632-
clearTile(food_pos.x, food_pos.y)
775+
clearTileAndReapplyTheme(food_pos.x, food_pos.y)
633776
food_pos = generateFood()
634777
addSprite(food_pos.x, food_pos.y, food)
635778
clearText()
@@ -654,29 +797,31 @@ function gameOver() {
654797
gameInterval = null
655798
}
656799

800+
playTune(gameOverMelody)
801+
657802
clearText()
658803
addText("Game Over!", { y: 6, color: color`3` })
659804
addText(`Your Score: ${score}`, { y: 8, color: color`6` })
660805
addText("I to restart!", { y: 10, color: color`7` })
661-
662-
playTune(gameOverMelody)
806+
addText("K for menu!", { y: 11, color: color`5` })
663807
}
664808

665809
function startGame() {
666810
gameState = GAME_STATE.PLAYING
667-
snake = [{ x: 4, y: 5 }]
811+
snake = [{ x: 4, y: 5 }, { x: 3, y: 5 }, { x: 2, y: 5 }]
668812
direction = { x: 1, y: 0 }
669813
prevDirection = { x: 1, y: 0 }
670814
score = 0
671815
gameRunning = true
672816

673-
currentSpeed = BASE_SPEED
817+
currentSpeed = BASE_SPEED * getSpeedMultiplier()
674818

675819
if (gameInterval) {
676820
clearInterval(gameInterval)
677821
}
678822

679823
setMap(levels[level])
824+
applyTheme()
680825
food_pos = generateFood()
681826
addSprite(food_pos.x, food_pos.y, food)
682827
drawSnake()
@@ -703,12 +848,41 @@ function checkDirectionChange(newDirection) {
703848
}
704849
}
705850

851+
// Settings navigation
852+
function navigateSettings(direction) {
853+
if (direction === 'up') {
854+
settingsMenu.selectedOption = Math.max(0, settingsMenu.selectedOption - 1)
855+
} else if (direction === 'down') {
856+
settingsMenu.selectedOption = Math.min(settingsMenu.options.length - 1, settingsMenu.selectedOption + 1)
857+
}
858+
showSettingsScreen()
859+
}
860+
861+
function changeSettingValue(direction) {
862+
if (settingsMenu.selectedOption === 0) { // Theme
863+
if (direction === 'left') {
864+
gameSettings.theme = THEMES.LIGHT
865+
} else if (direction === 'right') {
866+
gameSettings.theme = THEMES.DARK
867+
}
868+
} else if (settingsMenu.selectedOption === 1) { // Speed
869+
if (direction === 'left') {
870+
gameSettings.speed = Math.max(0, gameSettings.speed - 1)
871+
} else if (direction === 'right') {
872+
gameSettings.speed = Math.min(2, gameSettings.speed + 1)
873+
}
874+
}
875+
showSettingsScreen()
876+
}
877+
706878
// Controls
707879
onInput("w", () => {
708880
if (gameState === GAME_STATE.PLAYING && gameRunning && direction.y !== 1) {
709881
const newDirection = { x: 0, y: -1 }
710882
checkDirectionChange(newDirection)
711883
direction = newDirection
884+
} else if (gameState === GAME_STATE.SETTINGS) {
885+
navigateSettings('up')
712886
}
713887
})
714888

@@ -717,6 +891,8 @@ onInput("s", () => {
717891
const newDirection = { x: 0, y: 1 }
718892
checkDirectionChange(newDirection)
719893
direction = newDirection
894+
} else if (gameState === GAME_STATE.SETTINGS) {
895+
navigateSettings('down')
720896
}
721897
})
722898

@@ -725,6 +901,8 @@ onInput("a", () => {
725901
const newDirection = { x: -1, y: 0 }
726902
checkDirectionChange(newDirection)
727903
direction = newDirection
904+
} else if (gameState === GAME_STATE.SETTINGS) {
905+
changeSettingValue('left')
728906
}
729907
})
730908

@@ -733,6 +911,8 @@ onInput("d", () => {
733911
const newDirection = { x: 1, y: 0 }
734912
checkDirectionChange(newDirection)
735913
direction = newDirection
914+
} else if (gameState === GAME_STATE.SETTINGS) {
915+
changeSettingValue('right')
736916
}
737917
})
738918

@@ -741,6 +921,16 @@ onInput("i", () => {
741921
startGame()
742922
} else if (gameState === GAME_STATE.GAME_OVER) {
743923
restartGame()
924+
} else if (gameState === GAME_STATE.SETTINGS) {
925+
showStartScreen()
926+
}
927+
})
928+
929+
onInput("k", () => {
930+
if (gameState === GAME_STATE.START_SCREEN) {
931+
showSettingsScreen()
932+
} else if (gameState === GAME_STATE.GAME_OVER) {
933+
showStartScreen()
744934
}
745935
})
746936

0 commit comments

Comments
 (0)