@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