Added Glitch Room Source code to Gitty
This commit is contained in:
commit
1ac9eb5644
505 files changed
+28504
No files matched your search
@@ -0,0 +1,329 @@
|
||||
@tool
|
||||
extends ArcadeGame
|
||||
@export_range(0,99) var boards_to_win: int = 1
|
||||
@export_range(1,9) var starting_lives: int = 3
|
||||
@export_range(15,120) var crossing_time: float = 60.0
|
||||
@export_range(.5,2) var difficulty: float = 1.0
|
||||
@export var music_enabled: bool = true
|
||||
const HOME_X := [16,64,112,160,208]
|
||||
var phase := "title"
|
||||
var phase_time := 0.0
|
||||
var clock := 0.0
|
||||
var score := 0
|
||||
var high_score := 0
|
||||
var lives := 3
|
||||
var board_number := 1
|
||||
var homes: Array[bool] = [false,false,false,false,false]
|
||||
var frog := Vector2(112,232)
|
||||
var from := Vector2(112,232)
|
||||
var destination := Vector2(112,232)
|
||||
var direction := 0
|
||||
var hop_left := 0.0
|
||||
var repeat_left := 0.0
|
||||
var time_left := 60.0
|
||||
var furthest_row := 0
|
||||
var warned := false
|
||||
var extra_awarded := false
|
||||
var death_kind := "squash"
|
||||
var lanes: Array[Dictionary] = []
|
||||
var leaderboard: Array[int] = []
|
||||
var attract_time := 0.0
|
||||
var demo_active := false
|
||||
var demo_time := 0.0
|
||||
var idle_score := 0
|
||||
@onready var view: Node2D = $PixelScreen/Board
|
||||
|
||||
func _ready() -> void:
|
||||
make_lanes()
|
||||
lives = starting_lives
|
||||
|
||||
func make_lanes() -> void:
|
||||
lanes.clear()
|
||||
var definitions := [
|
||||
[1,"car",-24.0,16.0,80.0,20.0,1], [2,"car",28.0,16.0,82.0,48.0,3],
|
||||
[3,"car",-32.0,16.0,78.0,8.0,0], [4,"car",45.0,16.0,110.0,70.0,2],
|
||||
[5,"car",-22.0,32.0,100.0,30.0,4], [7,"turtle",-24.0,48.0,72.0,4.0,0],
|
||||
[8,"log",25.0,48.0,98.0,30.0,0], [9,"log",36.0,96.0,144.0,5.0,0],
|
||||
[10,"turtle",-30.0,32.0,70.0,14.0,1], [11,"log",22.0,64.0,100.0,40.0,0]]
|
||||
for item in definitions:
|
||||
lanes.append({"row":item[0],"kind":item[1],"speed":item[2]*(1+(board_number-1)*.10)*difficulty,"width":item[3],"spacing":item[4],"offset":item[5],"sprite":item[6]})
|
||||
|
||||
func start_game() -> bool:
|
||||
if not super.start_game(): return false
|
||||
demo_active = false
|
||||
attract_time = 0
|
||||
$Audio.stop_all()
|
||||
score = 0
|
||||
lives = starting_lives
|
||||
board_number = 1
|
||||
extra_awarded = false
|
||||
homes.fill(false)
|
||||
make_lanes()
|
||||
respawn()
|
||||
phase = "ready"
|
||||
phase_time = 1.5
|
||||
$Audio.effect("coin-in")
|
||||
return true
|
||||
|
||||
func reset_game() -> void:
|
||||
super.reset_game()
|
||||
demo_active = false
|
||||
attract_time = 0
|
||||
$Audio.stop_all()
|
||||
phase = "title"
|
||||
clock = 0
|
||||
score = 0
|
||||
board_number = 1
|
||||
lives = starting_lives
|
||||
homes.fill(false)
|
||||
make_lanes()
|
||||
respawn()
|
||||
|
||||
func respawn() -> void:
|
||||
frog = Vector2(112,232)
|
||||
from = frog
|
||||
destination = frog
|
||||
direction = 0
|
||||
hop_left = 0
|
||||
repeat_left = 0
|
||||
time_left = crossing_time
|
||||
furthest_row = 0
|
||||
warned = false
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if Engine.is_editor_hint() or (event is InputEventKey and event.echo): return
|
||||
if event.is_action_pressed("arcade_start"):
|
||||
if not is_running: start_game()
|
||||
|
||||
func pressed(action: String) -> bool:
|
||||
return Input.is_action_pressed(action)
|
||||
|
||||
func hop(step: Vector2i) -> bool:
|
||||
if phase != "play" or hop_left > 0 or absi(step.x)+absi(step.y) != 1: return false
|
||||
var target := frog+Vector2(step)*16
|
||||
if target.x < 8 or target.x > 216 or target.y > 232 or target.y < 40: return false
|
||||
direction = 0 if step.y < 0 else (2 if step.y > 0 else (1 if step.x < 0 else 3))
|
||||
from = frog
|
||||
destination = target
|
||||
hop_left = .12
|
||||
$Audio.effect("hop")
|
||||
return true
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
clock += delta
|
||||
if Engine.is_editor_hint():
|
||||
attract_time = fmod(clock,44.0)
|
||||
view.queue_redraw()
|
||||
return
|
||||
if phase in ["title","scores"]:
|
||||
attract_time += delta
|
||||
if phase == "title" and attract_time >= 44.0:
|
||||
begin_demo()
|
||||
view.queue_redraw()
|
||||
return
|
||||
elif phase == "scores" and attract_time >= 7.0:
|
||||
phase = "title"
|
||||
attract_time = 0
|
||||
elif phase in ["ready","dead","home","clear"]:
|
||||
phase_time -= delta
|
||||
if phase_time <= 0:
|
||||
match phase:
|
||||
"ready":
|
||||
phase = "play"
|
||||
if music_enabled and not demo_active: $Audio.music()
|
||||
"dead":
|
||||
if lives <= 0 and not demo_active:
|
||||
finish(false)
|
||||
else:
|
||||
respawn()
|
||||
phase = "play"
|
||||
if music_enabled and not demo_active: $Audio.music()
|
||||
"home":
|
||||
if not homes.has(false):
|
||||
add_score(1000)
|
||||
phase = "clear"
|
||||
phase_time = 2.0
|
||||
else:
|
||||
respawn()
|
||||
phase = "play"
|
||||
"clear":
|
||||
if demo_active:
|
||||
end_demo()
|
||||
elif boards_to_win > 0 and board_number >= boards_to_win:
|
||||
finish(true)
|
||||
else:
|
||||
board_number += 1
|
||||
homes.fill(false)
|
||||
make_lanes()
|
||||
respawn()
|
||||
phase = "ready"
|
||||
phase_time = 1.5
|
||||
elif phase == "play":
|
||||
repeat_left -= delta
|
||||
if hop_left <= 0 and repeat_left <= 0:
|
||||
var step := Vector2i.ZERO
|
||||
if demo_active: step = demo_step()
|
||||
elif pressed("arcade_up"): step = Vector2i.UP
|
||||
elif pressed("arcade_down"): step = Vector2i.DOWN
|
||||
elif pressed("arcade_left"): step = Vector2i.LEFT
|
||||
elif pressed("arcade_right"): step = Vector2i.RIGHT
|
||||
if step != Vector2i.ZERO and hop(step): repeat_left = .16
|
||||
step_world(delta)
|
||||
if demo_active:
|
||||
demo_time += delta
|
||||
if demo_time >= 32.0: end_demo()
|
||||
view.queue_redraw()
|
||||
|
||||
func begin_demo() -> void:
|
||||
idle_score = score
|
||||
demo_active = true
|
||||
demo_time = 0
|
||||
score = 0
|
||||
lives = starting_lives
|
||||
board_number = 1
|
||||
homes.fill(false)
|
||||
clock = 0
|
||||
make_lanes()
|
||||
respawn()
|
||||
phase = "play"
|
||||
$Audio.stop_all()
|
||||
|
||||
func end_demo() -> void:
|
||||
demo_active = false
|
||||
score = idle_score
|
||||
phase = "scores"
|
||||
attract_time = 0
|
||||
$Audio.stop_all()
|
||||
|
||||
# Predict the same lane positions and hitboxes used by live gameplay.
|
||||
# The demo supplies ordinary hops; it has no invulnerability or scripted teleports.
|
||||
func demo_safe(step: Vector2i) -> bool:
|
||||
var target := frog + Vector2(step)*16
|
||||
if target.x < 8 or target.x > 216 or target.y < 40 or target.y > 232: return false
|
||||
if target.y == 40:
|
||||
for i in range(5):
|
||||
if absf(target.x-HOME_X[i]) <= 7 and not homes[i]: return true
|
||||
return false
|
||||
for tick in range(1,13):
|
||||
var t := tick * .01
|
||||
var point := frog.lerp(target,t/.12)
|
||||
var row := roundi((232-point.y)/16)
|
||||
for lane in lanes:
|
||||
if lane.row != row: continue
|
||||
var supported := false
|
||||
var predicted := lane.duplicate()
|
||||
predicted.offset += lane.speed*t
|
||||
for x in lane_objects(predicted):
|
||||
if lane.kind == "car" and point.x+6 > x+2 and point.x-6 < x+lane.width-2: return false
|
||||
if lane.kind != "car" and point.x-5 >= x and point.x+5 <= x+lane.width:
|
||||
supported = true
|
||||
if tick == 12 and lane.kind != "car":
|
||||
if not supported: return false
|
||||
if lane.kind == "turtle" and lane.sprite == 1 and fposmod(clock+t,7.0) > 5.3: return false
|
||||
return true
|
||||
|
||||
func demo_step() -> Vector2i:
|
||||
if demo_safe(Vector2i.UP): return Vector2i.UP
|
||||
var target_x := 112.0
|
||||
var distance := INF
|
||||
for i in range(5):
|
||||
if not homes[i] and absf(frog.x-HOME_X[i]) < distance:
|
||||
distance = absf(frog.x-HOME_X[i])
|
||||
target_x = HOME_X[i]
|
||||
var side := Vector2i.RIGHT if target_x > frog.x else Vector2i.LEFT
|
||||
if absf(target_x-frog.x)>7 and demo_safe(side): return side
|
||||
return Vector2i.ZERO
|
||||
|
||||
func lane_objects(lane: Dictionary) -> Array[float]:
|
||||
var result: Array[float] = []
|
||||
var start: float = fposmod(lane.offset,lane.spacing)-lane.spacing
|
||||
while start < 224:
|
||||
result.append(start)
|
||||
start += lane.spacing
|
||||
return result
|
||||
|
||||
func submerged(lane: Dictionary) -> bool:
|
||||
return lane.kind == "turtle" and lane.sprite == 1 and fposmod(clock,7.0) > 5.7
|
||||
|
||||
func step_world(delta: float) -> void:
|
||||
time_left -= delta
|
||||
if time_left <= 0:
|
||||
die("time")
|
||||
return
|
||||
if time_left <= 10 and not warned:
|
||||
warned = true
|
||||
$Audio.effect("time")
|
||||
for lane in lanes: lane.offset = fposmod(lane.offset+lane.speed*delta,lane.spacing)
|
||||
if hop_left > 0:
|
||||
hop_left = maxf(0,hop_left-delta)
|
||||
frog = from.lerp(destination,1.0-hop_left/.12)
|
||||
if hop_left <= 0:
|
||||
var row := roundi((232-frog.y)/16)
|
||||
if row > furthest_row:
|
||||
add_score((row-furthest_row)*10)
|
||||
furthest_row = row
|
||||
# Evaluate the current rendered location, including the hop trajectory.
|
||||
var row := roundi((232-frog.y)/16)
|
||||
if row >= 12 and hop_left <= 0:
|
||||
var index := -1
|
||||
for i in range(5):
|
||||
if absf(frog.x-HOME_X[i]) <= 7: index = i
|
||||
if index < 0 or homes[index]:
|
||||
die("squash")
|
||||
else:
|
||||
homes[index] = true
|
||||
add_score(50+int(time_left)*10)
|
||||
phase = "home"
|
||||
phase_time = .8
|
||||
$Audio.effect("extra")
|
||||
return
|
||||
for lane in lanes:
|
||||
if lane.row != row: continue
|
||||
var supported := false
|
||||
for x in lane_objects(lane):
|
||||
if lane.kind == "car":
|
||||
if frog.x+5 > x+2 and frog.x-5 < x+lane.width-2:
|
||||
die("squash")
|
||||
return
|
||||
elif frog.x-4 >= x and frog.x+4 <= x+lane.width and not submerged(lane):
|
||||
supported = true
|
||||
if lane.kind != "car":
|
||||
# Support is checked on landing; hops may cross a platform edge in flight.
|
||||
if hop_left <= 0:
|
||||
if not supported:
|
||||
die("plunk")
|
||||
return
|
||||
frog.x += lane.speed*delta
|
||||
if frog.x < 5 or frog.x > 219: die("plunk")
|
||||
break
|
||||
|
||||
func die(kind: String) -> void:
|
||||
if phase != "play": return
|
||||
lives -= 1
|
||||
death_kind = kind
|
||||
phase = "dead"
|
||||
phase_time = 1.1
|
||||
hop_left = 0
|
||||
$Audio.stop_music()
|
||||
$Audio.effect(kind)
|
||||
|
||||
func add_score(value: int) -> void:
|
||||
score += value
|
||||
if not demo_active: high_score = maxi(high_score,score)
|
||||
if score >= 20000 and not extra_awarded:
|
||||
extra_awarded = true
|
||||
lives += 1
|
||||
$Audio.effect("extra")
|
||||
|
||||
func finish(won: bool) -> void:
|
||||
if demo_active:
|
||||
end_demo()
|
||||
return
|
||||
$Audio.stop_all()
|
||||
leaderboard.append(score)
|
||||
leaderboard.sort()
|
||||
leaderboard.reverse()
|
||||
if leaderboard.size() > 5: leaderboard.resize(5)
|
||||
phase = "won" if won else "lost"
|
||||
if won: win_game()
|
||||
else: lose_game()
|
||||
Reference in new issue
Block a user