package;

import openfl.Assets;
import res.Object;
import res.RES;
import res.ResolutionPresets;
import res.Scene;
import res.Sprite;
import res.geom.Recti;
import res.graphics.Graphics;
import res.input.ControllerEvent;
import res.input.Key;
import res.input.MouseEvent;
import res.platforms.openfl.OpenFLPlatform;
import res.rom.Rom;
import res.text.Textmap;
import res.tiles.Tilemap;
import res.tools.MathTools.wrapi;

using Math;
using StringTools;

var sound:Bool = true;

class Projectile {
	public var x:Float;
	public var y:Float;
	public final dir:Int;
	public var speed:Float = 80;

	public var type:Int = 1;
	public var damage:Int = 1;
	public var cost:Int = 1;

	public var amp:Float = 0;

	var or_x:Float;
	var phase:Float = 0;

	public var remove:Bool = false;

	public var particleTime:Float = 0;

	public function new(x:Float, y:Float, dir:Int) {
		this.x = or_x = x;
		this.y = y;
		this.dir = dir;
	}

	public function update(dt:Float) {
		y += dir * speed * dt;
		x = or_x + Math.sin(phase) * amp;

		phase += Math.PI * 2 * dt;

		remove = y < 0 || y > 144;

		particleTime -= dt;
	}

	public function render(g:Graphics) {
		switch (type) {
			case 1:
				g.drawRect(Math.floor(x) - 1, Math.floor(y) - 1, 2, 2, 10);
			case 2:
				g.drawRect(Math.floor(x) - 2, Math.floor(y) - 4, 4, 4, 10);
				g.drawRect(Math.floor(x) - 1, Math.floor(y) - 5, 4, 1, 10);
		}
	}
}

typedef Particle = {
	x:Float,
	y:Float,
	sx:Float,
	sy:Float,
	lifeTime:Float
}

enum UnitSlot {
	EMPTY;
	OCCUPIED;
	NONE;
}

typedef Laser = {
	obj:Object,
	time:Float,
	totalCoinTime:Float,
	coinTime:Float,
	damage:Int,
	cost:Int
};

class GameMain extends Scene {
	static final SELECT_DELAY:Float = 0.25;
	static final SELECT_TIME:Float = 4;

	var button:Object;

	var scoreText:Textmap;

	var projectiles:Array<Projectile> = [];
	var particles:Array<Particle> = [];
	var lasers:Array<Laser> = [];
	var rockets:Array<Rocket> = [];
	var tankProjectiles:Array<Object> = [];

	var pushProgress:Graphics;
	var projectilesGraphics:Graphics;
	var particlesGraphics:Graphics;
	var tankHealth:Graphics;

	var buttonDown:Bool = false;
	var buttonTime:Float = 0;

	var coins:Object;
	var units:Object = new Object();
	var projectileSprites:Object;

	var tank:MegaTank;

	var coinSprite:Sprite;
	var iconsSprite:Sprite;

	var icons:Map<String, Object> = [];

	var selector:Object;

	var unitSlots:Array<Array<UnitSlot>> = [
		for (line in 0...4) [
			for (col in 0...20)
				(line % 2 == 0 && col % 2 != 0) || (line % 2 != 0 && col % 2 == 0) ? NONE : EMPTY
		]
	];

	static final itemPrices = [
		'blue' => 100,
		'green' => 200,
		'yellow' => 400,
		'red' => 1000,
		'rocket' => 100,
		'drone' => 200,
		'laser' => 400,
		'fireball' => 1000
	];

	var totalScore:Int = 0;
	var totalSpent:Int = 0;
	var totalTime:Float = 0;

	var gameFinished:Bool = false;

	public var score(default, set):Int = 0;

	function set_score(val:Int) {
		scoreText.textCentered(0, 'Sx$val');
		return score = val;
	}

	public function new(res:RES) {
		super(res);

		tank = new MegaTank(res.rom.sprites['mega_tank']);
		tank.x = ((res.width - tank.sprite.width) / 2).floor();
		tank.y = 10;
		tank.wrap = false;
		tank.shot = () -> {
			spawnTankProjectile();
			if (sound)
				Assets.getSound('assets/snd/rocket.wav').play();
		};
		add(tank);

		add(units);

		particlesGraphics = res.createGraphics();

		particlesGraphics.draw = (g) -> {
			g.clear();

			for (p in particles) {
				g.setPixel(p.x.floor(), p.y.floor(), 10);
			}
		};
		add(particlesGraphics);

		projectilesGraphics = res.createGraphics();
		projectilesGraphics.draw = (g) -> {
			g.clear();

			for (proj in projectiles) {
				proj.render(g);
			}
		};
		add(projectilesGraphics);

		projectileSprites = new Object();
		add(projectileSprites);

		ui = res.rom.tilemaps['ui_bg'].clone();
		add(ui);

		iconsSprite = Sprite.fromTiles(res.rom.tilesets['tiles'], [for (n in 0...8) ([[n]])]);

		var blueTankIcon = new Object(iconsSprite);
		blueTankIcon.currentFrameIndex = 0;
		blueTankIcon.x = 8;
		blueTankIcon.y = 128;
		add(blueTankIcon);
		icons['blue'] = blueTankIcon;

		var greenTankIcon = new Object(iconsSprite);
		greenTankIcon.currentFrameIndex = 1;
		greenTankIcon.x = 24;
		greenTankIcon.y = 128;
		add(greenTankIcon);
		icons['green'] = greenTankIcon;

		var yellowTankIcon = new Object(iconsSprite);
		yellowTankIcon.currentFrameIndex = 2;
		yellowTankIcon.x = 40;
		yellowTankIcon.y = 128;
		add(yellowTankIcon);
		icons['yellow'] = yellowTankIcon;

		var redTankIcon = new Object(iconsSprite);
		redTankIcon.currentFrameIndex = 3;
		redTankIcon.x = 56;
		redTankIcon.y = 128;
		add(redTankIcon);
		icons['red'] = redTankIcon;

		var rocketIcon = new Object(iconsSprite);
		rocketIcon.currentFrameIndex = 4;
		rocketIcon.x = 96;
		rocketIcon.y = 128;
		add(rocketIcon);
		icons['rocket'] = rocketIcon;

		var droneIcon = new Object(iconsSprite);
		droneIcon.currentFrameIndex = 5;
		droneIcon.x = 112;
		droneIcon.y = 128;
		add(droneIcon);
		icons['drone'] = droneIcon;

		var laserIcon = new Object(iconsSprite);
		laserIcon.currentFrameIndex = 6;
		laserIcon.x = 128;
		laserIcon.y = 128;
		add(laserIcon);
		icons['laser'] = laserIcon;

		var fireballIcon = new Object(iconsSprite);
		fireballIcon.currentFrameIndex = 7;
		fireballIcon.x = 144;
		fireballIcon.y = 128;
		add(fireballIcon);
		icons['fireball'] = fireballIcon;

		tankHealth = res.createGraphics(152, 1);
		tankHealth.x = 4;
		tankHealth.y = 17;
		tankHealth.drawRect(0, 0, 152, 1, 8);
		add(tankHealth);

		selector = new Object(res.rom.sprites['select']);
		selector.visible = false;
		selector.y = 126;
		add(selector);

		var font = res.createFont('score_font', res.rom.tilesets['score_font'], 'Sx0123456789');

		scoreText = res.createTextmap(font);
		scoreText.scrollY = -2;
		add(scoreText);

		score = 0;

		updateIconsVisibility();

		coinSprite = Sprite.fromTiles(res.rom.tilesets['score_font'], [[[0]]]);
		coins = new Object();
		coins.wrap = false;
		add(coins);

		pushProgress = res.createGraphics();
		add(pushProgress);

		button = new Object(res.rom.sprites['button']);
		button.x = 72;
		button.y = 121;
		add(button);

		res.console.addCommand('score', 'Set score', (args:Array<String>) -> {
			score = Std.parseInt(args[0]);
			updateIconsVisibility();
		});

		res.console.addCommand('win', (_) -> {
			winGame();
		});

		mouseEvent = (event) -> {
			switch (event) {
				case DOWN(_):
					pushButton();
				case UP(_):
					releaseButton();
			}
		};

		controllerEvent = (event) -> {
			switch (event) {
				case BUTTON_DOWN(_, button):
					switch (button) {
						case A:
							pushButton();
						case _:
					}
				case BUTTON_UP(_, button):
					switch (button) {
						case A:
							releaseButton();
						case _:
					}
				case _:
			}
		}
	}

	override function keyDown(keyCode:Int) {
		if (keyCode == Key.ESCAPE || canQuit)
			res.setScene(new Menu(res, !canQuit));
	}

	function updateIconsVisibility() {
		for (id => icon in icons) {
			icon.visible = score >= itemPrices[id];
		}
	}

	function getEmptySlots():Array<{line:Int, col:Int}> {
		final result:Array<{line:Int, col:Int}> = [];
		for (line in 0...unitSlots.length) {
			for (col in 0...unitSlots[line].length) {
				if (unitSlots[line][col] == EMPTY) {
					result.push({line: line, col: col});
				}
			}
		}

		result.sort(function(a, b) {
			final la = Math.abs(a.col - 10);
			final lb = Math.abs(b.col - 10);

			return la < lb ? -1 : 1;
		});

		return result;
	}

	function spawnTankProjectile() {
		var proj = new Object(res.rom.sprites['tank_bullet']);
		proj.x = tank.x + tank.width / 2;
		proj.y = tank.y + tank.height;
		projectileSprites.add(proj);
		tankProjectiles.push(proj);
	}

	function spawnUnit(unit:Unit) {
		var empty = getEmptySlots();

		if (empty.length > 0) {
			final slot = empty[0];

			unit.shotCb = (type:String) -> {
				switch (type) {
					case 'bullet':
						spawnProjectile((unit.x + 4).floor(), unit.y.floor(), -1, 200);
					case 'laser':
						spawnLaser(unit.x + 4, unit.y);
					case 'big_bullet':
						spawnProjectile((unit.x + 4).floor(), unit.y.floor(), -1, 150, 2, 10, 5);
				}
			};
			unit.x = slot.col * 8;
			unit.y = 72 + slot.line * 8;
			unit.line = slot.line;
			unit.col = slot.col;
			units.add(unit);

			allUnits.push(unit);

			unitSlots[slot.line][slot.col] = OCCUPIED;
		} else
			trace('No more slots');
	}

	function spawnProjectile(x:Int, y:Int, dir:Int, speed:Float = 80, type:Int = 1, damage:Int = 1, cost:Int = 1) {
		var proj = new Projectile(x, y, dir);
		proj.speed = speed;
		proj.type = type;
		proj.damage = damage;
		proj.cost = cost;
		projectiles.push(proj);
	}

	function spawnLaser(x:Float, y:Float, time:Float = 0.5, type:Int = 0, damage:Int = 10, cost:Int = 3) {
		var laserObj = new Object(res.rom.sprites[type == 0 ? 'laser' : 'big_laser']);
		laserObj.playing = true;
		laserObj.x = x - laserObj.width / 2;
		laserObj.height = y.floor();
		projectileSprites.add(laserObj);

		lasers.push({
			obj: laserObj,
			time: time,
			totalCoinTime: time / cost,
			coinTime: 0,
			damage: damage,
			cost: cost
		});
	}

	function spawnRocket(x:Float, y:Float, type:String, travelTime:Float = 1, damage:Int = 100, cost:Int = 10) {
		var obj = new Object(res.rom.sprites['rockets']);

		obj.currentFrameIndex = switch (type) {
			case 'rocket': 0;
			case 'drone': 1;
			case 'fireball': 2;
			case _: 0;
		};

		projectileSprites.add(obj);

		var rocket = new Rocket(obj, x, y, travelTime, damage, cost);
		rockets.push(rocket);

		if (sound)
			Assets.getSound('assets/snd/rocket.wav').play();
	}

	function smallExplosion(atx:Int, aty:Int) {
		for (n in 0...10) {
			final sp = (50 + 30 * Math.random());
			particles.push({
				x: atx,
				y: aty,
				sx: Math.sin(Math.PI * 2 * (n / 10)) * sp,
				sy: Math.cos(Math.PI * 2 * (n / 10)) * sp,
				lifeTime: 0.2 + Math.random() * 0.3
			});
		}
	}

	function incScore(n:Int = 1) {
		score += n;
		totalScore += n;
	}

	function hitTank(x:Float, y:Float, damage:Int = 1, spawnCoins:Int = 1) {
		tank.health -= damage;
		if (tank.health <= 0) {
			tank.health = 0;
			winGame();
		}

		var nw = 152 * (tank.health / MegaTank.maxHealth);
		var nx = (152 - nw) / 2;

		tankHealth.clear();
		tankHealth.drawRect(nx.floor(), 0, nw.ceil(), 1, 8);

		smallExplosion(x.floor(), y.floor());

		if (sound)
			Assets.getSound('assets/snd/hit.wav').play();

		for (_ in 0...spawnCoins) {
			spawnCoin(x, y);
		}
	}

	function spawnCoin(atx:Float, aty:Float) {
		var coin = new Coin(coinSprite, atx, aty, () -> {
			if (sound)
				Assets.getSound('assets/snd/coin.wav').play();
			incScore();

			updateIconsVisibility();
		});
		coins.add(coin);
	}

	override function update(dt:Float) {
		super.update(dt);

		for (part in particles) {
			if (part.lifeTime <= 0) {
				particles.remove(part);
			} else {
				part.x += part.sx * dt;
				part.y += part.sy * dt;
				part.lifeTime -= dt;
			}
		}

		if (!gameFinished) {
			for (proj in projectiles) {
				if (!proj.remove) {
					proj.update(dt);

					if (Recti.intersect(proj.x.floor(), proj.y.floor(), 2, 2, tank.x.floor(), tank.y.floor(), tank.sprite.width, tank.sprite.height - 10)) {
						hitTank(proj.x, proj.y, proj.damage, proj.cost);

						projectiles.remove(proj);
					} else {
						if (proj.particleTime <= 0) {
							particles.push({
								x: proj.x + (-2 + Math.random() * 4),
								y: proj.y + Math.random() * 2,
								sx: 0,
								sy: 0,
								lifeTime: 0.3 * Math.random()
							});
							proj.particleTime = Math.random() * 0.1;
						}
					}
				} else
					projectiles.remove(proj);
			}

			for (proj in tankProjectiles) {
				if (proj.y >= 144) {
					tankProjectiles.remove(proj);
					projectileSprites.remove(proj);
				} else {
					for (unit in allUnits) {
						if (Recti.intersect(unit.x.floor(), unit.y.floor(), unit.width.floor(), unit.height.floor(), proj.x.floor(), proj.y.floor(),
							proj.width.floor(), proj.height.floor())) {
							smallExplosion((unit.x + unit.width / 2).floor(), (unit.y + unit.height / 2).floor());
							units.remove(unit);
							allUnits.remove(unit);
							unitSlots[unit.line][unit.col] = EMPTY;
							if (sound)
								Assets.getSound('assets/snd/explosion.wav').play();
						}
					}

					particles.push({
						x: proj.x + (proj.width - 1) * Math.random(),
						y: proj.y,
						sx: 0,
						sy: -30,
						lifeTime: 0.3 * Math.random()
					});

					proj.y += 10 * dt;
				}
			}

			for (rocket in rockets) {
				if (Recti.intersect(rocket.obj.x.floor(), rocket.obj.y.floor(), rocket.obj.width, rocket.obj.height, tank.x.floor(), tank.y.floor(),
					tank.sprite.width, tank.sprite.height - 10)) {
					hitTank(rocket.obj.x + rocket.obj.width / 2, rocket.obj.y, rocket.damage, rocket.cost);
					rocket.obj.toRemove = true;
					rockets.remove(rocket);
				} else {
					for (tp in tankProjectiles) {
						if (Recti.intersect(rocket.obj.x.floor(), rocket.obj.y.floor(), rocket.obj.width, rocket.obj.height, tp.x.floor(), tp.y.floor(),
							tp.width.floor(), tp.height.floor())) {
							tankProjectiles.remove(tp);
							projectileSprites.remove(tp);
							smallExplosion((tp.x + tp.width / 2).floor(), (tp.y + tp.height / 2).floor());
						}
					}

					rocket.update(dt);
					if (rocket.obj.toRemove)
						rockets.remove(rocket);
					else {
						particles.push({
							x: rocket.obj.x + 2 + (Math.random() * (rocket.obj.width - 4)),
							y: rocket.obj.y + rocket.obj.height,
							sx: 0,
							sy: 100,
							lifeTime: 0.3 * Math.random()
						});
					}
				}
			}

			for (laser in lasers) {
				if (laser.time <= 0) {
					projectileSprites.remove(laser.obj);
					lasers.remove(laser);
				} else {
					if (laser.coinTime <= 0) {
						if (Recti.intersect(laser.obj.x.floor() - 1, 0, laser.obj.width, laser.obj.height.floor(), tank.x.floor(), tank.y.floor(),
							tank.sprite.width, tank.sprite.height - 10)) {
							hitTank(laser.obj.x + laser.obj.width / 2, tank.y + tank.height - 10, Math.ceil(Math.max(1, laser.damage / laser.cost)), 1);
						}

						laser.coinTime = laser.totalCoinTime + laser.coinTime;
					} else
						laser.coinTime -= dt;
					laser.time -= dt;
				}
			}

			if (buttonDown) {
				if (buttonTime >= SELECT_DELAY) {
					final selectTime = buttonTime - SELECT_DELAY;
					if (selectTime <= SELECT_TIME) {
						pushProgress.drawRect(5, 138, Math.floor((selectTime / SELECT_TIME) * 150), 2, 8);

						final sel = getSelectedItem();

						selector.visible = true;

						switch (sel) {
							case 'blue':
								selector.x = 6;
							case 'green':
								selector.x = 22;
							case 'yellow':
								selector.x = 38;
							case 'red':
								selector.x = 54;
							case 'rocket':
								selector.x = 94;
							case 'drone':
								selector.x = 110;
							case 'laser':
								selector.x = 126;
							case 'fireball':
								selector.x = 142;
							case _:
								selector.visible = false;
						}

						buttonTime += dt;
					} else {
						releaseButton();
					}
				} else
					buttonTime += dt;
			}

			totalTime += dt;
		}
	}

	function winGame() {
		gameFinished = true;
		tank.health = 0;

		timeline.after(1, (_) -> {
			remove(coins);
			remove(projectileSprites);
			remove(projectilesGraphics);
			remove(tankHealth);
			remove(units);

			for (n in 0...20) {
				timeline.after(n * 0.25, (_) -> {
					smallExplosion((tank.x + Math.random() * tank.width).floor(), (tank.y + Math.random() * (tank.height - 10)).floor());
					if (sound)
						Assets.getSound('assets/snd/explosion.wav').play();
				});
			}

			var orX = tank.x;

			timeline.after(20 * 0.25, (_) -> {
				tweening.to(tank, {y: -20}, 3).then(() -> {
					var winText = res.createTextmap(res.rom.fonts['russian']);
					winText.textCentered(5, 'Победа!');
					add(winText);

					var seconds:Int = totalTime.floor();

					var minutes:Int = Math.floor(seconds / 60);
					seconds = minutes % 60;

					timeline.after(1, (_) -> {
						winText.textCentered(7, 'Время: ' + '$minutes'.lpad('0', 2) + ':' + '$seconds'.lpad('0', 2));
					});

					timeline.after(2, (_) -> {
						winText.textCentered(8, 'Собрано ⬤: $totalScore');
					});
					timeline.after(3, (_) -> {
						winText.textCentered(9, 'Потрачено ⬤: $totalSpent');
					});

					timeline.after(4, (_) -> {
						winText.textCentered(11, 'Любая клавиша: выход');

						canQuit = true;
					});
				}).onUpdate((_) -> {
					tank.x = orX + (-2 + Math.random() * 4);
				});
			});

			for (l in 2...13) {
				for (c in 0...20) {
					ui.set(c, l, 0);
				}
			}
		});
	}

	function getSelectedItem():String {
		if (buttonTime >= SELECT_DELAY) {
			final px = 150 * ((buttonTime - SELECT_DELAY) / SELECT_TIME);

			var id:String = if (px >= 143) {
				'fireball';
			} else if (px >= 127) {
				'laser';
			} else if (px >= 111) {
				'drone';
			} else if (px >= 95) {
				'rocket';
			} else if (px >= 55) {
				'red';
			} else if (px >= 39) {
				'yellow';
			} else if (px >= 23) {
				'green';
			} else if (px >= 7) {
				'blue';
			} else {
				'';
			};

			return id != '' && itemPrices[id] != null && score >= itemPrices[id] ? id : '';
		}
		return '';
	}

	function pushButton() {
		if (!gameFinished) {
			button.currentFrameIndex = 1;
			buttonDown = true;
		}
	}

	function releaseButton() {
		if (!gameFinished) {
			if (buttonTime > SELECT_DELAY) {
				var selected = getSelectedItem();

				if (selected != '') {
					switch (selected) {
						case 'blue':
							spawnUnit(new BlueUnit(iconsSprite));
						case 'green':
							spawnUnit(new GreenUnit(iconsSprite));
						case 'yellow':
							spawnUnit(new YellowUnit(iconsSprite));
						case 'red':
							spawnUnit(new RedUnit(iconsSprite));
						case 'laser':
							spawnLaser(80, 111, 1.5, 1, 500, 20);
						case 'rocket':
							spawnRocket(80, 111, 'rocket');
						case 'drone':
							for (n in 0...3) {
								timeline.after(0.3 * n, (_) -> {
									spawnRocket((80 - 30) + 30 * n, 120, 'drone', 1.5, 250, 15);
								});
							}
						case 'fireball':
							for (n in 0...10) {
								timeline.after(0.3 * Math.random(), (_) -> {
									spawnRocket(16 * n, 120 + 10 * Math.random(), 'fireball', 1 + Math.random(), 500, 20);
								});
							}
					}
					score -= itemPrices[selected];
					totalSpent += itemPrices[selected];

					updateIconsVisibility();
				}
			} else {
				spawnProjectile(80, 112, -1, 200);
				if (sound)
					Assets.getSound('assets/snd/shot.wav').play();
			}
			button.currentFrameIndex = 0;
			buttonDown = false;
			buttonTime = 0;
			pushProgress.clear();
			selector.visible = false;
		}
	}

	var canQuit:Bool = false;

	var ui:Tilemap;

	var allUnits:Array<Unit> = [];
}

class Menu extends Scene {
	var text:Textmap;

	var selectedItem(default, set):Int;

	var items:Array<{id:String, txt:Void->String}>;

	var runningText:String = '                    KEFIR специально для igdc.ru #177: One Button Strike. Июль - Август 2021';
	var runningPos:Int = 0;
	var runningDelay:Float = 0;

	function set_selectedItem(val:Int) {
		val = wrapi(val, items.length);
		selectedItem = val;

		updateMenuText();

		return selectedItem;
	}

	function updateMenuText() {
		for (n in 0...items.length)
			text.textAt(4, 12 + n, (n == selectedItem ? '⬤' : ' ') + '${items[n].txt()}');
	}

	public function new(res:RES, resume:Bool = false) {
		super(res);

		final logo = new Object(res.rom.sprites['logo']);
		logo.x = (res.width - logo.width) / 2;
		logo.y = 20;
		add(logo);

		items = [
			{id: 'new', txt: () -> 'Новая игра'},
			{id: 'snd', txt: () -> 'Звук: ${sound ? 'вкл.' : 'выкл.'}'},
			{id: 'ext', txt: () -> 'Выход'}
		];

		if (resume)
			items.unshift({id: 'res', txt: () -> 'Продолжить'});

		text = res.createTextmap(res.rom.fonts['russian']);
		text.scrollY = 1;
		add(text);

		selectedItem = 0;
	}

	override function update(dt:Float) {
		super.update(dt);

		if (runningDelay <= 0) {
			runningPos++;
			if (runningPos >= runningText.length)
				runningPos = 0;
			text.textAt(0, 17, runningText.substr(runningPos));
			runningDelay = 0.15 + runningDelay;
		} else {
			runningDelay -= dt;
		}
	}

	override function mouseEvent(event:MouseEvent) {
		switch (event) {}
	}

	override function controllerEvent(event:ControllerEvent) {
		switch (event) {
			case BUTTON_DOWN(_, button):
				switch (button) {
					case UP:
						selectedItem--;
					case DOWN:
						selectedItem++;
					case START:
						switch (items[selectedItem].id) {
							case 'res':
								res.setScene(GameMain);
							case 'new':
								res.setScene(GameMain, true);
							case 'snd':
								sound = !sound;
								updateMenuText();
							case 'ext':
								res.poweroff();
						}
					case _:
				}
			case _:
		}
	}
}

class Main extends openfl.display.Sprite {
	public function new() {
		super();

		final system = new RES(new OpenFLPlatform(this), ResolutionPresets.GAMEBOY, [
			0xFFD19D, 0xAEB5BD, 0x4D80C9, 0x054494, 0x511E43, 0x100820, 0x823E2C, 0xE93841, 0xF1892D, 0xFFE947, 0xFFA9A9, 0xEB6C82, 0x7D3EBF, 0x1E8A4C,
			0x5AE150
		], Menu, Rom.embed('Rom'));
	}
}
