diff --git a/Gameboy.Player.Godot/LcdColorTheme.cs b/Gameboy.Player.Godot/LcdColorTheme.cs index 8081f8f..ac093ac 100644 --- a/Gameboy.Player.Godot/LcdColorTheme.cs +++ b/Gameboy.Player.Godot/LcdColorTheme.cs @@ -96,5 +96,23 @@ public class LcdColorTheme { Obj0Black = new Color("004A00"), Obj1Black = new Color("000000"), }, + new LcdColorTheme { + Name = "Super Donkey Kong", + BgWhite = new Color("FFFF9C"), + Obj0White = new Color("FFC542"), + Obj1White = new Color("FFFFFF"), + + BgLightGrey = new Color("94B5FF"), + Obj0LightGrey = new Color("FFD600"), + Obj1LightGrey = new Color("FF8484"), + + BgDarkGrey = new Color("639473"), + Obj0DarkGrey = new Color("943A00"), + Obj1DarkGrey = new Color("943A3A"), + + BgBlack = new Color("003A3A"), + Obj0Black = new Color("4A0000"), + Obj1Black = new Color("000000"), + }, }; } \ No newline at end of file diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/BorderContainer.tres b/Gameboy.Player.Godot/Prefabs/GodotBoy/BorderContainer.tres new file mode 100644 index 0000000..3e3c735 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/BorderContainer.tres @@ -0,0 +1,4 @@ +[gd_resource type="Theme" format=3 uid="uid://chyyqvccs2as3"] + +[resource] +VBoxContainer/constants/separation = 4 diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.cs b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.cs index fd0caab..32db598 100644 --- a/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.cs +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.cs @@ -15,37 +15,18 @@ public enum ControlState { public bool IsPlaying => State == ControlState.Playing; [Export] public Control CartControl; + [Export] public Control PlaybackControl; [Export] public OptionButton SaveSlot; [Export] public OnscreenControls[] OnscreenControls; public Screen Screen {get; private set;} public Gameboy Console {get; init;} = new Gameboy(); - private LcdBitmap intro; - private LcdBitmap blank; - // Called when the node enters the scene tree for the first time. public override void _Ready() { if (OnscreenControls is null) { OnscreenControls = new OnscreenControls[0]; } - var intro = new LcdBitmap(Gpu.LCD_WIDTH, Gpu.LCD_HEIGHT); - this.intro = intro; - intro.Fill(ColourPallet.BackgroundDark); - - blank = new LcdBitmap(Gpu.LCD_WIDTH, Gpu.LCD_HEIGHT); - blank.Fill(ColourPallet.BackgroundDark); - - var text = new LcdBitmap[]{ LcdBitmap.StampB, LcdBitmap.StampL, LcdBitmap.StampA, LcdBitmap.StampZ, LcdBitmap.StampO, LcdBitmap.StampR, LcdBitmap.StampB, LcdBitmap.StampO, LcdBitmap.StampY }.Select(stamp => stamp.Invert().Enlarge(4)).ToArray(); - var width = text.Select(stamp => stamp.Width + 1).Sum(); - var height = text.Select(stamp => stamp.Height).Max(); - - var startX = (intro.Width / 2) - (width / 2); - var startY = (intro.Height / 2) - (height / 2); - foreach (var stamp in text) { - intro.Stamp(startX, startY, stamp); - startX += stamp.Width + 1; - } this.Screen = this.GetNode("Screen Layouts"); const int DesktopLayout = 0; @@ -59,15 +40,23 @@ string s when s.Contains("ios") => SkinnedLayout, _ => DesktopLayout } }; - this.Screen.Redraw(intro); } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { - if (State == ControlState.Stopped) { - CartControl.Visible = true; - } else { - CartControl.Visible = false; + if (CartControl is not null) { + if (State == ControlState.Stopped) { + CartControl.Visible = true; + } else { + CartControl.Visible = false; + } + } + if (PlaybackControl is not null) { + if (State == ControlState.Stopped) { + PlaybackControl.Visible = false; + } else { + PlaybackControl.Visible = true; + } } if (State == ControlState.Playing) { @@ -155,9 +144,7 @@ public void Start() { } } - if (blank is not null) { - this.Screen.Redraw(blank); - } + this.Screen.Blank(); this.State = ControlState.Playing; } else { GD.PushError("No cartridge loaded"); @@ -178,6 +165,14 @@ public void Pause() { } } + public void TogglePause() { + if (State == ControlState.Playing) { + State = ControlState.Paused; + } else if (State == ControlState.Paused) { + State = ControlState.Playing; + } + } + public void Stop() { if (this.Console is not null && this.Console.IsCartridgeLoaded()) { this.State = ControlState.Stopped; @@ -189,10 +184,13 @@ public void Stop() { } } this.Console.Reset(); - if (intro is not null) { - this.Screen.Redraw(intro); - } + this.Screen.ShowIntro(); } } + public void Restart() { + Stop(); + Play(); + } + } diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.tscn b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.tscn index bfd9f71..52d5224 100644 --- a/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.tscn +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=39 format=3 uid="uid://lagkciw3arws"] +[gd_scene load_steps=48 format=3 uid="uid://lagkciw3arws"] [ext_resource type="Script" path="res://Prefabs/GodotBoy/GodotBoy.cs" id="1_807qv"] [ext_resource type="Texture2D" uid="uid://dfkwdfgk0dp8n" path="res://icon.svg" id="1_fcyi5"] [ext_resource type="Script" path="res://Prefabs/GodotBoy/Screen.cs" id="2_36eiy"] [ext_resource type="PackedScene" uid="uid://bcvv8417hthjf" path="res://Prefabs/OnscreenControls/Joystick.tscn" id="4_drwt1"] +[ext_resource type="Texture2D" uid="uid://c1grnn67kto6b" path="res://Prefabs/GodotBoy/UI/transparent.png" id="4_yyevs"] [ext_resource type="Texture2D" uid="uid://dubx3mrt06u5p" path="res://Prefabs/GodotBoy/UI/switch_button_a_outline.png" id="5_5ygii"] [ext_resource type="Texture2D" uid="uid://bw2mb47r4ud7" path="res://Prefabs/GodotBoy/UI/switch_button_a.png" id="6_883pa"] [ext_resource type="Texture2D" uid="uid://it7a6pssev4k" path="res://Prefabs/GodotBoy/UI/switch_button_b_outline.png" id="7_o3yvv"] @@ -18,18 +19,26 @@ [ext_resource type="Script" path="res://Prefabs/OnscreenControls/OnscreenControls.cs" id="16_ffynx"] [ext_resource type="Texture2D" uid="uid://trhljjy16es1" path="res://Prefabs/GodotBoy/UI/layout_screen.svg" id="16_s0175"] [ext_resource type="Texture2D" uid="uid://dma6vgdj4ntir" path="res://Prefabs/GodotBoy/UI/layout_screen_control.svg" id="17_arug4"] +[ext_resource type="Texture2D" uid="uid://bqimngayey7nv" path="res://Prefabs/GodotBoy/UI/default_skin_header.svg" id="17_var3s"] +[ext_resource type="Script" path="res://Prefabs/GodotBoy/Options.cs" id="17_yhmc5"] +[ext_resource type="Texture2D" uid="uid://gixtpgxdg6fd" path="res://Prefabs/GodotBoy/UI/default_skin_logo.svg" id="18_bpf3f"] [ext_resource type="Texture2D" uid="uid://5ynu1mc2abvi" path="res://Prefabs/GodotBoy/UI/layout_skin.svg" id="18_sw6ge"] +[ext_resource type="Texture2D" uid="uid://dye1lrylf76q3" path="res://Prefabs/GodotBoy/UI/default_skin_speaker.svg" id="19_2f425"] [ext_resource type="Theme" uid="uid://dcduu766csudy" path="res://Theme.tres" id="20_0wny8"] [ext_resource type="PackedScene" uid="uid://c51yw0q5xqybk" path="res://Prefabs/KeyRebind/keybind.tscn" id="21_1cq1c"] [ext_resource type="PackedScene" uid="uid://7wdw54d0sdlj" path="res://Prefabs/ThemePicker/ThemePicker.tscn" id="21_xltfd"] [ext_resource type="PackedScene" uid="uid://t7khq15qfjuw" path="res://Debuggers/CartInfoDebug.tscn" id="21_y5t5s"] [ext_resource type="PackedScene" uid="uid://b5psa1c0pvkya" path="res://Debuggers/PaletteDebugger.tscn" id="22_e37a5"] +[ext_resource type="Texture2D" uid="uid://dsfu4d554womr" path="res://Prefabs/GodotBoy/UI/controller.svg" id="22_wkh71"] [ext_resource type="PackedScene" uid="uid://yis4daekext1" path="res://Debuggers/Sprite Debugger.tscn" id="23_01ypf"] [ext_resource type="PackedScene" uid="uid://ca3d3pani0rrj" path="res://Debuggers/Tile Debugger.tscn" id="24_cepwr"] [ext_resource type="PackedScene" uid="uid://2uytp7r2ouod" path="res://Debuggers/Map Debugger.tscn" id="25_i7c8m"] [ext_resource type="PackedScene" uid="uid://cs764ed0jafqu" path="res://Debuggers/Disassembler.tscn" id="26_1mdrh"] [ext_resource type="Texture2D" uid="uid://cip0keh15srys" path="res://Prefabs/GodotBoy/UI/cart.png" id="28_f1kbb"] [ext_resource type="Texture2D" uid="uid://byxxvldruom6j" path="res://Prefabs/GodotBoy/UI/play.png" id="29_f4os1"] +[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://Prefabs/GodotBoy/UI/reload.png" id="32_agcku"] +[ext_resource type="Texture2D" uid="uid://cm332so38crqs" path="res://Prefabs/GodotBoy/UI/pause.png" id="32_eqyyb"] +[ext_resource type="Texture2D" uid="uid://d0dpxbrvr2bo7" path="res://Prefabs/GodotBoy/UI/stop.png" id="32_qkn52"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_o72x6"] @@ -58,7 +67,7 @@ unicode = 105 [sub_resource type="Shortcut" id="Shortcut_2ct1q"] events = [SubResource("InputEventKey_mhgt7"), SubResource("InputEventKey_wrprd")] -[node name="GodotBoy" type="Control" node_paths=PackedStringArray("CartControl", "SaveSlot", "OnscreenControls")] +[node name="GodotBoy" type="Control" node_paths=PackedStringArray("CartControl", "PlaybackControl", "SaveSlot", "OnscreenControls")] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -67,6 +76,7 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_807qv") CartControl = NodePath("Loader") +PlaybackControl = NodePath("Playback") SaveSlot = NodePath("Loader/Save Slot") OnscreenControls = [NodePath("Screen Layouts/Onscreen Controls/Control"), NodePath("Screen Layouts/Skinned Controls/Control")] @@ -104,6 +114,22 @@ texture = ExtResource("1_fcyi5") expand_mode = 1 stretch_mode = 5 +[node name="Settings" type="TextureButton" parent="Screen Layouts/Screen Only"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -75.0 +offset_bottom = 75.0 +grow_horizontal = 0 +focus_mode = 0 +texture_normal = ExtResource("4_yyevs") +texture_pressed = ExtResource("10_um660") +texture_hover = ExtResource("9_t6atn") +ignore_texture_size = true +stretch_mode = 5 + [node name="Onscreen Controls" type="Control" parent="Screen Layouts"] visible = false layout_mode = 2 @@ -272,6 +298,43 @@ grow_vertical = 2 texture = ExtResource("15_43iv6") expand_mode = 1 +[node name="Skin Header" type="TextureRect" parent="Screen Layouts/Skinned Controls/Skin"] +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 41.0 +grow_horizontal = 2 +texture = ExtResource("17_var3s") +expand_mode = 1 + +[node name="Skin Logo" type="TextureRect" parent="Screen Layouts/Skinned Controls/Skin/Skin Header"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("18_bpf3f") +expand_mode = 1 +stretch_mode = 5 + +[node name="Skin Speaker" type="TextureRect" parent="Screen Layouts/Skinned Controls/Skin"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -108.0 +offset_top = -95.0 +offset_right = -33.0 +offset_bottom = -24.0 +grow_horizontal = 0 +grow_vertical = 0 +texture = ExtResource("19_2f425") +expand_mode = 1 +stretch_mode = 5 + [node name="Control" type="Control" parent="Screen Layouts/Skinned Controls" node_paths=PackedStringArray("Arrows", "A", "B", "Select", "Start")] layout_mode = 1 anchor_top = 0.501 @@ -282,13 +345,21 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("16_ffynx") Arrows = NodePath("Joystick") -A = NodePath("Buttons/A") -B = NodePath("Buttons/B") +A = NodePath("Buttons/Face/A") +B = NodePath("Buttons/Face/B") Select = NodePath("Buttons/Select") Start = NodePath("Buttons/Start") [node name="Joystick" parent="Screen Layouts/Skinned Controls/Control" instance=ExtResource("4_drwt1")] layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 42.0 +offset_top = -100.0 +offset_right = 242.0 +offset_bottom = 100.0 +grow_vertical = 2 [node name="Buttons" type="Control" parent="Screen Layouts/Skinned Controls/Control"] texture_filter = 1 @@ -300,7 +371,20 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 -[node name="A" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons"] +[node name="Face" type="Control" parent="Screen Layouts/Skinned Controls/Control/Buttons"] +layout_mode = 1 +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -40.0 +offset_top = -20.0 +offset_bottom = 20.0 +grow_horizontal = 0 +grow_vertical = 2 + +[node name="A" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons/Face"] custom_minimum_size = Vector2(75, 75) layout_mode = 1 anchors_preset = 3 @@ -309,9 +393,9 @@ anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 offset_left = -116.0 -offset_top = -189.0 +offset_top = -82.2655 offset_right = -41.0 -offset_bottom = -114.0 +offset_bottom = -7.26547 grow_horizontal = 0 grow_vertical = 0 focus_mode = 0 @@ -320,7 +404,7 @@ texture_pressed = ExtResource("6_883pa") ignore_texture_size = true stretch_mode = 5 -[node name="B" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons"] +[node name="B" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons/Face"] custom_minimum_size = Vector2(75, 75) layout_mode = 1 anchors_preset = 3 @@ -328,10 +412,10 @@ anchor_left = 1.0 anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 -offset_left = -193.0 -offset_top = -106.0 -offset_right = -118.0 -offset_bottom = -31.0 +offset_left = -161.0 +offset_top = -4.26547 +offset_right = -86.0 +offset_bottom = 70.7345 grow_horizontal = 0 grow_vertical = 0 focus_mode = 0 @@ -349,9 +433,9 @@ anchor_top = 1.0 anchor_right = 0.5 anchor_bottom = 1.0 offset_left = -81.5 -offset_top = -99.0 +offset_top = -89.0 offset_right = -6.5 -offset_bottom = -24.0 +offset_bottom = -14.0 grow_horizontal = 2 grow_vertical = 0 focus_mode = 0 @@ -369,9 +453,9 @@ anchor_top = 1.0 anchor_right = 0.5 anchor_bottom = 1.0 offset_left = 3.5 -offset_top = -100.0 +offset_top = -90.0 offset_right = 78.5 -offset_bottom = -25.0 +offset_bottom = -15.0 grow_horizontal = 2 grow_vertical = 0 focus_mode = 0 @@ -383,17 +467,14 @@ stretch_mode = 5 [node name="Settings" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons"] custom_minimum_size = Vector2(75, 75) layout_mode = 1 -anchors_preset = 8 +anchors_preset = 5 anchor_left = 0.5 -anchor_top = 0.5 anchor_right = 0.5 -anchor_bottom = 0.5 offset_left = -37.5 -offset_top = -37.5 +offset_top = 47.0 offset_right = 37.5 -offset_bottom = 37.5 +offset_bottom = 122.0 grow_horizontal = 2 -grow_vertical = 2 focus_mode = 0 texture_normal = ExtResource("9_t6atn") texture_pressed = ExtResource("10_um660") @@ -419,13 +500,17 @@ focus_mode = 0 mouse_filter = 2 shortcut = SubResource("Shortcut_54dsp") -[node name="Options" type="Window" parent="."] +[node name="Options" type="Window" parent="." node_paths=PackedStringArray("DisplayColourPicker", "SkinColourPicker", "KeyBindings")] title = "Options" initial_position = 2 size = Vector2i(640, 480) visible = false exclusive = true popup_window = true +script = ExtResource("17_yhmc5") +DisplayColourPicker = NodePath("MarginContainer/ScrollContainer/ScrollContents/Colours/Themes/OptionButton") +SkinColourPicker = NodePath("MarginContainer/ScrollContainer/ScrollContents/Colours/ColorRect") +KeyBindings = [NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind"), NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind2"), NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind3"), NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind4"), NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind5"), NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind6"), NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind7"), NodePath("MarginContainer/ScrollContainer/ScrollContents/Keybinds/Keybind8")] [node name="MarginContainer" type="MarginContainer" parent="Options"] anchors_preset = 15 @@ -443,14 +528,16 @@ size_flags_vertical = 3 [node name="ScrollContents" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer"] layout_mode = 2 +size_flags_horizontal = 3 theme_override_constants/separation = 24 -[node name="Display" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] layout_mode = 2 +text = "Layout" +horizontal_alignment = 1 -[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Display"] +[node name="Display" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] layout_mode = 2 -text = "Layout" [node name="GridContainer" type="GridContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Display"] layout_mode = 2 @@ -471,6 +558,14 @@ layout_mode = 2 tooltip_text = "Faux device with onscreen controls" texture_normal = ExtResource("18_sw6ge") +[node name="HSeparator" type="HSeparator" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 + +[node name="Label2" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 +text = "Theme" +horizontal_alignment = 1 + [node name="Colours" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] layout_mode = 2 @@ -497,6 +592,14 @@ layout_mode = 2 color = Color(0.803922, 0.392157, 0.129412, 1) edit_alpha = false +[node name="HSeparator2" type="HSeparator" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 + +[node name="Label3" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 +text = "Controls" +horizontal_alignment = 1 + [node name="Keybinds" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] layout_mode = 2 @@ -510,7 +613,7 @@ layout_mode = 2 [node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Keybinds/Heading"] custom_minimum_size = Vector2(128, 0) layout_mode = 2 -text = "Key" +text = "Action Name" vertical_alignment = 1 text_overrun_behavior = 3 @@ -556,12 +659,29 @@ ActionName = "button_start" layout_mode = 2 ActionName = "button_select" -[node name="Developer" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +[node name="Controller" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Controller"] +layout_mode = 2 +text = "Controller" + +[node name="TextureRect" type="TextureRect" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Controller"] +layout_mode = 2 +texture = ExtResource("22_wkh71") +expand_mode = 5 +stretch_mode = 5 + +[node name="HSeparator3" type="HSeparator" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] layout_mode = 2 -[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Developer"] +[node name="Label4" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] layout_mode = 2 text = "Developer" +horizontal_alignment = 1 + +[node name="Developer" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 [node name="RichTextLabel" type="RichTextLabel" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Developer"] layout_mode = 2 @@ -648,6 +768,7 @@ grow_horizontal = 2 [node name="Open ROM" type="Button" parent="Loader"] custom_minimum_size = Vector2(48, 48) layout_mode = 2 +tooltip_text = "Load Game Cart" focus_mode = 0 icon = ExtResource("28_f1kbb") expand_icon = true @@ -664,8 +785,9 @@ use_native_dialog = true [node name="Save Slot" type="OptionButton" parent="Loader"] layout_mode = 2 +tooltip_text = "Select Save Slot" focus_mode = 0 -item_count = 5 +item_count = 11 selected = 0 popup/item_0/text = "No Save" popup/item_0/id = 0 @@ -677,14 +799,63 @@ popup/item_3/text = "Save Slot 3" popup/item_3/id = 3 popup/item_4/text = "Save Slot 4" popup/item_4/id = 4 +popup/item_5/text = "Save Slot 5" +popup/item_5/id = 5 +popup/item_6/text = "Save Slot 6" +popup/item_6/id = 6 +popup/item_7/text = "Save Slot 7" +popup/item_7/id = 7 +popup/item_8/text = "Save Slot 8" +popup/item_8/id = 8 +popup/item_9/text = "Save Slot 9" +popup/item_9/id = 9 +popup/item_10/text = "Save Slot 10" +popup/item_10/id = 10 [node name="Play" type="Button" parent="Loader"] custom_minimum_size = Vector2(48, 48) layout_mode = 2 +tooltip_text = "Start Playback" focus_mode = 0 icon = ExtResource("29_f4os1") expand_icon = true +[node name="Playback" type="HBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -24.0 +offset_top = 12.0 +offset_right = 24.0 +offset_bottom = 60.0 +grow_horizontal = 2 + +[node name="Restart" type="Button" parent="Playback"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +tooltip_text = "Restart Playback" +focus_mode = 0 +icon = ExtResource("32_agcku") +expand_icon = true + +[node name="Pause" type="Button" parent="Playback"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +tooltip_text = "Pause/Resume Playback" +focus_mode = 0 +icon = ExtResource("32_eqyyb") +expand_icon = true + +[node name="Stop" type="Button" parent="Playback"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +tooltip_text = "Stop Playback" +focus_mode = 0 +icon = ExtResource("32_qkn52") +expand_icon = true + +[connection signal="pressed" from="Screen Layouts/Screen Only/Settings" to="Options" method="show"] [connection signal="pressed" from="Screen Layouts/Onscreen Controls/Control/Buttons/Settings" to="Options" method="show"] [connection signal="pressed" from="Screen Layouts/Skinned Controls/Control/Buttons/Settings" to="Options" method="show"] [connection signal="pressed" from="Options Shortcut" to="Options" method="show"] @@ -698,3 +869,6 @@ expand_icon = true [connection signal="pressed" from="Loader/Open ROM" to="Loader/Open ROM/FileDialog" method="show"] [connection signal="file_selected" from="Loader/Open ROM/FileDialog" to="." method="LoadCartFromPath"] [connection signal="pressed" from="Loader/Play" to="." method="Play"] +[connection signal="pressed" from="Playback/Restart" to="." method="Restart"] +[connection signal="pressed" from="Playback/Pause" to="." method="TogglePause"] +[connection signal="pressed" from="Playback/Stop" to="." method="Stop"] diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/Options.cs b/Gameboy.Player.Godot/Prefabs/GodotBoy/Options.cs new file mode 100644 index 0000000..42c97c9 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/Options.cs @@ -0,0 +1,73 @@ +using Godot; +using System; +using System.Linq; + +public partial class Options : Window { + + public override void _Ready() { + LoadConfig(); + this.CloseRequested += SaveConfig; + } + + + [Export] public OptionButton DisplayColourPicker; + [Export] public ColorPickerButton SkinColourPicker; + [Export] public Keybind[] KeyBindings; // Relies on stuff I don't have on this PC Need to add a way to examine the primary key & secondary key + + private static readonly string FilePath = "user://user_config.cfg"; + + private void SaveConfig() { + // Create config + var config = new ConfigFile(); + + // Store values + config.SetValue("theme", "display_colour", DisplayColourPicker.Selected); + config.SetValue("theme", "skin_colour", SkinColourPicker.Color); + + foreach (var rebind in KeyBindings) { + config.SetValue("keybind", rebind.ActionName, string.Join(',', rebind.GetPrimaryKey().ToString(), rebind.GetSecondaryKey().ToString())); // Add these functions + } + + // Save to a file + config.Save(FilePath); + } + + private void LoadConfig() { + // Create config + var config = new ConfigFile(); + + Error err = config.Load(FilePath); + if (err != Error.Ok) { + return; + } + + set(config, "theme", "display_colour", (v) => {DisplayColourPicker.Selected = v.AsInt32(); DisplayColourPicker.EmitSignal("item_selected", v.AsInt32());}); + set(config, "theme", "skin_colour", (v) => {SkinColourPicker.Color = v.AsColor(); SkinColourPicker.EmitSignal("color_changed", v.AsColor());}); + + foreach (var rebind in KeyBindings) { + set(config, "keybind", rebind.ActionName, (v) => { + if (v.VariantType != Variant.Type.String) + return; + + var str = v.AsString().Split(',', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + Enum.TryParse(str.FirstOrDefault() ?? string.Empty, ignoreCase: true, out Key primary); + Enum.TryParse(str.Skip(1).FirstOrDefault() ?? string.Empty, ignoreCase: true, out Key secondary); + rebind.SetKeys(primary, secondary); // Add this function + }); + } + } + + private static void set(ConfigFile config, string section, string key, Action setter) { + try { + if (!config.HasSectionKey(section, key)) { + return; + } + + Variant v = config.GetValue(section, key); + setter(v); + return; + } catch { + return; + } + } +} \ No newline at end of file diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/Screen.cs b/Gameboy.Player.Godot/Prefabs/GodotBoy/Screen.cs index 5ca183d..f9b0ba7 100644 --- a/Gameboy.Player.Godot/Prefabs/GodotBoy/Screen.cs +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/Screen.cs @@ -1,5 +1,6 @@ using Godot; using System; +using System.Linq; using Qkmaxware.Emulators.Gameboy; using Qkmaxware.Emulators.Gameboy.Hardware; using LcdBitmap = Qkmaxware.Emulators.Gameboy.Hardware.Bitmap; @@ -38,6 +39,10 @@ public partial class Screen : TabContainer { [Export] public Color Obj1Black = new Color(0, 0, 0); + private LcdBitmap intro; + private LcdBitmap blank; + + private Image image; private ImageTexture texture; private TextureRect[] screens; @@ -48,10 +53,41 @@ public override void _Ready() { for (int i = 0; i < tabs; i++) { screens[i] = GetTabControl(i).GetNode("Screen"); } + + var intro = new LcdBitmap(Gpu.LCD_WIDTH, Gpu.LCD_HEIGHT); + this.intro = intro; + intro.Fill(ColourPallet.BackgroundDark); + + blank = new LcdBitmap(Gpu.LCD_WIDTH, Gpu.LCD_HEIGHT); + blank.Fill(ColourPallet.BackgroundDark); + + var text = new LcdBitmap[]{ LcdBitmap.StampB, LcdBitmap.StampL, LcdBitmap.StampA, LcdBitmap.StampZ, LcdBitmap.StampO, LcdBitmap.StampR, LcdBitmap.StampB, LcdBitmap.StampO, LcdBitmap.StampY }.Select(stamp => stamp.Invert().Enlarge(4)).ToArray(); + var width = text.Select(stamp => stamp.Width + 1).Sum(); + var height = text.Select(stamp => stamp.Height).Max(); + + var startX = (intro.Width / 2) - (width / 2); + var startY = (intro.Height / 2) - (height / 2); + foreach (var stamp in text) { + intro.Stamp(startX, startY, stamp); + startX += stamp.Width + 1; + } + + this.Redraw(this.intro, assignToAllScreens: true); } - public void Redraw(LcdBitmap bmp) { - var pixels = Image.Create(width: bmp.Width, height: bmp.Height, useMipmaps: false, format: Image.Format.Rgb8); + public void ShowIntro() { + this.Redraw(intro); + } + + public void Blank() { + this.Redraw(blank); + } + + public void Redraw(LcdBitmap bmp, bool assignToAllScreens = false) { + if (this.image is null) { + this.image = Image.Create(width: bmp.Width, height: bmp.Height, useMipmaps: false, format: Image.Format.Rgb8); + } + var pixels = this.image; for (var col = 0; col < bmp.Height; col++) { for (var row = 0; row < bmp.Width; row++) { @@ -82,8 +118,17 @@ public void Redraw(LcdBitmap bmp) { } else { texture.Update(pixels); } - var screen = screens[CurrentTab]; - if (screen is not null) - screen.Texture = texture; + if (assignToAllScreens) { + foreach (var screen in this.screens) { + if (screen is null) + continue; + screen.Texture = texture; + } + } else { + var screen = screens[CurrentTab]; + if (screen is not null) + screen.Texture = texture; + } + } } diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/controller.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/controller.svg new file mode 100644 index 0000000..35fe971 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/controller.svg @@ -0,0 +1,278 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/controller.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/controller.svg.import new file mode 100644 index 0000000..a0cc0f0 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/controller.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dsfu4d554womr" +path="res://.godot/imported/controller.svg-cfd9986cf9d47c4cfb7d3d4384652b34.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/controller.svg" +dest_files=["res://.godot/imported/controller.svg-cfd9986cf9d47c4cfb7d3d4384652b34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg index c8b8ca0..f7a3fa9 100644 --- a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg @@ -34,9 +34,9 @@ inkscape:window-x="-8" inkscape:window-height="987" inkscape:window-width="1680" - inkscape:cy="61.599052" - inkscape:cx="32.504297" - inkscape:zoom="2.1997092" + inkscape:cy="42.081895" + inkscape:cx="74.369255" + inkscape:zoom="1.5554293" showgrid="false" inkscape:document-units="mm" inkscape:pagecheckerboard="0" @@ -75,12 +75,6 @@ id="path3362" d="M 0.23849925,0.23850226 V 48.358225 H 48.358223 v -3.60843 H 9.2850302 c -2.965197,-0.13959 -4.440364,-1.709607 -4.570679,-4.570682 V 0.48636726 Z" style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - - - - - - - - - - - diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_header.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_header.svg new file mode 100644 index 0000000..cecad00 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_header.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_header.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_header.svg.import new file mode 100644 index 0000000..ff14e62 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_header.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bqimngayey7nv" +path="res://.godot/imported/default_skin_header.svg-33d9ac8a92f9a85b42f01a8bb6299cc6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/default_skin_header.svg" +dest_files=["res://.godot/imported/default_skin_header.svg-33d9ac8a92f9a85b42f01a8bb6299cc6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=4.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_logo.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_logo.svg new file mode 100644 index 0000000..9c3e397 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_logo.svg @@ -0,0 +1,69 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_logo.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_logo.svg.import new file mode 100644 index 0000000..38e9013 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_logo.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gixtpgxdg6fd" +path="res://.godot/imported/default_skin_logo.svg-8405ffa3016f6e9b66906cbfc1633f67.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/default_skin_logo.svg" +dest_files=["res://.godot/imported/default_skin_logo.svg-8405ffa3016f6e9b66906cbfc1633f67.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=4.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_speaker.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_speaker.svg new file mode 100644 index 0000000..8fc499f --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_speaker.svg @@ -0,0 +1,111 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_speaker.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_speaker.svg.import new file mode 100644 index 0000000..538ac34 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin_speaker.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dye1lrylf76q3" +path="res://.godot/imported/default_skin_speaker.svg-6c6c71df501cbc1548f345ecfbaa3fac.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/default_skin_speaker.svg" +dest_files=["res://.godot/imported/default_skin_speaker.svg-6c6c71df501cbc1548f345ecfbaa3fac.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=4.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg index e0ed44e..19745ab 100644 --- a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg @@ -1,51 +1,73 @@ - - + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + sodipodi:docname="layout_screen.svg" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + id="svg5" + version="1.1" + viewBox="0 0 32 32" + height="32mm" + width="32mm"> + + + + image/svg+xml + + + + + + inkscape:window-y="-8" + inkscape:window-x="-8" + inkscape:window-height="987" + inkscape:window-width="1680" + inkscape:cy="60.671153" + inkscape:cx="70.419695" + inkscape:zoom="4.3994184" + width="32mm" + showgrid="false" + inkscape:document-units="mm" + inkscape:pagecheckerboard="true" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#595959" + id="namedview7" /> + inkscape:label="Layer 1"> + x="5.0880098" + height="21.82398" + width="21.82398" + id="rect846" + style="fill:#ffffff;stroke:#ffffff;stroke-width:0.383688;stroke-linecap:round;stroke-linejoin:round" /> + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png index ca3b98e..13cbea3 100644 Binary files a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/transparent.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/transparent.png new file mode 100644 index 0000000..6b7a50e Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/transparent.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/transparent.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/transparent.png.import new file mode 100644 index 0000000..ab4dc67 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/transparent.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c1grnn67kto6b" +path="res://.godot/imported/transparent.png-1b23ce7cf73055690fc2caba064e03ed.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/transparent.png" +dest_files=["res://.godot/imported/transparent.png-1b23ce7cf73055690fc2caba064e03ed.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Gameboy.Player.Godot/Prefabs/KeyRebind/Keybind.cs b/Gameboy.Player.Godot/Prefabs/KeyRebind/Keybind.cs index bc4b418..1076506 100644 --- a/Gameboy.Player.Godot/Prefabs/KeyRebind/Keybind.cs +++ b/Gameboy.Player.Godot/Prefabs/KeyRebind/Keybind.cs @@ -27,6 +27,9 @@ public override void _Ready() { private InputEventKey key_primary; private InputEventKey key_secondary; + public Key GetPrimaryKey() => EventKey(key_primary); + public Key GetSecondaryKey() => EventKey(key_secondary); + public void MakeAction(string name) { InputMap.AddAction(name); this.action = name; @@ -67,6 +70,28 @@ private static string EventString(InputEventKey key) { return "(Unset)"; } + private static Key EventKey(InputEventKey key) { + if (key is null) + return Key.None; + if (key.Keycode != Key.None) + return key.Keycode; + if (key.PhysicalKeycode != Key.None) + return key.PhysicalKeycode; + return Key.None; + } + + public void SetKeys(Key primary, Key secondary) { + if (this.key_primary is not null) { + this.key_primary.Keycode = primary; + this.key_primary.PhysicalKeycode = primary; + } + + if (this.key_secondary is not null) { + this.key_secondary.Keycode = secondary; + this.key_secondary.PhysicalKeycode = secondary; + } + } + public override void _Input(InputEvent @event) { base._Input(@event);