Added Glitch Room Source code to Gitty

This commit is contained in:
Jeff Luke committed 2026-10-04 11:31:17 -07:00
commit 1ac9eb5644
505 files changed
+28504

No files matched your search

+211
View File
@@ -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)