@tool extends Node2D const PLAYER = preload("res://games/robotron/art/player.png") const HUMANS = preload("res://games/robotron/art/humans.png") const ROBOTS = preload("res://games/robotron/art/robotrons.png") const TITLE = preload("res://games/robotron/art/titlescreen_example.png") const COLORS := [Color.WHITE,Color.YELLOW,Color("ff8000"),Color.MAGENTA,Color.GREEN,Color.RED,Color.BLUE] # Small pixel font for HUD and instructions; artwork remains in supplied sheets. const FONT := { "A":"01110100011000111111100011000110001","B":"11110100011000111110100011000111110","C":"01111100001000010000100001000001111","D":"11110100011000110001100011000111110", "E":"11111100001000011110100001000011111","F":"11111100001000011110100001000010000","G":"01111100001000010111100011000101111","H":"10001100011000111111100011000110001", "I":"11111001000010000100001000010011111","J":"00111000100001000010000101001001100","K":"10001100101010011000101001001010001","L":"10000100001000010000100001000011111", "M":"10001110111010110101100011000110001","N":"10001110011010110011100011000110001","O":"01110100011000110001100011000101110","P":"11110100011000111110100001000010000", "Q":"01110100011000110001101011001001101","R":"11110100011000111110101001001010001","S":"01111100001000001110000010000111110","T":"11111001000010000100001000010000100", "U":"10001100011000110001100011000101110","V":"10001100011000110001100010101000100","W":"10001100011000110101101011101110001","X":"10001100010101000100010101000110001", "Y":"10001100010101000100001000010000100","Z":"11111000010001000100010001000011111","0":"01110100011001110101110011000101110","1":"00100011000010000100001000010001110", "2":"01110100010000100010001000100011111","3":"11110000010000101110000010000111110","4":"00010001100101010010111110001000010","5":"11111100001000011110000010000111110", "6":"01110100001000011110100011000101110","7":"11111000010001000100010000100001000","8":"01110100011000101110100011000101110","9":"01110100011000101111000010000101110", "-":"00000000000000011111000000000000000",":":"00000001000010000000001000010000000","!":"00100001000010000100001000000000100",".":"00000000000000000000000000010000100"} var sprites: Dictionary = {} var game: Node var badge: ImageTexture func _ready() -> void: game = get_parent().get_parent() var player := PLAYER.get_image() for f in range(3): for d in range(4): register("player%d_%d"%[d,f],player,Rect2i([0,7,14,23][d],[0,12,25][f],[5,5,7,7][d],[11,12,10][f]),Vector2i(8,12)) var human := HUMANS.get_image() for f in range(3): for d in range(4): register("human0_%d_%d"%[d,f],human,Rect2i([0,9,19,30][d],[0,13,27][f],[8,8,9,8][d],12),Vector2i(10,14)) register("human1_%d_%d"%[d,f],human,Rect2i([0,9,18,28][d],[41,54,70][f],[8,8,10,10][d],12 if f==0 else 14),Vector2i(10,14)) register("human2_%d_%d"%[d,f],human,Rect2i([1,7,12,18][d],[85,96,108][f],[4,4,5,4][d],10 if f==0 else 11),Vector2i(6,12)) register("prog",human,Rect2i(0,120,11,11)) var robots := ROBOTS.get_image() for f in range(3): register("grunt%d"%f,robots,Rect2i(f*12,0,9,13),Vector2i(10,14)) register("hulk%d"%f,robots,Rect2i(14,[14,30,46][f],11,15),Vector2i(14,16)) register("brain%d"%f,robots,Rect2i(1,[141,157,174][f],12,15),Vector2i(14,16)) register("spheroid",robots,Rect2i(45,65,11,11)) register("enforcer",robots,Rect2i(39,79,11,11)) register("quark",robots,Rect2i(27,103,9,9)) register("tank0",robots,Rect2i(29,116,14,17)) register("tank1",robots,Rect2i(44,116,14,17)) register("tank2",robots,Rect2i(59,116,14,17)) register("shell",robots,Rect2i(0,134,7,7)) for i in range(6): var key := "electrode%d"%i register(key,robots,[Rect2i(1,220,7,7),Rect2i(11,220,7,7),Rect2i(21,220,7,7),Rect2i(32,220,7,7),Rect2i(49,220,7,7),Rect2i(84,220,7,7)][i]) var mask: Image = sprites[key].get_image() for y in range(mask.get_height()): for x in range(mask.get_width()): var alpha := mask.get_pixel(x,y).a mask.set_pixel(x,y,Color(1,1,1,alpha)) sprites[key] = ImageTexture.create_from_image(mask) # Reuse the supplied title's exact Williams emblem as a tintable mask. var emblem := TITLE.get_image().get_region(Rect2i(48,16,56,54)) emblem.convert(Image.FORMAT_RGBA8) for y in range(emblem.get_height()): for x in range(emblem.get_width()): var c := emblem.get_pixel(x,y) emblem.set_pixel(x,y,Color.WHITE if c.r>.8 and c.g>.8 and c.b>.8 else Color.TRANSPARENT) badge = ImageTexture.create_from_image(emblem) func register(key: String, image: Image, rect: Rect2i, canvas: Vector2i = Vector2i.ZERO) -> void: var piece := image.get_region(rect) if canvas!=Vector2i.ZERO: var padded := Image.create(canvas.x,canvas.y,false,Image.FORMAT_RGBA8) piece.convert(Image.FORMAT_RGBA8) padded.blit_rect(piece,Rect2i(Vector2i.ZERO,piece.get_size()),Vector2i((canvas.x-piece.get_width())/2,canvas.y-piece.get_height())) piece = padded sprites[key] = ImageTexture.create_from_image(piece) func sprite(key: String, point: Vector2, tint: Color = Color.WHITE) -> void: var texture: Texture2D = sprites[key] draw_texture(texture,(point-texture.get_size()*.5).round(),tint) func text(value: String, point: Vector2, color: Color = Color.WHITE) -> void: for i in range(value.length()): if not FONT.has(value[i]): continue var bits: String = FONT[value[i]] for pixel in range(35): if bits[pixel]=="1": draw_rect(Rect2(point+Vector2(i*6+pixel%5,pixel/5),Vector2.ONE),color) func center(value: String,y: float,color: Color = Color.WHITE) -> void: text(value,Vector2(roundf((292-value.length()*6)/2.0),y),color) func direction_index(v: Vector2) -> int: if absf(v.x)>absf(v.y): return 0 if v.x<0 else 1 return 3 if v.y<0 else 2 func draw_title() -> void: draw_texture_rect(TITLE,Rect2(0,0,292,240),false) # Animated emblems move around the perimeter, rather than freezing the screenshot. draw_rect(Rect2(0,0,292,44),Color.BLACK) draw_rect(Rect2(0,200,292,40),Color.BLACK) draw_rect(Rect2(0,44,36,164),Color.BLACK) draw_rect(Rect2(256,44,36,164),Color.BLACK) var perimeter := 2.0*(252+200) var shift: float = 0.0 if game.reduced_flashes else game.phase_time*24 for i in range(28): var distance := fposmod(i*perimeter/28+shift,perimeter) var p: Vector2 if distance<252: p=Vector2(20+distance,20) elif distance<452: p=Vector2(272,20+distance-252) elif distance<704: p=Vector2(272-(distance-452),220) else: p=Vector2(20,220-(distance-704)) draw_texture_rect(badge,Rect2((p-Vector2(13,13)).round(),Vector2(26,26)),false,COLORS[(i+int(game.phase_time*2))%COLORS.size()] if not game.reduced_flashes else COLORS[i%COLORS.size()]) draw_rect(Rect2(108,158,76,10),Color.BLACK) center("FREE PLAY",160,Color.BLUE) func _draw() -> void: if game==null: return draw_rect(Rect2(0,0,292,240),Color.BLACK) if game.phase=="title": draw_title() return var accent: Color = COLORS[int(game.clock*3)%COLORS.size()] if not game.reduced_flashes else Color.RED draw_rect(Rect2(4,16,284,216),Color.BLUE if game.phase in ["story","roster","scores"] else Color.RED,false,1) text(str(game.last_score if game.demo else game.score),Vector2(52,4)) if game.phase in ["story","roster","scores"]: center("FREE PLAY",233,Color.GREEN) else: for i in range(mini(maxi(0,game.lives-1),7)): sprite("player2_0",Vector2(134+i*10,8)) center("%d WAVE"%game.wave,233,Color.BLUE) if game.phase=="story": center("ROBOTRON: 2084",30,accent) var lines := ["YOU ARE THE LAST HOPE OF MANKIND.","SAVE THE LAST HUMAN FAMILY!","MOVE AND FIRE IN EIGHT DIRECTIONS.","WASD MOVES ARROWS FIRE","RESCUE HUMANS FOR 1000 TO 5000","EXTRA LIFE EVERY %d POINTS"%game.extra_life_interval if game.extra_life_interval>0 else "BONUS LIVES DISABLED"] for i in range(lines.size()): if game.phase_time>i*.8: center(lines[i],56+i*22,COLORS[(i+2)%7]) sprite("player2_0",Vector2(114,205)) sprite("human0_2_0",Vector2(146,205)) return if game.phase=="roster": center("THE ROBOTRONS",30,accent) var names := ["GRUNT","HULK","SPHEROID","ENFORCER","BRAIN","QUARK","TANK","PROG"] var keys := ["grunt0","hulk0","spheroid","enforcer","brain0","quark","tank0","prog"] for i in range(8): sprite(keys[i],Vector2(48,54+i*20)) text(names[i],Vector2(76,50+i*20),COLORS[i%7]) return if game.phase=="scores": center("ROBOTRON HERO",60,accent) center("HIGH SCORE",92) center(str(game.high_score),110,Color.YELLOW) center("PUSH START",176,accent) return for entity in game.entities: if not entity.alive: continue var frame := int(game.clock*8+entity.frame)%3 var key: String = entity.kind match key: "human": key="human%d_%d_%d"%[entity.human_type,direction_index(entity.velocity),frame] "grunt","hulk","brain","tank": key+=str(frame) "electrode": key+=str((game.wave+entity.id)%6) "converting": key="prog" var tint := Color.WHITE if entity.kind=="converting": tint = accent elif entity.kind=="electrode": tint = [Color.GREEN,Color.RED,Color.BLUE,Color.YELLOW,Color.MAGENTA,Color.CYAN][(game.wave-1)%6] sprite(key,entity.position,tint) if game.phase=="dead": for i in range(8): var offset: Vector2 = Vector2.from_angle(i*TAU/8)*(1.2-game.phase_time)*30 sprite("player%d_%d"%[direction_index(game.facing),i%3],game.player+offset,Color(1,1,1,clampf(game.phase_time,0,1))) elif game.phase not in ["won","lost","transition"]: if game.invulnerable<=0 or int(game.clock*12)%2==0: sprite("player%d_%d"%[direction_index(game.facing),int(game.clock*10)%3 if game.moving else 0],game.player) for shot in game.shots: var tail: Vector2 = shot.position-shot.velocity.normalized()*5 draw_line(tail.round(),shot.position.round(),Color.WHITE,1) for shot in game.enemy_shots: if shot.kind=="shell": sprite("shell",shot.position) else: draw_rect(Rect2(shot.position.round()-Vector2(1,1),Vector2(3,3)),accent) for effect in game.effects: if effect.label!="": text(effect.label,effect.position.round()-Vector2(10,8),accent) else: for i in range(8): var p: Vector2 = effect.position+Vector2.from_angle(i*TAU/8)*(1-effect.life/.25)*12 draw_rect(Rect2(p.round(),Vector2(2,2)),accent) if game.phase=="ready": center("GET READY",110,accent) if game.phase=="transition": for i in range(8): var expansion: float = fposmod((1.8-game.phase_time)*150+i*18,150) draw_rect(Rect2(Vector2(146,124)-Vector2(expansion,expansion*.7),Vector2(expansion*2,expansion*1.4)),accent,false,1) if game.phase=="won": center("YOU WIN!",114,accent) if game.phase=="lost" or game.demo: center("GAME OVER",114,accent)