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,25 @@
|
||||
[gd_scene format=3 uid="uid://da7lj6k47rotp"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://fqff3d88cr1j" path="res://games/frogger/frogger.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://df5twn4utxg0n" path="res://games/frogger/frogger_view.gd" id="2"]
|
||||
[ext_resource type="Script" uid="uid://blkjvi1sb7hmb" path="res://games/frogger/display.gd" id="3"]
|
||||
[ext_resource type="Script" uid="uid://dh0mu01mar7nl" path="res://games/frogger/frogger_audio.gd" id="4"]
|
||||
|
||||
[node name="Frogger" type="Node" unique_id=1686543748]
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="PixelScreen" type="SubViewport" parent="." unique_id=865659315]
|
||||
disable_3d = true
|
||||
size = Vector2i(224, 256)
|
||||
render_target_update_mode = 4
|
||||
|
||||
[node name="Board" type="Node2D" parent="PixelScreen" unique_id=184374191]
|
||||
texture_filter = 1
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="Display" type="Node2D" parent="." unique_id=333917260]
|
||||
texture_filter = 1
|
||||
script = ExtResource("3")
|
||||
|
||||
[node name="Audio" type="Node" parent="." unique_id=712591344]
|
||||
script = ExtResource("4")
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b242bxbbuaj5o"
|
||||
path="res://.godot/imported/spr_frogger.png-7ce0f59b8e564b24229faf1647204a8e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/art/spr_frogger.png"
|
||||
dest_files=["res://.godot/imported/spr_frogger.png-7ce0f59b8e564b24229faf1647204a8e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="mp3"
|
||||
type="AudioStreamMP3"
|
||||
uid="uid://sp8rad7erusb"
|
||||
path="res://.godot/imported/frogger-music.mp3-83a0af3950edf8e2ce0550d65f173319.mp3str"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/audio/frogger-music.mp3"
|
||||
dest_files=["res://.godot/imported/frogger-music.mp3-83a0af3950edf8e2ce0550d65f173319.mp3str"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://smtgenseeuju"
|
||||
path="res://.godot/imported/sound-frogger-coin-in.wav-8633ca7444f8a7846bf6405524ad9b37.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/audio/sound-frogger-coin-in.wav"
|
||||
dest_files=["res://.godot/imported/sound-frogger-coin-in.wav-8633ca7444f8a7846bf6405524ad9b37.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://jdgf84jxnjus"
|
||||
path="res://.godot/imported/sound-frogger-extra.wav-158c017030f00ec8317ac46cfefa90a8.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/audio/sound-frogger-extra.wav"
|
||||
dest_files=["res://.godot/imported/sound-frogger-extra.wav-158c017030f00ec8317ac46cfefa90a8.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://dbeeno1vhd72o"
|
||||
path="res://.godot/imported/sound-frogger-hop.wav-cf3b2064d4fb4c61b75fee957fe06e77.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/audio/sound-frogger-hop.wav"
|
||||
dest_files=["res://.godot/imported/sound-frogger-hop.wav-cf3b2064d4fb4c61b75fee957fe06e77.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://c40g5wcvnh1a1"
|
||||
path="res://.godot/imported/sound-frogger-plunk.wav-73a8475dfe1089b682fa96175c511864.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/audio/sound-frogger-plunk.wav"
|
||||
dest_files=["res://.godot/imported/sound-frogger-plunk.wav-73a8475dfe1089b682fa96175c511864.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://cgy6c1rwyev5e"
|
||||
path="res://.godot/imported/sound-frogger-squash.wav-1b686c87ff414930a009a0fcd27650d0.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/audio/sound-frogger-squash.wav"
|
||||
dest_files=["res://.godot/imported/sound-frogger-squash.wav-1b686c87ff414930a009a0fcd27650d0.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://clqop5ekc2wkt"
|
||||
path="res://.godot/imported/sound-frogger-time.wav-18d22f8e1a9342d25829a0bf05ddae73.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://games/frogger/audio/sound-frogger-time.wav"
|
||||
dest_files=["res://.godot/imported/sound-frogger-time.wav-18d22f8e1a9342d25829a0bf05ddae73.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
@@ -0,0 +1,19 @@
|
||||
@tool
|
||||
extends Node2D
|
||||
const LOGICAL := Vector2(224,256)
|
||||
var destination := Rect2(0,0,672,768)
|
||||
func _process(_delta: float) -> void:
|
||||
var available := get_viewport_rect().size if not Engine.is_editor_hint() else Vector2(672,768)
|
||||
var factor := minf(available.x/7.0,available.y/8.0)
|
||||
var size := Vector2(7,8)*factor
|
||||
destination = Rect2((available-size)*.5,size)
|
||||
queue_redraw()
|
||||
func to_logical(point: Vector2) -> Vector2:
|
||||
return (point-destination.position)/destination.size*LOGICAL
|
||||
func to_logical_delta(delta: Vector2) -> Vector2:
|
||||
return delta/destination.size*LOGICAL
|
||||
func _draw() -> void:
|
||||
draw_rect(Rect2(Vector2.ZERO,get_viewport_rect().size),Color.BLACK)
|
||||
if has_node("../PixelScreen"):
|
||||
draw_texture_rect(get_node("../PixelScreen").get_texture(),destination,false)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://blkjvi1sb7hmb
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
uid://fqff3d88cr1j
|
||||
@@ -0,0 +1,35 @@
|
||||
extends Node
|
||||
var voices: Array[AudioStreamPlayer] = []
|
||||
var tracks: Dictionary = {}
|
||||
var tune: AudioStreamPlayer
|
||||
var current := 0
|
||||
func _ready() -> void:
|
||||
for name in ["hop","plunk","squash","time","extra","coin-in"]:
|
||||
tracks[name] = load("res://games/frogger/audio/sound-frogger-"+name+".wav")
|
||||
for i in range(6):
|
||||
var voice := AudioStreamPlayer.new()
|
||||
voice.volume_db = -8
|
||||
add_child(voice)
|
||||
voices.append(voice)
|
||||
tune = AudioStreamPlayer.new()
|
||||
tune.stream = load("res://games/frogger/audio/frogger-music.mp3")
|
||||
tune.volume_db = -12
|
||||
add_child(tune)
|
||||
tune.finished.connect(_music_finished)
|
||||
func effect(name: String) -> void:
|
||||
if get_parent().demo_active: return
|
||||
if not tracks.has(name): return
|
||||
var voice := voices[current]
|
||||
current = (current+1)%voices.size()
|
||||
voice.stream = tracks[name]
|
||||
voice.play()
|
||||
func music() -> void:
|
||||
if get_parent().demo_active: return
|
||||
if not tune.playing: tune.play()
|
||||
func _music_finished() -> void:
|
||||
if get_parent().phase in ["play","home"] and get_parent().music_enabled: tune.play()
|
||||
func stop_music() -> void:
|
||||
if tune: tune.stop()
|
||||
func stop_all() -> void:
|
||||
stop_music()
|
||||
for voice in voices: voice.stop()
|
||||
@@ -0,0 +1 @@
|
||||
uid://dh0mu01mar7nl
|
||||
@@ -0,0 +1,138 @@
|
||||
@tool
|
||||
extends Node2D
|
||||
const SHEET = preload("res://games/frogger/art/spr_frogger.png")
|
||||
const CHARACTERS := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-"
|
||||
var game: Node
|
||||
var atlas: ImageTexture
|
||||
var regions: Dictionary = {}
|
||||
var source: Image
|
||||
func _ready() -> void:
|
||||
game = get_parent().get_parent()
|
||||
source = SHEET.get_image()
|
||||
source.convert(Image.FORMAT_RGBA8)
|
||||
for i in range(8): register("frog%d"%i,Rect2i(1+i*18,1,16,16))
|
||||
for i in range(2): register("attract%d"%i,Rect2i(1+i*18,98,16,16))
|
||||
for i in range(7): register("death%d"%i,Rect2i(1+i*18,80,16,16))
|
||||
for i in range(5): register("car%d"%i,Rect2i(1+i*18,116,32 if i==4 else 16,16))
|
||||
for i in range(3): register("log%d"%i,Rect2i(1+i*18,134,16,16))
|
||||
for i in range(5): register("turtle%d"%i,Rect2i(1+i*18,152,16,16))
|
||||
for i in range(5): register("letter%d"%i,Rect2i(1+i*18,232,16,16))
|
||||
register("home",Rect2i(1,188,32,24))
|
||||
register("hedge",Rect2i(35,188,8,24))
|
||||
register("saved",Rect2i(45,196,16,16))
|
||||
register("bank",Rect2i(135,196,16,16))
|
||||
for color in range(5):
|
||||
for i in range(CHARACTERS.length()):
|
||||
register("font%d_%s"%[color,CHARACTERS[i]],Rect2i(1+(i%17)*9,250+color*28+int(i/17)*9,8,8))
|
||||
atlas = ImageTexture.create_from_image(source)
|
||||
source = null
|
||||
|
||||
func register(name: String,area: Rect2i) -> void:
|
||||
regions[name] = Rect2(area)
|
||||
# Flood only border-connected black/gray padding; preserve enclosed dark detail.
|
||||
var pending: Array[Vector2i] = []
|
||||
for x in range(area.size.x):
|
||||
pending.append(Vector2i(x,0))
|
||||
pending.append(Vector2i(x,area.size.y-1))
|
||||
for y in range(area.size.y):
|
||||
pending.append(Vector2i(0,y))
|
||||
pending.append(Vector2i(area.size.x-1,y))
|
||||
var seen: Dictionary = {}
|
||||
while not pending.is_empty():
|
||||
var p: Vector2i = pending.pop_back()
|
||||
if p.x<0 or p.y<0 or p.x>=area.size.x or p.y>=area.size.y or seen.has(p): continue
|
||||
seen[p] = true
|
||||
var pixel := source.get_pixelv(area.position+p)
|
||||
var key := pixel.is_equal_approx(Color.BLACK) or pixel.is_equal_approx(Color("404040")) or pixel.a == 0
|
||||
if not key: continue
|
||||
source.set_pixelv(area.position+p,Color.TRANSPARENT)
|
||||
for offset in [Vector2i.LEFT,Vector2i.RIGHT,Vector2i.UP,Vector2i.DOWN]: pending.append(p+offset)
|
||||
|
||||
func sprite(name: String,point: Vector2) -> void:
|
||||
var region: Rect2 = regions[name]
|
||||
draw_texture_rect_region(atlas,Rect2(point.round(),region.size),region)
|
||||
func text_at(value: String,point: Vector2,color: int = 0) -> void:
|
||||
for i in range(value.length()):
|
||||
var key := "font%d_%s"%[color,value[i]]
|
||||
if regions.has(key): sprite(key,point+Vector2(i*8,0))
|
||||
func label(value: String,y: float,color: int = 0) -> void:
|
||||
text_at(value,Vector2((224-value.length()*8)/2.0,y),color)
|
||||
func title() -> void:
|
||||
var t: float = game.attract_time
|
||||
for i in range(7):
|
||||
var age: float = t - .5 - i*4
|
||||
if age < 0: continue
|
||||
var x: float = 28+i*24
|
||||
var y := 136.0
|
||||
if age < 3.5:
|
||||
x = lerpf(232,x,floorf(age*12)/42.0)
|
||||
sprite("attract1",Vector2(x,y))
|
||||
elif t < 29:
|
||||
sprite("attract0",Vector2(x,y))
|
||||
elif t < 33:
|
||||
y = lerpf(136,55,floorf((t-29)*12)/48.0)
|
||||
sprite("frog%d"%(int(t*6)%2),Vector2(x,y))
|
||||
elif t < 33+i*.45:
|
||||
sprite("attract0",Vector2(x,55))
|
||||
else:
|
||||
sprite("letter%d"%[0,1,2,3,3,4,1][i],Vector2(x,55))
|
||||
if t >= 36: label("-POINT TABLE-",89)
|
||||
if t >= 37: label("10 PTS FOR EACH STEP",115,1)
|
||||
if t >= 38:
|
||||
label("50 PTS FOR EVERY FROG",139,1)
|
||||
label("ARRIVED HOME SAFELY",151,2)
|
||||
if t >= 39:
|
||||
label("1000 PTS FOR FIVE",177,1)
|
||||
label("FROGS HOME",189,1)
|
||||
if int(t*2)%2 == 0: label("PUSH START",219,4)
|
||||
label("FREE PLAY",247,4)
|
||||
func board() -> void:
|
||||
for x in range(0,224,8): sprite("hedge",Vector2(x,24))
|
||||
for i in range(5):
|
||||
draw_rect(Rect2(game.HOME_X[i]-16,24,32,24),Color("000047"))
|
||||
sprite("home",Vector2(game.HOME_X[i]-16,24))
|
||||
if game.homes[i]: sprite("saved",Vector2(game.HOME_X[i]-8,32))
|
||||
for y in [128,224]:
|
||||
for x in range(0,224,16): sprite("bank",Vector2(x,y))
|
||||
for lane in game.lanes:
|
||||
var y: float = 232-lane.row*16-8
|
||||
for x in game.lane_objects(lane):
|
||||
match lane.kind:
|
||||
"car": sprite("car%d"%lane.sprite,Vector2(x,y))
|
||||
"log":
|
||||
var tiles := int(lane.width/16)
|
||||
for i in range(tiles): sprite("log%d"%(0 if i==0 else (2 if i==tiles-1 else 1)),Vector2(x+i*16,y))
|
||||
"turtle":
|
||||
if game.submerged(lane): continue
|
||||
var frame := int(game.clock*5)%3
|
||||
if lane.sprite == 1 and fposmod(game.clock,7.0)>5:
|
||||
frame = 3 if fposmod(game.clock,7.0)<5.4 else 4
|
||||
for i in range(int(lane.width/16)): sprite("turtle%d"%frame,Vector2(x+i*16,y))
|
||||
if game.phase == "dead":
|
||||
var frame := clampi(int((1.1-game.phase_time)*7),0,6)
|
||||
sprite("death%d"%frame,game.frog-Vector2(8,8))
|
||||
elif game.phase not in ["home","clear","won","lost"]:
|
||||
sprite("frog%d"%(game.direction*2+(1 if game.hop_left>0 else 0)),game.frog-Vector2(8,8))
|
||||
for i in range(mini(game.lives,6)): sprite("frog0",Vector2(i*12,239))
|
||||
var fraction: float = clampf(game.time_left/game.crossing_time,0,1)
|
||||
draw_rect(Rect2(88,248,104*fraction,8),Color("e00000") if fraction<.167 else Color("00c000"))
|
||||
text_at("TIME",Vector2(192,248),1)
|
||||
if game.phase == "ready": label("PLAYER 1",135,2)
|
||||
if game.phase == "clear": label("FIVE FROGS HOME",135,1)
|
||||
if game.phase == "won": label("YOU WIN",135,1)
|
||||
if game.phase == "lost": label("GAME OVER",135,2)
|
||||
if game.demo_active and int(game.demo_time*2)%2 == 0: label("GAME OVER",135,2)
|
||||
func _draw() -> void:
|
||||
if game == null or atlas == null: return
|
||||
draw_rect(Rect2(0,0,224,256),Color.BLACK)
|
||||
draw_rect(Rect2(0,0,224,128),Color("000047"))
|
||||
text_at("1-UP",Vector2(24,0))
|
||||
text_at("HI-SCORE",Vector2(104,0))
|
||||
text_at("%05d"%game.score,Vector2(16,8),2)
|
||||
text_at("%05d"%game.high_score,Vector2(112,8),2)
|
||||
if game.phase == "title": title()
|
||||
elif game.phase == "scores":
|
||||
label("HIGH SCORES",55,1)
|
||||
for i in range(5): label("%d %05d"%[i+1,game.leaderboard[i]] if i<game.leaderboard.size() else "%d -----"%(i+1),88+i*20)
|
||||
label("PUSH START",218,4)
|
||||
else: board()
|
||||
@@ -0,0 +1 @@
|
||||
uid://df5twn4utxg0n
|
||||
Reference in new issue
Block a user