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,24 @@
|
||||
[gd_scene format=3 uid="uid://crtp0cdh3x55h"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dxkm5iqysxkyy" path="res://games/missile_command/missile_command.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://ed0yaf45xgff" path="res://games/missile_command/pixel_board.gd" id="2"]
|
||||
[ext_resource type="Script" uid="uid://ci3wofmlmkbbg" path="res://games/missile_command/display.gd" id="3"]
|
||||
[ext_resource type="Script" uid="uid://dsft8skyj7r8x" path="res://games/missile_command/arcade_audio.gd" id="4"]
|
||||
|
||||
[node name="MissileCommand" type="Node" unique_id=2072674313]
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="PixelScreen" type="SubViewport" parent="." unique_id=1549979971]
|
||||
disable_3d = true
|
||||
size = Vector2i(256, 231)
|
||||
render_target_update_mode = 4
|
||||
|
||||
[node name="Board" type="Node2D" parent="PixelScreen" unique_id=1583118688]
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="Display" type="Node2D" parent="." unique_id=1661990548]
|
||||
texture_filter = 1
|
||||
script = ExtResource("3")
|
||||
|
||||
[node name="Audio" type="Node" parent="." unique_id=2862985]
|
||||
script = ExtResource("4")
|
||||
@@ -0,0 +1,42 @@
|
||||
extends Node
|
||||
## Small synthesized arcade effects; no external audio assets required.
|
||||
var voices: Array[AudioStreamPlayer] = []
|
||||
var sounds: Dictionary = {}
|
||||
var voice_index := 0
|
||||
func _ready() -> void:
|
||||
for i in range(6):
|
||||
var voice := AudioStreamPlayer.new()
|
||||
voice.volume_db = -17.0
|
||||
add_child(voice)
|
||||
voices.append(voice)
|
||||
sounds["fire"] = tone(1300.0, 220.0, 0.09)
|
||||
sounds["hit"] = tone(170.0, 40.0, 0.18)
|
||||
sounds["zap"] = tone(1700.0, 70.0, 0.55)
|
||||
sounds["death"] = tone(440.0, 35.0, 0.7)
|
||||
sounds["level"] = tone(220.0, 880.0, 0.4)
|
||||
func tone(start: float, finish: float, duration: float) -> AudioStreamWAV:
|
||||
var samples := PackedByteArray()
|
||||
var count := int(22050 * duration)
|
||||
samples.resize(count * 2)
|
||||
var phase := 0.0
|
||||
for i in range(count):
|
||||
var t := float(i) / count
|
||||
phase += lerpf(start, finish, t) * TAU / 22050.0
|
||||
var envelope := minf(t * 50.0, 1.0) * pow(1.0 - t, 1.5)
|
||||
var value := int((sin(phase) + sin(phase * 2.0) * 0.25) * envelope * 18000)
|
||||
samples.encode_s16(i * 2, value)
|
||||
var stream := AudioStreamWAV.new()
|
||||
stream.format = AudioStreamWAV.FORMAT_16_BITS
|
||||
stream.mix_rate = 22050
|
||||
stream.data = samples
|
||||
return stream
|
||||
func play(effect: String) -> void:
|
||||
if not sounds.has(effect) or voices.is_empty():
|
||||
return
|
||||
var voice := voices[voice_index]
|
||||
voice_index = (voice_index + 1) % voices.size()
|
||||
voice.stream = sounds[effect]
|
||||
voice.play()
|
||||
func stop() -> void:
|
||||
for voice in voices:
|
||||
voice.stop()
|
||||
@@ -0,0 +1 @@
|
||||
uid://dsft8skyj7r8x
|
||||
@@ -0,0 +1,18 @@
|
||||
@tool
|
||||
extends Node2D
|
||||
const LOGICAL := Vector2(256,231)
|
||||
var destination := Rect2(0,0,768,576)
|
||||
func _process(_delta: float) -> void:
|
||||
var available := get_viewport_rect().size if not Engine.is_editor_hint() else Vector2(768,576)
|
||||
var factor := minf(available.x/4.0,available.y/3.0)
|
||||
var size := Vector2(4,3)*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://ci3wofmlmkbbg
|
||||
@@ -0,0 +1,283 @@
|
||||
@tool
|
||||
extends ArcadeGame
|
||||
## All positions use the 256 x 231 logical arcade screen.
|
||||
@export_range(0,99) var waves_to_win: int = 3
|
||||
@export_range(.5,2.0) var difficulty: float = 1.0
|
||||
@export_range(1000,50000,1000) var bonus_city_interval: int = 10000
|
||||
@export var relative_mouse_aim: bool = false
|
||||
@export_range(.1,4.0) var trackball_sensitivity: float = 1.0
|
||||
const CITY_X := [43,68,93,163,188,213]
|
||||
const BASE_X := [16,128,240]
|
||||
var phase := "title"
|
||||
var phase_time := 0.0
|
||||
var clock := 0.0
|
||||
var attract_time := 0.0
|
||||
var wave := 1
|
||||
var score := 0
|
||||
var high_score := 0
|
||||
var bonus_bank := 0
|
||||
var next_bonus := 10000
|
||||
var cities: Array[bool] = [true,true,true,true,true,true]
|
||||
var bases: Array[bool] = [true,true,true]
|
||||
var ammo: Array[int] = [10,10,10]
|
||||
var cooldown: Array[float] = [0.0,0.0,0.0]
|
||||
var aim := Vector2(128,100)
|
||||
var incoming: Array[Dictionary] = []
|
||||
var interceptors: Array[Dictionary] = []
|
||||
var blasts: Array[Dictionary] = []
|
||||
var aircraft: Array[Dictionary] = []
|
||||
var leaderboard: Array[int] = []
|
||||
var remaining := 0
|
||||
var spawn_time := 0.0
|
||||
var aircraft_time := 0.0
|
||||
var wave_bonus := 0
|
||||
var recorded := false
|
||||
var rng := RandomNumberGenerator.new()
|
||||
@onready var board: Node2D = $PixelScreen/Board
|
||||
@onready var display: Node2D = $Display
|
||||
|
||||
func _ready() -> void:
|
||||
if not Engine.is_editor_hint(): rng.randomize()
|
||||
|
||||
func start_game() -> bool:
|
||||
if not super.start_game(): return false
|
||||
wave = 1
|
||||
score = 0
|
||||
bonus_bank = 0
|
||||
next_bonus = bonus_city_interval
|
||||
recorded = false
|
||||
cities.fill(true)
|
||||
incoming.clear()
|
||||
interceptors.clear()
|
||||
blasts.clear()
|
||||
aircraft.clear()
|
||||
_begin_wave()
|
||||
return true
|
||||
|
||||
func reset_game() -> void:
|
||||
super.reset_game()
|
||||
$Audio.stop()
|
||||
phase = "title"
|
||||
attract_time = 0
|
||||
phase_time = 0
|
||||
score = 0
|
||||
wave = 1
|
||||
cities.fill(true)
|
||||
bases.fill(true)
|
||||
ammo.fill(10)
|
||||
cooldown.fill(0)
|
||||
incoming.clear()
|
||||
interceptors.clear()
|
||||
blasts.clear()
|
||||
aircraft.clear()
|
||||
bonus_bank = 0
|
||||
|
||||
func _begin_wave() -> void:
|
||||
bases.fill(true)
|
||||
ammo.fill(10)
|
||||
cooldown.fill(0)
|
||||
remaining = 12 + mini(wave,20)*3
|
||||
spawn_time = 1.0
|
||||
aircraft_time = 9.0
|
||||
phase = "ready"
|
||||
phase_time = 2.2
|
||||
wave_bonus = 0
|
||||
|
||||
func add_trackball_motion(motion: Vector2) -> void:
|
||||
set_aim(aim+motion*trackball_sensitivity)
|
||||
|
||||
func set_aim(point: Vector2) -> void:
|
||||
aim = point.clamp(Vector2(1,25),Vector2(254,199))
|
||||
|
||||
func _action(event: InputEvent, action: String) -> bool:
|
||||
return event.is_action_pressed(action)
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if Engine.is_editor_hint(): return
|
||||
if event is InputEventMouseMotion:
|
||||
if relative_mouse_aim:
|
||||
add_trackball_motion(display.to_logical_delta(event.relative))
|
||||
else:
|
||||
set_aim(display.to_logical(event.position))
|
||||
if event is InputEventKey and event.echo: return
|
||||
if _action(event,"arcade_start"):
|
||||
if not is_running: start_game()
|
||||
return
|
||||
if phase != "play": return
|
||||
if event is InputEventMouseButton and not relative_mouse_aim:
|
||||
set_aim(display.to_logical(event.position))
|
||||
for i in range(3):
|
||||
if _action(event,"arcade_action_%d" % (i+1)):
|
||||
fire(i)
|
||||
return
|
||||
|
||||
func fire(base: int) -> bool:
|
||||
if phase != "play" or base < 0 or base > 2 or not bases[base] or ammo[base] <= 0 or cooldown[base] > 0: return false
|
||||
ammo[base] -= 1
|
||||
cooldown[base] = .12
|
||||
var origin := Vector2(BASE_X[base],202)
|
||||
interceptors.append({"origin":origin,"pos":origin,"target":aim,"speed":180.0 if base == 1 else 120.0})
|
||||
$Audio.play("fire")
|
||||
return true
|
||||
|
||||
func multiplier() -> int:
|
||||
return mini(6,1+int((wave-1)/2))
|
||||
|
||||
func add_score(amount: int) -> void:
|
||||
score += amount
|
||||
high_score = maxi(high_score,score)
|
||||
while score >= next_bonus:
|
||||
bonus_bank += 1
|
||||
next_bonus += bonus_city_interval
|
||||
|
||||
func _record_score() -> void:
|
||||
if recorded: return
|
||||
recorded = true
|
||||
leaderboard.append(score)
|
||||
leaderboard.sort()
|
||||
leaderboard.reverse()
|
||||
if leaderboard.size() > 8: leaderboard.resize(8)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
clock += delta
|
||||
if Engine.is_editor_hint():
|
||||
board.queue_redraw()
|
||||
return
|
||||
for i in range(3): cooldown[i] = maxf(0,cooldown[i]-delta)
|
||||
match phase:
|
||||
"title","leaderboard":
|
||||
attract_time += delta
|
||||
if phase == "title" and attract_time >= 9.0:
|
||||
phase = "leaderboard"
|
||||
attract_time = 0.0
|
||||
elif phase == "leaderboard" and attract_time >= 7.0:
|
||||
phase = "title"
|
||||
attract_time = 0.0
|
||||
"ready":
|
||||
phase_time -= delta
|
||||
if phase_time <= 0: phase = "play"
|
||||
"play": _step(delta)
|
||||
"bonus":
|
||||
phase_time -= delta
|
||||
if phase_time <= 0:
|
||||
if waves_to_win > 0 and wave >= waves_to_win:
|
||||
phase = "won"
|
||||
_record_score()
|
||||
win_game()
|
||||
else:
|
||||
wave += 1
|
||||
_begin_wave()
|
||||
"lost":
|
||||
phase_time -= delta
|
||||
if phase_time <= 0:
|
||||
phase = "title"
|
||||
attract_time = 0
|
||||
board.queue_redraw()
|
||||
|
||||
func target_point() -> Vector2:
|
||||
var candidates: Array[Vector2] = []
|
||||
for i in range(6):
|
||||
if cities[i]: candidates.append(Vector2(CITY_X[i],211))
|
||||
for i in range(3):
|
||||
if bases[i]: candidates.append(Vector2(BASE_X[i],204))
|
||||
return candidates[rng.randi_range(0,candidates.size()-1)] if not candidates.is_empty() else Vector2(128,212)
|
||||
|
||||
func spawn_missile(origin: Vector2, kind: String = "missile") -> void:
|
||||
incoming.append({"origin":origin,"pos":origin,"target":target_point(),"kind":kind,"speed":(11.0+mini(wave,25)*1.8)*difficulty,"split":kind == "splitter","alive":true})
|
||||
|
||||
func explosion(point: Vector2, hostile: bool = false, radius: float = 18.0) -> void:
|
||||
blasts.append({"pos":point,"age":0.0,"radius":0.0,"max":radius,"hostile":hostile})
|
||||
if phase == "play": $Audio.play("hit")
|
||||
|
||||
func _restore_cities() -> void:
|
||||
for i in range(6):
|
||||
if not cities[i] and bonus_bank > 0:
|
||||
cities[i] = true
|
||||
bonus_bank -= 1
|
||||
|
||||
func _impact(point: Vector2) -> void:
|
||||
for i in range(6):
|
||||
if absf(point.x-CITY_X[i]) < 9: cities[i] = false
|
||||
for i in range(3):
|
||||
if absf(point.x-BASE_X[i]) < 12:
|
||||
bases[i] = false
|
||||
ammo[i] = 0
|
||||
explosion(point,true,14)
|
||||
|
||||
func _step(delta: float) -> void:
|
||||
spawn_time -= delta
|
||||
if remaining > 0 and spawn_time <= 0:
|
||||
var kind := "missile"
|
||||
if wave >= 3 and remaining%5 == 0: kind = "splitter"
|
||||
if wave >= 5 and remaining%7 == 0: kind = "smart"
|
||||
spawn_missile(Vector2(rng.randf_range(4,252),24),kind)
|
||||
remaining -= 1
|
||||
spawn_time = maxf(.24,1.1-wave*.045)/difficulty
|
||||
if wave >= 3 and remaining > 0:
|
||||
aircraft_time -= delta
|
||||
if aircraft_time <= 0:
|
||||
aircraft.append({"pos":Vector2(-10,55+rng.randf()*35),"drop":1.0,"alive":true})
|
||||
aircraft_time = 15
|
||||
for shot in interceptors:
|
||||
shot.pos = shot.pos.move_toward(shot.target,shot.speed*delta)
|
||||
if shot.pos == shot.target: explosion(shot.target)
|
||||
interceptors = interceptors.filter(func(s: Dictionary) -> bool: return s.pos != s.target)
|
||||
for blast in blasts:
|
||||
blast.age += delta
|
||||
var age: float = blast.age
|
||||
blast.radius = blast.max * (minf(1,age/.45) if age < 1.0 else maxf(0,(1.8-age)/.8))
|
||||
blasts = blasts.filter(func(b: Dictionary) -> bool: return b.age < 1.8)
|
||||
var children: Array[Vector2] = []
|
||||
for missile in incoming:
|
||||
var previous: Vector2 = missile.pos
|
||||
var destination: Vector2 = missile.target
|
||||
if missile.kind == "smart":
|
||||
for blast in blasts:
|
||||
if not blast.hostile and missile.pos.distance_to(blast.pos) < blast.radius+15:
|
||||
destination.x += 60 if missile.pos.x >= blast.pos.x else -60
|
||||
missile.pos = missile.pos.move_toward(destination,missile.speed*delta).clamp(Vector2(0,24),Vector2(255,214))
|
||||
for blast in blasts:
|
||||
if blast.hostile: continue
|
||||
var nearest := Geometry2D.get_closest_point_to_segment(blast.pos,previous,missile.pos)
|
||||
if nearest.distance_to(blast.pos) <= blast.radius+1:
|
||||
missile.alive = false
|
||||
add_score((125 if missile.kind == "smart" else 25)*multiplier())
|
||||
break
|
||||
if not missile.alive:
|
||||
explosion(missile.pos,false,13)
|
||||
elif missile.pos.y >= 202 and missile.pos.distance_to(missile.target) < 12:
|
||||
missile.alive = false
|
||||
_impact(missile.target)
|
||||
elif missile.split and missile.pos.y > 90:
|
||||
missile.split = false
|
||||
children.append(missile.pos)
|
||||
incoming = incoming.filter(func(m: Dictionary) -> bool: return m.alive)
|
||||
for point in children:
|
||||
spawn_missile(point)
|
||||
spawn_missile(point)
|
||||
for plane in aircraft:
|
||||
plane.pos.x += delta*(18+wave)
|
||||
plane.drop -= delta
|
||||
if plane.drop <= 0:
|
||||
spawn_missile(plane.pos)
|
||||
plane.drop = 3
|
||||
for blast in blasts:
|
||||
if not blast.hostile and plane.pos.distance_to(blast.pos) < blast.radius+4:
|
||||
plane.alive = false
|
||||
add_score(100*multiplier())
|
||||
explosion(plane.pos)
|
||||
break
|
||||
aircraft = aircraft.filter(func(p: Dictionary) -> bool: return p.alive and p.pos.x < 268)
|
||||
if not cities.has(true) and bonus_bank == 0:
|
||||
phase = "lost"
|
||||
phase_time = 5
|
||||
_record_score()
|
||||
lose_game()
|
||||
elif remaining == 0 and incoming.is_empty() and interceptors.is_empty() and blasts.is_empty() and aircraft.is_empty():
|
||||
wave_bonus = (cities.count(true)*100+(ammo[0]+ammo[1]+ammo[2])*5)*multiplier()
|
||||
add_score(wave_bonus)
|
||||
_restore_cities()
|
||||
phase = "bonus"
|
||||
phase_time = 3.0
|
||||
$Audio.play("level")
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://dxkm5iqysxkyy
|
||||
@@ -0,0 +1,211 @@
|
||||
@tool
|
||||
extends Node2D
|
||||
const SIZE := Vector2(256,231)
|
||||
const FONT := {
|
||||
"A":[14,17,17,31,17,17,17],"B":[30,17,17,30,17,17,30],"C":[14,17,16,16,16,17,14],"D":[30,17,17,17,17,17,30],"E":[31,16,16,30,16,16,31],"F":[31,16,16,30,16,16,16],"G":[14,17,16,23,17,17,15],"H":[17,17,17,31,17,17,17],"I":[31,4,4,4,4,4,31],"J":[7,2,2,2,18,18,12],"K":[17,18,20,24,20,18,17],"L":[16,16,16,16,16,16,31],"M":[17,27,21,21,17,17,17],"N":[17,25,21,19,17,17,17],"O":[14,17,17,17,17,17,14],"P":[30,17,17,30,16,16,16],"Q":[14,17,17,17,21,18,13],"R":[30,17,17,30,20,18,17],"S":[15,16,16,14,1,1,30],"T":[31,4,4,4,4,4,4],"U":[17,17,17,17,17,17,14],"V":[17,17,17,17,17,10,4],"W":[17,17,17,21,21,21,10],"X":[17,17,10,4,10,17,17],"Y":[17,17,10,4,4,4,4],"Z":[31,1,2,4,8,16,31],
|
||||
"0":[14,17,19,21,25,17,14],"1":[4,12,4,4,4,4,14],"2":[14,17,1,2,4,8,31],"3":[30,1,1,14,1,1,30],"4":[2,6,10,18,31,2,2],"5":[31,16,16,30,1,1,30],"6":[14,16,16,30,17,17,14],"7":[31,1,2,4,8,8,8],"8":[14,17,17,14,17,17,14],"9":[14,17,17,15,1,1,14],"!":[4,4,4,4,4,0,4],"-":[0,0,0,31,0,0,0],".":[0,0,0,0,0,12,12],":":[0,12,12,0,12,12,0]
|
||||
}
|
||||
var game: Node
|
||||
var mesh := ArrayMesh.new()
|
||||
var vertices := PackedVector3Array()
|
||||
var colors := PackedColorArray()
|
||||
var indices := PackedInt32Array()
|
||||
var logo_pixels: Array[Vector2i] = []
|
||||
var title_craters: Array[Dictionary] = []
|
||||
var title_rows: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
game = get_parent().get_parent()
|
||||
_build_logo()
|
||||
|
||||
func _build_logo() -> void:
|
||||
for row in range(2):
|
||||
var word := "MISSILE" if row == 0 else "COMMAND"
|
||||
for letter in range(word.length()):
|
||||
for y in range(7):
|
||||
for x in range(5):
|
||||
if FONT[word[letter]][y] & (1 << (4-x)):
|
||||
for sy in range(4):
|
||||
for sx in range(4):
|
||||
logo_pixels.append(Vector2i(46+letter*24+x*4+sx,65+row*43+y*4+sy))
|
||||
# Each crater has a deterministic impact time and persists as missing pixels.
|
||||
for i in range(42):
|
||||
title_craters.append({"pos":Vector2(42+fposmod(i*59.7,174),62+fposmod(i*37.3,79)),"time":1.0+i*.145,"radius":9.0+float(i%5)})
|
||||
|
||||
for row in range(2):
|
||||
for letter in range(7):
|
||||
title_craters.append({"pos":Vector2(56+letter*24,79+row*43),"time":6.2+letter*.1,"radius":20.0})
|
||||
# Precompute erosion times once, not a pixel/crater search every frame.
|
||||
for point in logo_pixels:
|
||||
var erased_at := 7.7
|
||||
for crater in title_craters:
|
||||
var distance := Vector2(point).distance_to(crater.pos)
|
||||
if distance < crater.radius:
|
||||
erased_at = minf(erased_at,crater.time+distance/30.0)
|
||||
if not title_rows.has(point.y): title_rows[point.y] = []
|
||||
title_rows[point.y].append(Vector2(point.x,erased_at))
|
||||
for y in title_rows:
|
||||
title_rows[y].sort_custom(func(a: Vector2,b: Vector2) -> bool: return a.x < b.x)
|
||||
|
||||
func rect(position: Vector2, size: Vector2, color: Color) -> void:
|
||||
var start := vertices.size()
|
||||
for point in [position,position+Vector2(size.x,0),position+size,position+Vector2(0,size.y)]:
|
||||
vertices.append(Vector3(floorf(point.x),floorf(point.y),0))
|
||||
colors.append(color)
|
||||
indices.append_array(PackedInt32Array([start,start+1,start+2,start,start+2,start+3]))
|
||||
|
||||
func segment(a: Vector2,b: Vector2,color: Color) -> void:
|
||||
var count := maxi(1,int(maxf(absf(b.x-a.x),absf(b.y-a.y))))
|
||||
for i in range(count+1): rect(a.lerp(b,float(i)/count).round(),Vector2.ONE,color)
|
||||
|
||||
func disk(center: Vector2,radius: float,color: Color) -> void:
|
||||
var r := int(radius)
|
||||
for y in range(-r,r+1):
|
||||
var extent := int(sqrt(maxf(0,r*r-y*y)))
|
||||
rect(center+Vector2(-extent,y),Vector2(extent*2+1,1),color)
|
||||
|
||||
func text_at(value: String,at: Vector2,color: Color,size: int = 1) -> void:
|
||||
for i in range(value.length()):
|
||||
if not FONT.has(value[i]): continue
|
||||
for y in range(7):
|
||||
var run := -1
|
||||
for x in range(6):
|
||||
var filled: bool = x < 5 and (FONT[value[i]][y] & (1 << (4-x))) != 0
|
||||
if filled and run < 0: run = x
|
||||
if not filled and run >= 0:
|
||||
rect(at+Vector2((i*6+run)*size,y*size),Vector2((x-run)*size,size),color)
|
||||
run = -1
|
||||
|
||||
func label(value: String,y: float,color: Color,size: int = 1) -> void:
|
||||
text_at(value,Vector2((256-(value.length()*6-1)*size)/2.0,y),color,size)
|
||||
|
||||
func palette() -> Array:
|
||||
return [[Color("ffff00"),Color("203cff"),Color("ff2525")],[Color("20e5ff"),Color("ff3030"),Color("ffffff")],[Color("ff3030"),Color("24ff35"),Color("ffff00")]][int((game.wave-1)/2)%3]
|
||||
|
||||
func landscape() -> void:
|
||||
var p := palette()
|
||||
rect(Vector2(0,214),Vector2(256,17),p[0])
|
||||
for i in range(3):
|
||||
var x: float = game.BASE_X[i]
|
||||
for y in range(12): rect(Vector2(x-8-y,202+y),Vector2(16+y*2,1),p[0])
|
||||
if game.bases[i]:
|
||||
var slots := [Vector2(0,-9),Vector2(-2,-6),Vector2(2,-6),Vector2(-4,-3),Vector2(0,-3),Vector2(4,-3),Vector2(-6,0),Vector2(-2,0),Vector2(2,0),Vector2(6,0)]
|
||||
for missile in range(game.ammo[i]):
|
||||
var at: Vector2 = Vector2(x,213)+slots[missile]
|
||||
rect(at-Vector2(0,2),Vector2(1,3),p[1])
|
||||
rect(at-Vector2(1,0),Vector2(3,1),p[1])
|
||||
else: rect(Vector2(x-7,207),Vector2(14,7),Color.BLACK)
|
||||
for i in range(6):
|
||||
var x: float = game.CITY_X[i]
|
||||
if game.cities[i]:
|
||||
for building in range(7):
|
||||
var h := 2+(building*3+i)%7
|
||||
rect(Vector2(x-9+building*3,214-h),Vector2(2,h),p[1])
|
||||
rect(Vector2(x-9,212),Vector2(20,2),p[1])
|
||||
else:
|
||||
rect(Vector2(x-9,213),Vector2(19,2),Color.BLACK)
|
||||
|
||||
func hud() -> void:
|
||||
var p := palette()
|
||||
text_at(str(game.score),Vector2(14,7),p[2])
|
||||
text_at(str(game.high_score),Vector2(106,7),p[2])
|
||||
text_at("1UP",Vector2(17,18),p[1])
|
||||
text_at("HIGH",Vector2(112,18),p[1])
|
||||
text_at("WAVE %d" % game.wave,Vector2(204,7),p[2])
|
||||
|
||||
func title() -> void:
|
||||
var t: float = game.attract_time
|
||||
var color := Color.from_hsv(fposmod(floor(t*5)/7,1),1,1)
|
||||
for y in title_rows:
|
||||
var run_start := -1
|
||||
var run_end := -1
|
||||
for pixel in title_rows[y]:
|
||||
var x := int(pixel.x)
|
||||
var visible: bool = t < pixel.y
|
||||
if run_start >= 0 and (not visible or x > run_end+1):
|
||||
rect(Vector2(run_start,y),Vector2(run_end-run_start+1,1),color)
|
||||
run_start = -1
|
||||
if visible:
|
||||
if run_start < 0: run_start = x
|
||||
run_end = x
|
||||
if run_start >= 0: rect(Vector2(run_start,y),Vector2(run_end-run_start+1,1),color)
|
||||
for crater in title_craters:
|
||||
var age: float = t-crater.time
|
||||
if age > 0 and age < .65:
|
||||
disk(crater.pos,minf(9,age*25)*(1-age/.65),Color.from_hsv(fposmod(t*.7+.33,1),1,1))
|
||||
label("PUSH START",174,Color("24ff35"))
|
||||
label("MISSILE COMMAND",193,Color("2040ff"))
|
||||
label("FREE PLAY",222,Color("ffff00"))
|
||||
|
||||
func _draw() -> void:
|
||||
if game == null: return
|
||||
vertices.clear()
|
||||
colors.clear()
|
||||
indices.clear()
|
||||
draw_rect(Rect2(Vector2.ZERO,SIZE),Color.BLACK)
|
||||
if game.phase == "title":
|
||||
title()
|
||||
elif game.phase == "leaderboard":
|
||||
landscape()
|
||||
hud()
|
||||
label("HIGH SCORES",37,Color("2040ff"))
|
||||
for i in range(8):
|
||||
var value := "%06d" % game.leaderboard[i] if i < game.leaderboard.size() else "------"
|
||||
text_at("P1" if i < game.leaderboard.size() else "--",Vector2(70,56+i*12),Color.YELLOW)
|
||||
text_at(value,Vector2(126,56+i*12),Color.YELLOW)
|
||||
label("BONUS CITY EVERY",163,Color("2040ff"))
|
||||
label("%d POINTS" % game.bonus_city_interval,176,Color.YELLOW)
|
||||
label("PUSH START",195,Color("2040ff"))
|
||||
else:
|
||||
landscape()
|
||||
hud()
|
||||
var p := palette()
|
||||
for missile in game.incoming:
|
||||
segment(missile.origin,missile.pos,p[2])
|
||||
if missile.kind == "smart":
|
||||
var at: Vector2 = missile.pos
|
||||
segment(at+Vector2(0,-3),at+Vector2(2,0),Color.WHITE)
|
||||
segment(at+Vector2(2,0),at+Vector2(0,3),Color.WHITE)
|
||||
segment(at+Vector2(0,3),at+Vector2(-2,0),Color.WHITE)
|
||||
segment(at+Vector2(-2,0),at+Vector2(0,-3),Color.WHITE)
|
||||
else: rect(missile.pos,Vector2(2,2),Color.WHITE)
|
||||
for shot in game.interceptors:
|
||||
segment(shot.origin,shot.pos,p[1])
|
||||
rect(shot.pos,Vector2(2,2),Color.WHITE)
|
||||
for plane in game.aircraft:
|
||||
var at: Vector2 = plane.pos
|
||||
segment(at-Vector2(6,0),at+Vector2(6,0),p[2])
|
||||
segment(at-Vector2(2,4),at+Vector2(2,4),p[2])
|
||||
segment(at-Vector2(5,2),at-Vector2(5,-2),p[2])
|
||||
for blast in game.blasts:
|
||||
var color: Color = p[2] if int(game.clock*12)%3 != 0 else Color.WHITE
|
||||
disk(blast.pos,blast.radius,color)
|
||||
if blast.age > 1.0: disk(blast.pos,blast.radius*.5,Color.BLACK)
|
||||
if game.phase in ["play","ready"]:
|
||||
var at: Vector2 = game.aim.round()
|
||||
segment(at-Vector2(3,0),at+Vector2(3,0),p[1])
|
||||
segment(at-Vector2(0,3),at+Vector2(0,3),p[1])
|
||||
if game.phase == "ready": label("DEFEND CITIES",181,p[1])
|
||||
if game.phase == "bonus":
|
||||
label("BONUS POINTS",85,p[1])
|
||||
label(str(game.wave_bonus),102,p[0],2)
|
||||
label("%d CITIES SAVED" % game.cities.count(true),131,p[1])
|
||||
if game.phase == "lost":
|
||||
disk(Vector2(128,114),minf(75,(5-game.phase_time)*24),p[2])
|
||||
label("THE END",110,Color.BLACK,2)
|
||||
if game.phase == "won":
|
||||
label("YOU WIN!",100,p[0],2)
|
||||
label("CITIES DEFENDED",123,p[1])
|
||||
label("PUSH START",178,p[1])
|
||||
mesh.clear_surfaces()
|
||||
if not vertices.is_empty():
|
||||
var arrays := []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = vertices
|
||||
arrays[Mesh.ARRAY_COLOR] = colors
|
||||
arrays[Mesh.ARRAY_INDEX] = indices
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,arrays)
|
||||
draw_mesh(mesh,null)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://ed0yaf45xgff
|
||||
Reference in new issue
Block a user