diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4c58105..124de03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: fail-fast: false matrix: godot: [ '4.2.2-stable' ] - os: [ 'ubuntu-latest' ] + os: [ 'windows-latest' ] platform: [ 'Windows x64' ] steps: - name: Checkout Code @@ -43,11 +43,13 @@ jobs: with: dotnet-version: '8.0.x' # SDK Version to use - name: Download Godot - run: curl -LO https://github.com/godotengine/godot-builds/releases/download/${{ matrix.godot }}/Godot_v${{ matrix.godot }}_mono_linux_x86_64.zip -o godot.zip + run: curl https://github.com/godotengine/godot-builds/releases/download/${{ matrix.godot }}/Godot_v${{ matrix.godot }}_mono_win64.zip -o godot.zip; curl https://github.com/godotengine/godot-builds/releases/download/${{ matrix.godot }}/Godot_v${{ matrix.godot }}_mono_export_templates.tpz -o godot_export_templates.tpz - name: Extract Godot - run: mkdir Godot && unzip godot.zip -d Godot && mv Godot/Godot_v${{ matrix.godot }}_mono_linux_x86_64 Godot/Engine && mv Godot/Engine/Godot_v${{ matrix.godot }}_mono_linux.x86_64 Godot/Engine/godot + run: Expand-Archive -Path godot.zip -DestinationPath Godot; Rename-Item Godot/Godot_v${{ matrix.godot }}_mono_win64 -NewName Engine; Rename-Item Godot/Engine/Godot_v${{ matrix.godot }}_mono_win64.exe -NewName "godot.exe"; Rename-Item Godot/Engine/Godot_v${{ matrix.godot }}_mono_win64_console.exe -NewName "godot_console.exe" + - name: Extract Build Templates + run: echo 1 - name: Build Binary - run: mkdir -p ./bin/${{ matrix.platform }} && ./Godot/Engine/godot --path ./Gameboy.Player.Godot/project.godot --export ${{ matrix.platform }} ./bin/${{ matrix.platform }} + run: New-Item -Force -Path "./bin/${{ matrix.platform }}" -ItemType "directory"; ./Godot/Engine/godot_console.exe --build-solutions --export-release ${{ matrix.platform }} ../bin/${{ matrix.platform }}/BlazorBoy.exe ./Gameboy.Player.Godot/project.godot cli: needs: [ test ] diff --git a/.gitignore b/.gitignore index 9b6b744..e394ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ obj/ nupkg/ [pP]rivate/ TestResults/ -.site/ \ No newline at end of file +.site/ + +/*.zip +/[gG]odot/ \ No newline at end of file diff --git a/Gameboy.Player.Godot/Debuggers/CartInfoDebug.cs b/Gameboy.Player.Godot/Debuggers/CartInfoDebug.cs index e30f9f3..064d525 100644 --- a/Gameboy.Player.Godot/Debuggers/CartInfoDebug.cs +++ b/Gameboy.Player.Godot/Debuggers/CartInfoDebug.cs @@ -7,7 +7,7 @@ namespace Qkmaxware.Emulators.Gameboy.Player; public partial class CartInfoDebug : Control { - [Export] public TextureRenderer Player; + [Export] public GodotBoy Player; private RichTextLabel text; diff --git a/Gameboy.Player.Godot/Debuggers/CartInfoDebug.tscn b/Gameboy.Player.Godot/Debuggers/CartInfoDebug.tscn index fb3d42c..7bd1b7b 100644 --- a/Gameboy.Player.Godot/Debuggers/CartInfoDebug.tscn +++ b/Gameboy.Player.Godot/Debuggers/CartInfoDebug.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://t7khq15qfjuw"] -[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://reload.png" id="1_0a8i2"] +[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://Prefabs/GodotBoy/UI/reload.png" id="1_0a8i2"] [ext_resource type="Script" path="res://Debuggers/CartInfoDebug.cs" id="1_m4mxx"] [node name="Cartridge Info" type="Control"] diff --git a/Gameboy.Player.Godot/Debuggers/Disassembler.cs b/Gameboy.Player.Godot/Debuggers/Disassembler.cs index a4d68ed..2ab366b 100644 --- a/Gameboy.Player.Godot/Debuggers/Disassembler.cs +++ b/Gameboy.Player.Godot/Debuggers/Disassembler.cs @@ -8,7 +8,7 @@ namespace Qkmaxware.Emulators.Gameboy.Player; public partial class Disassembler : Control { - [Export] public TextureRenderer Player; + [Export] public GodotBoy Player; [Export] public PackedScene RowPrefab; diff --git a/Gameboy.Player.Godot/Debuggers/Disassembler.tscn b/Gameboy.Player.Godot/Debuggers/Disassembler.tscn index 753163f..4d00c7d 100644 --- a/Gameboy.Player.Godot/Debuggers/Disassembler.tscn +++ b/Gameboy.Player.Godot/Debuggers/Disassembler.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://Debuggers/Disassembler.cs" id="1_bgd2f"] [ext_resource type="PackedScene" uid="uid://c2fsn6ee6p522" path="res://Debuggers/Disassembler.Row.tscn" id="2_hoclf"] -[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://reload.png" id="3_5ffph"] +[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://Prefabs/GodotBoy/UI/reload.png" id="3_5ffph"] [node name="Disassembler" type="Control"] layout_mode = 3 diff --git a/Gameboy.Player.Godot/Debuggers/IDebugable.cs b/Gameboy.Player.Godot/Debuggers/IDebugable.cs new file mode 100644 index 0000000..11bda16 --- /dev/null +++ b/Gameboy.Player.Godot/Debuggers/IDebugable.cs @@ -0,0 +1,5 @@ +using Qkmaxware.Emulators.Gameboy; + +public interface IDebugable { + public Gameboy Console {get;} +} \ No newline at end of file diff --git a/Gameboy.Player.Godot/Debuggers/Map Debugger.tscn b/Gameboy.Player.Godot/Debuggers/Map Debugger.tscn index caf907a..b860148 100644 --- a/Gameboy.Player.Godot/Debuggers/Map Debugger.tscn +++ b/Gameboy.Player.Godot/Debuggers/Map Debugger.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://Debuggers/MapDebugger.cs" id="1_gnyfy"] [ext_resource type="Script" path="res://BitmapTextureRect.cs" id="2_0ayh7"] -[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://reload.png" id="3_fd1ro"] +[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://Prefabs/GodotBoy/UI/reload.png" id="3_fd1ro"] [node name="Map Debugger" type="Control"] layout_mode = 3 diff --git a/Gameboy.Player.Godot/Debuggers/MapDebugger.cs b/Gameboy.Player.Godot/Debuggers/MapDebugger.cs index b6ba913..ec7a95b 100644 --- a/Gameboy.Player.Godot/Debuggers/MapDebugger.cs +++ b/Gameboy.Player.Godot/Debuggers/MapDebugger.cs @@ -10,7 +10,7 @@ public enum MapType { public partial class MapDebugger : Control { - [Export] public TextureRenderer Player; + [Export] public GodotBoy Player; [Export] public MapType Map; private BitmapTextureRect rect; diff --git a/Gameboy.Player.Godot/Debuggers/PaletteDebugger.cs b/Gameboy.Player.Godot/Debuggers/PaletteDebugger.cs index e8eda37..0df1928 100644 --- a/Gameboy.Player.Godot/Debuggers/PaletteDebugger.cs +++ b/Gameboy.Player.Godot/Debuggers/PaletteDebugger.cs @@ -8,7 +8,7 @@ namespace Qkmaxware.Emulators.Gameboy.Player; public partial class PaletteDebugger : Control { - [Export] public TextureRenderer Player; + [Export] public GodotBoy Player; private ColorPickerButton bg0Black; private ColorPickerButton bg0DarkGrey; @@ -30,31 +30,31 @@ public partial class PaletteDebugger : Control { public override void _Ready() { // Colours bg0Black = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Bg/HBoxContainer/BgBlack"); - bg0Black.Color = Player?.BgBlack ?? new Color(0,0,0,1); + bg0Black.Color = Player?.Screen?.BgBlack ?? new Color(0,0,0,1); bg0DarkGrey = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Bg/HBoxContainer/BgDarkGrey"); - bg0DarkGrey.Color = Player?.BgDarkGrey ?? new Color(0.3f,0.3f,0.3f,1); + bg0DarkGrey.Color = Player?.Screen?.BgDarkGrey ?? new Color(0.3f,0.3f,0.3f,1); bg0LightGrey = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Bg/HBoxContainer/BgLightGrey"); - bg0LightGrey.Color = Player?.BgLightGrey ?? new Color(0.6f,0.6f,0.6f,1); + bg0LightGrey.Color = Player?.Screen?.BgLightGrey ?? new Color(0.6f,0.6f,0.6f,1); bg0White = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Bg/HBoxContainer/BgWhite"); - bg0White.Color = Player?.BgWhite ?? new Color(1,1,1,1); + bg0White.Color = Player?.Screen?.BgWhite ?? new Color(1,1,1,1); obj0Black = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer/Obj0Black"); - obj0Black.Color = Player?.Obj0Black ?? new Color(0,0,0,1); + obj0Black.Color = Player?.Screen?.Obj0Black ?? new Color(0,0,0,1); obj0DarkGrey = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer/Obj0DarkGrey"); - obj0DarkGrey.Color = Player?.Obj0DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); + obj0DarkGrey.Color = Player?.Screen?.Obj0DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); obj0LightGrey = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer/Obj0LightGrey"); - obj0LightGrey.Color = Player?.Obj0LightGrey ?? new Color(0.6f,0.6f,0.6f,1); + obj0LightGrey.Color = Player?.Screen?.Obj0LightGrey ?? new Color(0.6f,0.6f,0.6f,1); obj0White = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer/Obj0White"); - obj0White.Color = Player?.Obj0White ?? new Color(1,1,1,1); + obj0White.Color = Player?.Screen?.Obj0White ?? new Color(1,1,1,1); obj1Black = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer2/Obj1Black"); - obj1Black.Color = Player?.Obj1Black ?? new Color(0,0,0,1); + obj1Black.Color = Player?.Screen?.Obj1Black ?? new Color(0,0,0,1); obj1DarkGrey = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer2/Obj1DarkGrey"); - obj1DarkGrey.Color = Player?.Obj1DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); + obj1DarkGrey.Color = Player?.Screen?.Obj1DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); obj1LightGrey = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer2/Obj1LightGrey"); - obj1LightGrey.Color = Player?.Obj1LightGrey ?? new Color(0.6f,0.6f,0.6f,1); + obj1LightGrey.Color = Player?.Screen?.Obj1LightGrey ?? new Color(0.6f,0.6f,0.6f,1); obj1White = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Objects/HBoxContainer2/Obj1White"); - obj1White.Color = Player?.Obj1White ?? new Color(1,1,1,1); + obj1White.Color = Player?.Screen?.Obj1White ?? new Color(1,1,1,1); var themePicker = GetNode("ScrollContainer/VSplitContainer/Colours/VBoxContainer/HBoxContainer/Themes/OptionButton"); foreach (var theme in LcdColorTheme.Named) { @@ -80,20 +80,20 @@ public override void _Ready() { } public void Refresh() { - bg0Black.Color = Player?.BgBlack ?? new Color(0,0,0,1); - bg0DarkGrey.Color = Player?.BgDarkGrey ?? new Color(0.3f,0.3f,0.3f,1); - bg0LightGrey.Color = Player?.BgLightGrey ?? new Color(0.6f,0.6f,0.6f,1); - bg0White.Color = Player?.BgWhite ?? new Color(1,1,1,1); + bg0Black.Color = Player?.Screen?.BgBlack ?? new Color(0,0,0,1); + bg0DarkGrey.Color = Player?.Screen?.BgDarkGrey ?? new Color(0.3f,0.3f,0.3f,1); + bg0LightGrey.Color = Player?.Screen?.BgLightGrey ?? new Color(0.6f,0.6f,0.6f,1); + bg0White.Color = Player?.Screen?.BgWhite ?? new Color(1,1,1,1); - obj0Black.Color = Player?.Obj0Black ?? new Color(0,0,0,1); - obj0DarkGrey.Color = Player?.Obj0DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); - obj0LightGrey.Color = Player?.Obj0LightGrey ?? new Color(0.6f,0.6f,0.6f,1); - obj0White.Color = Player?.Obj0White ?? new Color(1,1,1,1); + obj0Black.Color = Player?.Screen?.Obj0Black ?? new Color(0,0,0,1); + obj0DarkGrey.Color = Player?.Screen?.Obj0DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); + obj0LightGrey.Color = Player?.Screen?.Obj0LightGrey ?? new Color(0.6f,0.6f,0.6f,1); + obj0White.Color = Player?.Screen?.Obj0White ?? new Color(1,1,1,1); - obj1Black.Color = Player?.Obj1Black ?? new Color(0,0,0,1); - obj1DarkGrey.Color = Player?.Obj1DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); - obj1LightGrey.Color = Player?.Obj1LightGrey ?? new Color(0.6f,0.6f,0.6f,1); - obj1White.Color = Player?.Obj1White ?? new Color(1,1,1,1); + obj1Black.Color = Player?.Screen?.Obj1Black ?? new Color(0,0,0,1); + obj1DarkGrey.Color = Player?.Screen?.Obj1DarkGrey ?? new Color(0.3f,0.3f,0.3f,1); + obj1LightGrey.Color = Player?.Screen?.Obj1LightGrey ?? new Color(0.6f,0.6f,0.6f,1); + obj1White.Color = Player?.Screen?.Obj1White ?? new Color(1,1,1,1); var ppu = Player?.Console?.GPU; if (ppu is IDebuggablePpu debug) { @@ -102,23 +102,23 @@ public void Refresh() { foreach (var pair in palette.First.Zip(palette.Second)) { var palettedColour = pair.First; var rgb = palettedColour switch { - ColourPallet.BackgroundDark => Player.BgBlack, - ColourPallet.Object0Dark => Player.BgBlack, - ColourPallet.Object1Dark => Player.BgBlack, + ColourPallet.BackgroundDark => Player.Screen.BgBlack, + ColourPallet.Object0Dark => Player.Screen.BgBlack, + ColourPallet.Object1Dark => Player.Screen.BgBlack, - ColourPallet.BackgroundMedium => Player.BgDarkGrey, - ColourPallet.Object0Medium => Player.BgDarkGrey, - ColourPallet.Object1Medium => Player.BgDarkGrey, + ColourPallet.BackgroundMedium => Player.Screen.BgDarkGrey, + ColourPallet.Object0Medium => Player.Screen.BgDarkGrey, + ColourPallet.Object1Medium => Player.Screen.BgDarkGrey, - ColourPallet.BackgroundLight => Player.BgLightGrey, - ColourPallet.Object0Light => Player.BgLightGrey, - ColourPallet.Object1Light => Player.BgLightGrey, + ColourPallet.BackgroundLight => Player.Screen.BgLightGrey, + ColourPallet.Object0Light => Player.Screen.BgLightGrey, + ColourPallet.Object1Light => Player.Screen.BgLightGrey, - ColourPallet.BackgroundWhite => Player.BgWhite, - ColourPallet.Object0White => Player.BgWhite, - ColourPallet.Object1White => Player.BgWhite, + ColourPallet.BackgroundWhite => Player.Screen.BgWhite, + ColourPallet.Object0White => Player.Screen.BgWhite, + ColourPallet.Object1White => Player.Screen.BgWhite, - _ => Player.BgBlack, + _ => Player.Screen.BgBlack, }; bgcolours.Add(palettedColour); pair.Second.Color = rgb; @@ -131,10 +131,10 @@ public void Refresh() { HashSet objcolours = new HashSet(); var colourSetIndex = 0; foreach (var palette in debug.ObjectPalettes.Zip(obj)) { - var black = colourSetIndex == 0 ? Player.Obj0Black : Player.Obj1Black; - var dg = colourSetIndex == 0 ? Player.Obj0DarkGrey : Player.Obj1DarkGrey; - var lg = colourSetIndex == 0 ? Player.Obj0LightGrey : Player.Obj1LightGrey; - var white = colourSetIndex == 0 ? Player.Obj0White : Player.Obj1White; + var black = colourSetIndex == 0 ? Player.Screen.Obj0Black : Player.Screen.Obj1Black; + var dg = colourSetIndex == 0 ? Player.Screen.Obj0DarkGrey : Player.Screen.Obj1DarkGrey; + var lg = colourSetIndex == 0 ? Player.Screen.Obj0LightGrey : Player.Screen.Obj1LightGrey; + var white = colourSetIndex == 0 ? Player.Screen.Obj0White : Player.Screen.Obj1White; foreach (var pair in palette.First.Zip(palette.Second)) { var palettedColour = pair.First; @@ -192,20 +192,20 @@ public void SelectTheme(int index) { public void UpdatePlayerColours() { if (Player is not null) { - Player.BgBlack = bg0Black.Color; - Player.BgDarkGrey = bg0DarkGrey.Color; - Player.BgLightGrey = bg0LightGrey.Color; - Player.BgWhite = bg0White.Color; + Player.Screen.BgBlack = bg0Black.Color; + Player.Screen.BgDarkGrey = bg0DarkGrey.Color; + Player.Screen.BgLightGrey = bg0LightGrey.Color; + Player.Screen.BgWhite = bg0White.Color; - Player.Obj0Black = obj0Black.Color; - Player.Obj0DarkGrey = obj0DarkGrey.Color; - Player.Obj0LightGrey = obj0LightGrey.Color; - Player.Obj0White = obj0White.Color; + Player.Screen.Obj0Black = obj0Black.Color; + Player.Screen.Obj0DarkGrey = obj0DarkGrey.Color; + Player.Screen.Obj0LightGrey = obj0LightGrey.Color; + Player.Screen.Obj0White = obj0White.Color; - Player.Obj1Black = obj1Black.Color; - Player.Obj1DarkGrey = obj1DarkGrey.Color; - Player.Obj1LightGrey = obj1LightGrey.Color; - Player.Obj1White = obj1White.Color; + Player.Screen.Obj1Black = obj1Black.Color; + Player.Screen.Obj1DarkGrey = obj1DarkGrey.Color; + Player.Screen.Obj1LightGrey = obj1LightGrey.Color; + Player.Screen.Obj1White = obj1White.Color; } } } diff --git a/Gameboy.Player.Godot/Debuggers/PaletteDebugger.tscn b/Gameboy.Player.Godot/Debuggers/PaletteDebugger.tscn index c93f73f..7664698 100644 --- a/Gameboy.Player.Godot/Debuggers/PaletteDebugger.tscn +++ b/Gameboy.Player.Godot/Debuggers/PaletteDebugger.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://b5psa1c0pvkya"] [ext_resource type="Script" path="res://Debuggers/PaletteDebugger.cs" id="1_sor31"] -[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://reload.png" id="2_wsgsr"] +[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://Prefabs/GodotBoy/UI/reload.png" id="2_wsgsr"] [node name="Palette Debugger" type="Control"] layout_mode = 3 diff --git a/Gameboy.Player.Godot/Debuggers/Sprite Debugger.tscn b/Gameboy.Player.Godot/Debuggers/Sprite Debugger.tscn index 94d6b0f..a0c78d1 100644 --- a/Gameboy.Player.Godot/Debuggers/Sprite Debugger.tscn +++ b/Gameboy.Player.Godot/Debuggers/Sprite Debugger.tscn @@ -1,9 +1,9 @@ [gd_scene load_steps=5 format=3 uid="uid://yis4daekext1"] -[ext_resource type="Texture2D" uid="uid://cip0keh15srys" path="res://cart.png" id="1_44h0a"] +[ext_resource type="Texture2D" uid="uid://cip0keh15srys" path="res://Prefabs/GodotBoy/UI/cart.png" id="1_44h0a"] [ext_resource type="Script" path="res://Debuggers/SpriteDebugger.cs" id="1_kpo0v"] [ext_resource type="Script" path="res://BitmapTextureRect.cs" id="2_hqqgw"] -[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://reload.png" id="4_26upw"] +[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://Prefabs/GodotBoy/UI/reload.png" id="4_26upw"] [node name="Sprite Debugger" type="Control"] layout_mode = 3 diff --git a/Gameboy.Player.Godot/Debuggers/SpriteDebugger.cs b/Gameboy.Player.Godot/Debuggers/SpriteDebugger.cs index 1faf2f6..16aafec 100644 --- a/Gameboy.Player.Godot/Debuggers/SpriteDebugger.cs +++ b/Gameboy.Player.Godot/Debuggers/SpriteDebugger.cs @@ -7,7 +7,7 @@ namespace Qkmaxware.Emulators.Gameboy.Player; public partial class SpriteDebugger : Control { - [Export] public TextureRenderer Player; + [Export] public GodotBoy Player; private BitmapTextureRect[] spriteTextures; diff --git a/Gameboy.Player.Godot/Debuggers/Tile Debugger.tscn b/Gameboy.Player.Godot/Debuggers/Tile Debugger.tscn index 70e0390..5e5400f 100644 --- a/Gameboy.Player.Godot/Debuggers/Tile Debugger.tscn +++ b/Gameboy.Player.Godot/Debuggers/Tile Debugger.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://Debuggers/TileDebugger.cs" id="1_gbx3v"] [ext_resource type="PackedScene" uid="uid://0jxwbr6l750m" path="res://Debuggers/Tile Debugger.TileTemplate.tscn" id="2_3xqvo"] -[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://reload.png" id="4_i77dr"] +[ext_resource type="Texture2D" uid="uid://divrqb47wvwvt" path="res://Prefabs/GodotBoy/UI/reload.png" id="4_i77dr"] [node name="Tile Debugger" type="Control"] layout_mode = 3 diff --git a/Gameboy.Player.Godot/Debuggers/TileDebugger.cs b/Gameboy.Player.Godot/Debuggers/TileDebugger.cs index 82e92b3..129ed30 100644 --- a/Gameboy.Player.Godot/Debuggers/TileDebugger.cs +++ b/Gameboy.Player.Godot/Debuggers/TileDebugger.cs @@ -7,7 +7,7 @@ namespace Qkmaxware.Emulators.Gameboy.Player; public partial class TileDebugger : Control { - [Export] public TextureRenderer Player; + [Export] public GodotBoy Player; private HFlowContainer container; [Export] diff --git a/Gameboy.Player.Godot/FpsCounter.cs b/Gameboy.Player.Godot/FpsCounter.cs deleted file mode 100644 index b47d436..0000000 --- a/Gameboy.Player.Godot/FpsCounter.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Godot; -using System; - -namespace Qkmaxware.Emulators.Gameboy.Player; - -public partial class FpsCounter : Label { - [Export] public TextureRenderer Renderer {get; set;} - private int fps; - - // Called every frame. 'delta' is the elapsed time since the previous frame. - public override void _Process(double delta) { - if (Renderer is not null && Renderer.IsPlaying) { - var fps = (int)Math.Round(Godot.Engine.GetFramesPerSecond()); - if (this.fps != fps) { - // New string only if the FPS changed - this.Text = "FPS: " + fps; - this.fps = fps; - } - } else { - this.Text = string.Empty; - } - } -} diff --git a/Gameboy.Player.Godot/Gameboy.Player.Godot.csproj b/Gameboy.Player.Godot/Gameboy.Player.Godot.csproj index 5f2fe48..04eb7e4 100644 --- a/Gameboy.Player.Godot/Gameboy.Player.Godot.csproj +++ b/Gameboy.Player.Godot/Gameboy.Player.Godot.csproj @@ -1,4 +1,4 @@ - + diff --git a/Gameboy.Player.Godot/Gameboy.tscn b/Gameboy.Player.Godot/Gameboy.tscn deleted file mode 100644 index 5aac39f..0000000 --- a/Gameboy.Player.Godot/Gameboy.tscn +++ /dev/null @@ -1,190 +0,0 @@ -[gd_scene load_steps=16 format=3 uid="uid://bytv8v3jd4m0l"] - -[ext_resource type="Script" path="res://TextureRenderer.cs" id="1_hfp5s"] -[ext_resource type="Texture2D" uid="uid://dfkwdfgk0dp8n" path="res://icon.svg" id="1_ptiq5"] -[ext_resource type="PackedScene" uid="uid://yis4daekext1" path="res://Debuggers/Sprite Debugger.tscn" id="3_1xhtv"] -[ext_resource type="PackedScene" uid="uid://2uytp7r2ouod" path="res://Debuggers/Map Debugger.tscn" id="3_8v8ku"] -[ext_resource type="Texture2D" uid="uid://cip0keh15srys" path="res://cart.png" id="3_gvrte"] -[ext_resource type="Theme" uid="uid://dcduu766csudy" path="res://Theme.tres" id="3_kw20t"] -[ext_resource type="Texture2D" uid="uid://bw2t0i8lwy22o" path="res://grabber.svg" id="3_w3ofc"] -[ext_resource type="PackedScene" uid="uid://b5psa1c0pvkya" path="res://Debuggers/PaletteDebugger.tscn" id="4_80mls"] -[ext_resource type="Texture2D" uid="uid://byxxvldruom6j" path="res://play.png" id="4_lwet3"] -[ext_resource type="Texture2D" uid="uid://cm332so38crqs" path="res://pause.png" id="5_5clxm"] -[ext_resource type="Script" path="res://FpsCounter.cs" id="5_rtd4o"] -[ext_resource type="PackedScene" uid="uid://t7khq15qfjuw" path="res://Debuggers/CartInfoDebug.tscn" id="5_x5b4b"] -[ext_resource type="Texture2D" uid="uid://d0dpxbrvr2bo7" path="res://stop.png" id="6_cecoo"] -[ext_resource type="PackedScene" uid="uid://ca3d3pani0rrj" path="res://Debuggers/Tile Debugger.tscn" id="7_ny0ae"] -[ext_resource type="PackedScene" uid="uid://cs764ed0jafqu" path="res://Debuggers/Disassembler.tscn" id="9_q1hjn"] - -[node name="Gameboy" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme = ExtResource("3_kw20t") - -[node name="LCD" type="TextureRect" parent="." node_paths=PackedStringArray("SaveSlot")] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_bottom = -31.0 -grow_horizontal = 2 -grow_vertical = 2 -texture = ExtResource("1_ptiq5") -expand_mode = 1 -stretch_mode = 5 -script = ExtResource("1_hfp5s") -SaveSlot = NodePath("../Top Left/Save Slot") - -[node name="Debug Menu" type="VSplitContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 48.0 -grow_horizontal = 2 -grow_vertical = 2 -theme = ExtResource("3_kw20t") -theme_override_icons/grabber = ExtResource("3_w3ofc") -split_offset = 550 - -[node name="Empty" type="Control" parent="Debug Menu"] -layout_mode = 2 -mouse_filter = 2 - -[node name="Debuggers" type="TabContainer" parent="Debug Menu"] -layout_mode = 2 -theme = ExtResource("3_kw20t") -current_tab = 1 -tab_focus_mode = 0 - -[node name="Cartridge Info" parent="Debug Menu/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("5_x5b4b")] -visible = false -layout_mode = 2 -Player = NodePath("../../../LCD") - -[node name="Colour Palettes" parent="Debug Menu/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("4_80mls")] -layout_mode = 2 -Player = NodePath("../../../LCD") - -[node name="Sprites" parent="Debug Menu/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("3_1xhtv")] -visible = false -layout_mode = 2 -Player = NodePath("../../../LCD") - -[node name="Tiles" parent="Debug Menu/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("7_ny0ae")] -visible = false -layout_mode = 2 -Player = NodePath("../../../LCD") - -[node name="Background Map" parent="Debug Menu/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("3_8v8ku")] -visible = false -layout_mode = 2 -Player = NodePath("../../../LCD") - -[node name="Window Map" parent="Debug Menu/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("3_8v8ku")] -visible = false -layout_mode = 2 -Player = NodePath("../../../LCD") -Map = 1 - -[node name="Disassembler" parent="Debug Menu/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("9_q1hjn")] -visible = false -layout_mode = 2 -Player = NodePath("../../../LCD") - -[node name="Top Left" type="HBoxContainer" parent="."] -layout_mode = 0 -offset_right = 186.0 -offset_bottom = 60.0 - -[node name="Open ROM" type="Button" parent="Top Left"] -custom_minimum_size = Vector2(48, 48) -layout_mode = 2 -focus_mode = 0 -icon = ExtResource("3_gvrte") -expand_icon = true - -[node name="FileDialog" type="FileDialog" parent="Top Left/Open ROM"] -title = "Open a File" -size = Vector2i(392, 159) -ok_button_text = "Open" -dialog_hide_on_ok = true -file_mode = 0 -access = 2 -filters = PackedStringArray("*.gb") -use_native_dialog = true - -[node name="Save Slot" type="OptionButton" parent="Top Left"] -layout_mode = 2 -focus_mode = 0 -item_count = 5 -selected = 0 -popup/item_0/text = "No Save" -popup/item_0/id = 0 -popup/item_1/text = "Save Slot 1" -popup/item_1/id = 1 -popup/item_2/text = "Save Slot 2" -popup/item_2/id = 2 -popup/item_3/text = "Save Slot 3" -popup/item_3/id = 3 -popup/item_4/text = "Save Slot 4" -popup/item_4/id = 4 - -[node name="Top Right" type="HBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -266.0 -offset_bottom = 48.0 -grow_horizontal = 0 -alignment = 2 - -[node name="Play" type="Button" parent="Top Right"] -custom_minimum_size = Vector2(48, 48) -layout_mode = 2 -focus_mode = 0 -icon = ExtResource("4_lwet3") -expand_icon = true - -[node name="FPS" type="Label" parent="Top Right/Play" node_paths=PackedStringArray("Renderer")] -layout_mode = 1 -anchors_preset = 7 -anchor_left = 0.5 -anchor_top = 1.0 -anchor_right = 0.5 -anchor_bottom = 1.0 -offset_left = -22.5 -offset_top = -3.0 -offset_right = 22.5 -offset_bottom = 20.0 -grow_horizontal = 2 -grow_vertical = 0 -text = "FPS: 00" -horizontal_alignment = 1 -script = ExtResource("5_rtd4o") -Renderer = NodePath("../../../LCD") - -[node name="Pause" type="Button" parent="Top Right"] -custom_minimum_size = Vector2(48, 48) -layout_mode = 2 -focus_mode = 0 -icon = ExtResource("5_5clxm") -expand_icon = true - -[node name="Stop" type="Button" parent="Top Right"] -custom_minimum_size = Vector2(48, 48) -layout_mode = 2 -focus_mode = 0 -icon = ExtResource("6_cecoo") -expand_icon = true - -[connection signal="pressed" from="Top Left/Open ROM" to="Top Left/Open ROM/FileDialog" method="show"] -[connection signal="file_selected" from="Top Left/Open ROM/FileDialog" to="LCD" method="LoadCartFromPath"] -[connection signal="pressed" from="Top Right/Play" to="LCD" method="Play"] -[connection signal="pressed" from="Top Right/Pause" to="LCD" method="Pause"] -[connection signal="pressed" from="Top Right/Stop" to="LCD" method="Stop"] diff --git a/Gameboy.Player.Godot/Prefabs/FpsCounter/FpsCounter.cs b/Gameboy.Player.Godot/Prefabs/FpsCounter/FpsCounter.cs new file mode 100644 index 0000000..c04189d --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/FpsCounter/FpsCounter.cs @@ -0,0 +1,18 @@ +using Godot; +using System; + +namespace Qkmaxware.Emulators.Gameboy.Player; + +public partial class FpsCounter : Label { + private int fps; + + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(double delta) { + var fps = (int)Math.Round(Godot.Engine.GetFramesPerSecond()); + if (this.fps != fps) { + // New string only if the FPS changed + this.Text = "FPS: " + fps; + this.fps = fps; + } + } +} diff --git a/Gameboy.Player.Godot/Prefabs/FpsCounter/FpsCounter.tscn b/Gameboy.Player.Godot/Prefabs/FpsCounter/FpsCounter.tscn new file mode 100644 index 0000000..6af8801 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/FpsCounter/FpsCounter.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=3 uid="uid://h76usr16og0b"] + +[ext_resource type="Script" path="res://Prefabs/FpsCounter/FpsCounter.cs" id="1_frcmh"] + +[node name="FPS" type="Label"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_frcmh") diff --git a/Gameboy.Player.Godot/TextureRenderer.cs b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.cs similarity index 50% rename from Gameboy.Player.Godot/TextureRenderer.cs rename to Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.cs index 0f47cc3..fbe3eec 100644 --- a/Gameboy.Player.Godot/TextureRenderer.cs +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.cs @@ -5,36 +5,38 @@ using LcdBitmap = Qkmaxware.Emulators.Gameboy.Hardware.Bitmap; using System.Linq; using System.IO; +using Qkmaxware.Emulators.Gameboy.Player; -namespace Qkmaxware.Emulators.Gameboy.Player; - -public partial class TextureRenderer : BitmapTextureRect { - - public enum RendererState { +public partial class GodotBoy : Control, IDebugable { + public enum ControlState { Stopped, Paused, Playing } - private RendererState State {get; set;} = RendererState.Stopped; - public bool IsPlaying => State == RendererState.Playing; - - [Export] public OptionButton SaveSlot; + private ControlState State {get; set;} = ControlState.Stopped; + public bool IsPlaying => State == ControlState.Playing; - private Gameboy gb = new Gameboy(); + [Export] public Control CartControl; + [Export] public OptionButton SaveSlot; + [Export] public OnscreenControls[] OnscreenControls; - public Gameboy Console => gb; + public Screen Screen {get; private set;} + public Gameboy Console {get; init;} = new Gameboy(); private LcdBitmap intro; - private LcdBitmap white; + 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.BackgroundWhite); + intro.Fill(ColourPallet.BackgroundDark); - white = new LcdBitmap(Gpu.LCD_WIDTH, Gpu.LCD_HEIGHT); - white.Fill(ColourPallet.BackgroundWhite); + 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.Enlarge(4)).ToArray(); + 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(); @@ -45,45 +47,52 @@ public override void _Ready() { startX += stamp.Width + 1; } - Redraw(intro); + this.Screen = this.GetNode("Screen Layouts"); + this.Screen.Redraw(intro); } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { - if (State == RendererState.Playing) { + if (State == ControlState.Stopped) { + CartControl.Visible = true; + } else { + CartControl.Visible = false; + } + + if (State == ControlState.Playing) { PollInput(); - gb.DispatchUntilBufferFlush(); - if (gb.GPU.HasBufferJustFlushed) { + this.Console.DispatchUntilBufferFlush(); + if (this.Console.GPU.HasBufferJustFlushed) { // Repaint - Redraw(gb.GPU.Canvas); + this.Screen.Redraw(this.Console.GPU.Canvas); } } } public void PollInput() { // Arrows - setInput(KeyCodes.Up, Key.W, Key.Up); - setInput(KeyCodes.Down, Key.S, Key.Down); - setInput(KeyCodes.Left, Key.A, Key.Left); - setInput(KeyCodes.Right, Key.D, Key.Right); + setInput(KeyCodes.Up, OnscreenControls.Where(ctrl => ctrl.IsUpPressed()).Any() | Godot.Input.IsActionPressed("move_up")); + setInput(KeyCodes.Down, OnscreenControls.Where(ctrl => ctrl.IsDownPressed()).Any() | Godot.Input.IsActionPressed("move_down")); + setInput(KeyCodes.Left, OnscreenControls.Where(ctrl => ctrl.IsLeftPressed()).Any() | Godot.Input.IsActionPressed("move_left")); + setInput(KeyCodes.Right, OnscreenControls.Where(ctrl => ctrl.IsRightPressed()).Any() | Godot.Input.IsActionPressed("move_right")); // Buttons - setInput(KeyCodes.A, Key.X); - setInput(KeyCodes.B, Key.Z); - setInput(KeyCodes.Start, Key.Enter); - setInput(KeyCodes.Select, Key.Space); - + + setInput(KeyCodes.A, OnscreenControls.Where(ctrl => ctrl.IsAPressed()).Any() | Godot.Input.IsActionJustPressed("button_a")); + setInput(KeyCodes.B, OnscreenControls.Where(ctrl => ctrl.IsBPressed()).Any() | Godot.Input.IsActionJustPressed("button_b")); + setInput(KeyCodes.Start, OnscreenControls.Where(ctrl => ctrl.IsStartPressed()).Any() | Godot.Input.IsActionJustPressed("button_start")); + setInput(KeyCodes.Select, OnscreenControls.Where(ctrl => ctrl.IsSelectPressed()).Any() | Godot.Input.IsActionJustPressed("button_select")); } - - private void setInput(KeyCodes vkey, params Key[] pkeys) { - var pressed = false; + private void setInput(KeyCodes vkey, bool @default, params Key?[] pkeys) { + var pressed = @default; foreach (var key in pkeys) { - pressed |= Godot.Input.IsKeyPressed(key); + if (key.HasValue) + pressed |= Godot.Input.IsKeyPressed(key.Value); } if (pressed) { - gb.Input.KeyDown(vkey); + Console.Input.KeyDown(vkey); } else { - gb.Input.KeyUp(vkey); + Console.Input.KeyUp(vkey); } } @@ -113,12 +122,12 @@ 4.The given palette setup is decoded and the color scheme is set. var hash = cart.Info.title.Select(character => (int)character).Sum(); */ Stop(); - this.gb?.LoadCartridge(cart); + this.Console?.LoadCartridge(cart); } public void Start() { - if (this.gb is not null && this.gb.IsCartridgeLoaded()) { - this.gb.Reset(); + if (this.Console is not null && this.Console.IsCartridgeLoaded()) { + this.Console.Reset(); // If we have a save slot selected, a cart loaded, and that cart has a battery then... if (SaveSlot is not null) { @@ -129,49 +138,50 @@ public void Start() { var saveSlotPath = LastCartPath + ".sav" + index; if (File.Exists(saveSlotPath)) { var saveData = File.ReadAllBytes(saveSlotPath); - this.gb.RestoreCartRam(saveData); + this.Console.RestoreCartRam(saveData); GD.Print("Loaded eRAM from: " + saveSlotPath); } } } - if (white is not null) { - Redraw(white); + if (blank is not null) { + this.Screen.Redraw(blank); } - this.State = RendererState.Playing; + this.State = ControlState.Playing; } else { GD.PushError("No cartridge loaded"); } } public void Play() { - if (State == RendererState.Paused) { - State = RendererState.Playing; + if (State == ControlState.Paused) { + State = ControlState.Playing; } else { Start(); } } public void Pause() { - if (State == RendererState.Playing) { - State = RendererState.Paused; + if (State == ControlState.Playing) { + State = ControlState.Paused; } } public void Stop() { - if (this.gb is not null && this.gb.IsCartridgeLoaded()) { - this.State = RendererState.Stopped; - if (this.gb.SupportsSaves() && !string.IsNullOrEmpty(LastCartPath) && SaveSlot is not null) { + if (this.Console is not null && this.Console.IsCartridgeLoaded()) { + this.State = ControlState.Stopped; + if (this.Console.SupportsSaves() && !string.IsNullOrEmpty(LastCartPath) && SaveSlot is not null) { var index = SaveSlot.GetItemId(SaveSlot.Selected); if (index > 0) { var saveSlotPath = LastCartPath + ".sav" + index; - File.WriteAllBytes(saveSlotPath, this.gb.DumpCartRam().ToArray()); + File.WriteAllBytes(saveSlotPath, this.Console.DumpCartRam().ToArray()); } } - this.gb.Reset(); + this.Console.Reset(); if (intro is not null) { - Redraw(intro); + this.Screen.Redraw(intro); } } } + } diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.tscn b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.tscn new file mode 100644 index 0000000..b3c0a54 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/GodotBoy.tscn @@ -0,0 +1,645 @@ +[gd_scene load_steps=38 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://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"] +[ext_resource type="Texture2D" uid="uid://ddlfysh3yd7bd" path="res://Prefabs/GodotBoy/UI/switch_button_b.png" id="8_bkmfl"] +[ext_resource type="Texture2D" uid="uid://dkg85wdntlrvs" path="res://Prefabs/GodotBoy/UI/playstation3_button_select_outline.png" id="9_8mje5"] +[ext_resource type="Texture2D" uid="uid://c4cm8i3y40sa5" path="res://Prefabs/GodotBoy/UI/playstation4_button_options_outline.png" id="9_t6atn"] +[ext_resource type="Texture2D" uid="uid://c8lu3cuownl6a" path="res://Prefabs/GodotBoy/UI/playstation3_button_select.png" id="10_7irxr"] +[ext_resource type="Texture2D" uid="uid://bf8t51vbpk3o4" path="res://Prefabs/GodotBoy/UI/playstation4_button_options.png" id="10_um660"] +[ext_resource type="Texture2D" uid="uid://cynul7h3p1uih" path="res://Prefabs/GodotBoy/UI/playstation3_button_start_outline.png" id="11_0eyg3"] +[ext_resource type="Texture2D" uid="uid://kifhudxotdni" path="res://Prefabs/GodotBoy/UI/playstation3_button_start.png" id="12_df12b"] +[ext_resource type="Texture2D" uid="uid://ddnjngordtat1" path="res://Prefabs/GodotBoy/UI/default_skin.svg" id="15_43iv6"] +[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://5ynu1mc2abvi" path="res://Prefabs/GodotBoy/UI/layout_skin.svg" id="18_sw6ge"] +[ext_resource type="Theme" uid="uid://dcduu766csudy" path="res://Theme.tres" id="20_0wny8"] +[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="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"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_o72x6"] + +[sub_resource type="AudioStreamGenerator" id="AudioStreamGenerator_h4nlb"] + +[sub_resource type="AudioStreamGenerator" id="AudioStreamGenerator_xf5bx"] + +[sub_resource type="InputEventKey" id="InputEventKey_81spp"] +device = -1 +keycode = 4194305 + +[sub_resource type="Shortcut" id="Shortcut_54dsp"] +events = [SubResource("InputEventKey_81spp")] + +[sub_resource type="InputEventKey" id="InputEventKey_mhgt7"] +device = -1 +keycode = 4194343 + +[sub_resource type="InputEventKey" id="InputEventKey_wrprd"] +device = -1 +shift_pressed = true +ctrl_pressed = true +keycode = 73 +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")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_807qv") +CartControl = NodePath("Loader") +SaveSlot = NodePath("Loader/Save Slot") +OnscreenControls = [NodePath("Screen Layouts/Onscreen Controls/Control"), NodePath("Screen Layouts/Skinned Controls/Control")] + +[node name="Screen Layouts" type="TabContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxEmpty_o72x6") +current_tab = 2 +tabs_visible = false +script = ExtResource("2_36eiy") + +[node name="Screen Only" type="Control" parent="Screen Layouts"] +visible = false +layout_mode = 2 + +[node name="Background" type="ColorRect" parent="Screen Layouts/Screen Only"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="Screen" type="TextureRect" parent="Screen Layouts/Screen Only"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("1_fcyi5") +expand_mode = 1 +stretch_mode = 5 + +[node name="Onscreen Controls" type="Control" parent="Screen Layouts"] +visible = false +layout_mode = 2 + +[node name="Background" type="ColorRect" parent="Screen Layouts/Onscreen Controls"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="Screen" type="TextureRect" parent="Screen Layouts/Onscreen Controls"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("1_fcyi5") +expand_mode = 1 +stretch_mode = 5 + +[node name="Control" type="Control" parent="Screen Layouts/Onscreen Controls" node_paths=PackedStringArray("Arrows", "A", "B", "Select", "Start")] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("16_ffynx") +Arrows = NodePath("Joystick") +A = NodePath("Buttons/A") +B = NodePath("Buttons/B") +Select = NodePath("Buttons/Select") +Start = NodePath("Buttons/Start") + +[node name="Joystick" parent="Screen Layouts/Onscreen Controls/Control" instance=ExtResource("4_drwt1")] +layout_mode = 1 + +[node name="Buttons" type="Control" parent="Screen Layouts/Onscreen Controls/Control"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="A" type="TextureButton" parent="Screen Layouts/Onscreen Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -116.0 +offset_top = -189.0 +offset_right = -41.0 +offset_bottom = -114.0 +grow_horizontal = 0 +grow_vertical = 0 +focus_mode = 0 +texture_normal = ExtResource("5_5ygii") +texture_pressed = ExtResource("6_883pa") +ignore_texture_size = true +stretch_mode = 5 + +[node name="B" type="TextureButton" parent="Screen Layouts/Onscreen Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +anchors_preset = 3 +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 +grow_horizontal = 0 +grow_vertical = 0 +focus_mode = 0 +texture_normal = ExtResource("7_o3yvv") +texture_pressed = ExtResource("8_bkmfl") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Select" type="TextureButton" parent="Screen Layouts/Onscreen Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +offset_right = 75.0 +offset_bottom = 75.0 +focus_mode = 0 +texture_normal = ExtResource("9_8mje5") +texture_pressed = ExtResource("10_7irxr") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Start" type="TextureButton" parent="Screen Layouts/Onscreen Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +offset_left = 90.0 +offset_right = 165.0 +offset_bottom = 75.0 +focus_mode = 0 +texture_normal = ExtResource("11_0eyg3") +texture_pressed = ExtResource("12_df12b") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Settings" type="TextureButton" parent="Screen Layouts/Onscreen Controls/Control/Buttons"] +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("9_t6atn") +texture_pressed = ExtResource("10_um660") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Skinned Controls" type="Control" parent="Screen Layouts"] +layout_mode = 2 + +[node name="Background" type="ColorRect" parent="Screen Layouts/Skinned Controls"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="Screen" type="TextureRect" parent="Screen Layouts/Skinned Controls"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("1_fcyi5") +expand_mode = 1 +stretch_mode = 5 + +[node name="Skin" type="TextureRect" parent="Screen Layouts/Skinned Controls"] +self_modulate = Color(0.805699, 0.390695, 0.13034, 1) +texture_filter = 1 +layout_mode = 1 +anchors_preset = -1 +anchor_top = 0.501 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -0.117035 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("15_43iv6") +expand_mode = 1 + +[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 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -0.117035 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("16_ffynx") +Arrows = NodePath("Joystick") +A = NodePath("Buttons/A") +B = NodePath("Buttons/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 + +[node name="Buttons" type="Control" parent="Screen Layouts/Skinned Controls/Control"] +texture_filter = 1 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="A" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -116.0 +offset_top = -189.0 +offset_right = -41.0 +offset_bottom = -114.0 +grow_horizontal = 0 +grow_vertical = 0 +focus_mode = 0 +texture_normal = ExtResource("5_5ygii") +texture_pressed = ExtResource("6_883pa") +ignore_texture_size = true +stretch_mode = 5 + +[node name="B" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +anchors_preset = 3 +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 +grow_horizontal = 0 +grow_vertical = 0 +focus_mode = 0 +texture_normal = ExtResource("7_o3yvv") +texture_pressed = ExtResource("8_bkmfl") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Select" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -81.5 +offset_top = -99.0 +offset_right = -6.5 +offset_bottom = -24.0 +grow_horizontal = 2 +grow_vertical = 0 +focus_mode = 0 +texture_normal = ExtResource("9_8mje5") +texture_pressed = ExtResource("10_7irxr") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Start" type="TextureButton" parent="Screen Layouts/Skinned Controls/Control/Buttons"] +custom_minimum_size = Vector2(75, 75) +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = 3.5 +offset_top = -100.0 +offset_right = 78.5 +offset_bottom = -25.0 +grow_horizontal = 2 +grow_vertical = 0 +focus_mode = 0 +texture_normal = ExtResource("11_0eyg3") +texture_pressed = ExtResource("12_df12b") +ignore_texture_size = true +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 +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_right = 37.5 +offset_bottom = 37.5 +grow_horizontal = 2 +grow_vertical = 2 +focus_mode = 0 +texture_normal = ExtResource("9_t6atn") +texture_pressed = ExtResource("10_um660") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Speakers" type="Control" parent="."] +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="Channel1 - Square1" type="AudioStreamPlayer" parent="Speakers"] +stream = SubResource("AudioStreamGenerator_h4nlb") + +[node name="Channel2 - Square2" type="AudioStreamPlayer" parent="Speakers"] +stream = SubResource("AudioStreamGenerator_xf5bx") + +[node name="Options Shortcut" type="Button" parent="."] +layout_mode = 0 +offset_right = 8.0 +offset_bottom = 8.0 +focus_mode = 0 +mouse_filter = 2 +shortcut = SubResource("Shortcut_54dsp") + +[node name="Options" type="Window" parent="."] +title = "Options" +initial_position = 2 +size = Vector2i(640, 480) +visible = false +exclusive = true +popup_window = true + +[node name="MarginContainer" type="MarginContainer" parent="Options"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 6 +theme_override_constants/margin_right = 6 + +[node name="ScrollContainer" type="ScrollContainer" parent="Options/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ScrollContents" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer"] +layout_mode = 2 +theme_override_constants/separation = 24 + +[node name="Display" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Display"] +layout_mode = 2 +text = "Layout" + +[node name="GridContainer" type="GridContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Display"] +layout_mode = 2 +columns = 3 + +[node name="Screen Only" type="TextureButton" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Display/GridContainer"] +layout_mode = 2 +tooltip_text = "Screen Only" +texture_normal = ExtResource("16_s0175") + +[node name="Screen + Controls" type="TextureButton" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Display/GridContainer"] +layout_mode = 2 +tooltip_text = "Onscreen Controls" +texture_normal = ExtResource("17_arug4") + +[node name="Skinned" type="TextureButton" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Display/GridContainer"] +layout_mode = 2 +tooltip_text = "Faux device with onscreen controls" +texture_normal = ExtResource("18_sw6ge") + +[node name="Colours" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Colours"] +layout_mode = 2 +text = "Display Colours +" + +[node name="Themes" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Colours"] +layout_mode = 2 + +[node name="OptionButton" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Colours/Themes" node_paths=PackedStringArray("Screen") instance=ExtResource("21_xltfd")] +layout_mode = 2 +Screen = NodePath("../../../../../../../Screen Layouts") + +[node name="Label2" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Colours"] +layout_mode = 2 +text = "Skin Colour +" + +[node name="ColorRect" type="ColorPickerButton" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Colours"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +color = Color(0.803922, 0.392157, 0.129412, 1) +edit_alpha = false + +[node name="Keybinds" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Keybinds"] +layout_mode = 2 +text = "Keybindings" + +[node name="Developer" type="VBoxContainer" parent="Options/MarginContainer/ScrollContainer/ScrollContents"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Developer"] +layout_mode = 2 +text = "Developer" + +[node name="RichTextLabel" type="RichTextLabel" parent="Options/MarginContainer/ScrollContainer/ScrollContents/Developer"] +layout_mode = 2 +text = "Press ctrl+shift+i or F12 to open the inspector." +fit_content = true + +[node name="Debug Shortcut" type="Button" parent="."] +layout_mode = 0 +offset_right = 8.0 +offset_bottom = 8.0 +focus_mode = 0 +mouse_filter = 2 +shortcut = SubResource("Shortcut_2ct1q") + +[node name="Debug" type="Window" parent="."] +title = "Inspector" +initial_position = 2 +size = Vector2i(640, 480) +visible = false +exclusive = true +popup_window = true + +[node name="MarginContainer" type="MarginContainer" parent="Debug"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 6 +theme_override_constants/margin_right = 6 + +[node name="Debuggers" type="TabContainer" parent="Debug/MarginContainer"] +layout_mode = 2 +theme = ExtResource("20_0wny8") +tab_focus_mode = 0 + +[node name="Cartridge Info" parent="Debug/MarginContainer/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("21_y5t5s")] +layout_mode = 2 +Player = NodePath("../../../..") + +[node name="Colour Palettes" parent="Debug/MarginContainer/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("22_e37a5")] +visible = false +layout_mode = 2 +Player = NodePath("../../../..") + +[node name="Sprites" parent="Debug/MarginContainer/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("23_01ypf")] +visible = false +layout_mode = 2 +Player = NodePath("../../../..") + +[node name="Tiles" parent="Debug/MarginContainer/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("24_cepwr")] +visible = false +layout_mode = 2 +Player = NodePath("../../../..") + +[node name="Background Map" parent="Debug/MarginContainer/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("25_i7c8m")] +visible = false +layout_mode = 2 +Player = NodePath("../../../..") + +[node name="Window Map" parent="Debug/MarginContainer/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("25_i7c8m")] +visible = false +layout_mode = 2 +Player = NodePath("../../../..") +Map = 1 + +[node name="Disassembler" parent="Debug/MarginContainer/Debuggers" node_paths=PackedStringArray("Player") instance=ExtResource("26_1mdrh")] +visible = false +layout_mode = 2 +Player = NodePath("../../../..") + +[node name="Loader" type="HBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -116.0 +offset_right = 116.0 +offset_bottom = 48.0 +grow_horizontal = 2 + +[node name="Open ROM" type="Button" parent="Loader"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +focus_mode = 0 +icon = ExtResource("28_f1kbb") +expand_icon = true + +[node name="FileDialog" type="FileDialog" parent="Loader/Open ROM"] +title = "Open a File" +size = Vector2i(392, 159) +ok_button_text = "Open" +dialog_hide_on_ok = true +file_mode = 0 +access = 2 +filters = PackedStringArray("*.gb") +use_native_dialog = true + +[node name="Save Slot" type="OptionButton" parent="Loader"] +layout_mode = 2 +focus_mode = 0 +item_count = 5 +selected = 0 +popup/item_0/text = "No Save" +popup/item_0/id = 0 +popup/item_1/text = "Save Slot 1" +popup/item_1/id = 1 +popup/item_2/text = "Save Slot 2" +popup/item_2/id = 2 +popup/item_3/text = "Save Slot 3" +popup/item_3/id = 3 +popup/item_4/text = "Save Slot 4" +popup/item_4/id = 4 + +[node name="Play" type="Button" parent="Loader"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +focus_mode = 0 +icon = ExtResource("29_f4os1") +expand_icon = true + +[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"] +[connection signal="close_requested" from="Options" to="Options" method="hide"] +[connection signal="pressed" from="Options/MarginContainer/ScrollContainer/ScrollContents/Display/GridContainer/Screen Only" to="Screen Layouts" method="set_current_tab" binds= [0]] +[connection signal="pressed" from="Options/MarginContainer/ScrollContainer/ScrollContents/Display/GridContainer/Screen + Controls" to="Screen Layouts" method="set_current_tab" binds= [1]] +[connection signal="pressed" from="Options/MarginContainer/ScrollContainer/ScrollContents/Display/GridContainer/Skinned" to="Screen Layouts" method="set_current_tab" binds= [2]] +[connection signal="color_changed" from="Options/MarginContainer/ScrollContainer/ScrollContents/Colours/ColorRect" to="Screen Layouts/Skinned Controls/Skin" method="set_self_modulate"] +[connection signal="pressed" from="Debug Shortcut" to="Debug" method="show"] +[connection signal="close_requested" from="Debug" to="Debug" method="hide"] +[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"] diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/Screen.cs b/Gameboy.Player.Godot/Prefabs/GodotBoy/Screen.cs new file mode 100644 index 0000000..5ca183d --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/Screen.cs @@ -0,0 +1,89 @@ +using Godot; +using System; +using Qkmaxware.Emulators.Gameboy; +using Qkmaxware.Emulators.Gameboy.Hardware; +using LcdBitmap = Qkmaxware.Emulators.Gameboy.Hardware.Bitmap; + +namespace Qkmaxware.Emulators.Gameboy.Player; + +public partial class Screen : TabContainer { + + [Export] + [ExportGroup("Colour Pallet/Background")] + public Color BgWhite = new Color(1, 1, 1); + [Export] + public Color BgLightGrey = new Color(0.6f, 0.6f, 0.6f); + [Export] + public Color BgDarkGrey = new Color(0.3f, 0.3f, 0.3f); + [Export] + public Color BgBlack = new Color(0, 0, 0); + + [Export] + [ExportGroup("Colour Pallet/Object 0")] + public Color Obj0White = new Color(1, 1, 1); + [Export] + public Color Obj0LightGrey = new Color(0.6f, 0.6f, 0.6f); + [Export] + public Color Obj0DarkGrey = new Color(0.3f, 0.3f, 0.3f); + [Export] + public Color Obj0Black = new Color(0, 0, 0); + + [Export] + [ExportGroup("Colour Pallet/Object 1")] + public Color Obj1White = new Color(1, 1, 1); + [Export] + public Color Obj1LightGrey = new Color(0.6f, 0.6f, 0.6f); + [Export] + public Color Obj1DarkGrey = new Color(0.3f, 0.3f, 0.3f); + [Export] + public Color Obj1Black = new Color(0, 0, 0); + + private ImageTexture texture; + private TextureRect[] screens; + + public override void _Ready() { + base._Ready(); + var tabs = this.GetTabCount(); + screens = new TextureRect[tabs]; + for (int i = 0; i < tabs; i++) { + screens[i] = GetTabControl(i).GetNode("Screen"); + } + } + + public void Redraw(LcdBitmap bmp) { + var pixels = Image.Create(width: bmp.Width, height: bmp.Height, useMipmaps: false, format: Image.Format.Rgb8); + + for (var col = 0; col < bmp.Height; col++) { + for (var row = 0; row < bmp.Width; row++) { + pixels.SetPixel(row, col, bmp[row, col] switch { + ColourPallet.BackgroundDark => BgBlack, + ColourPallet.Object0Dark => Obj0Black, + ColourPallet.Object1Dark => Obj1Black, + + ColourPallet.BackgroundMedium => BgDarkGrey, + ColourPallet.Object0Medium => Obj0DarkGrey, + ColourPallet.Object1Medium => Obj1DarkGrey, + + ColourPallet.BackgroundLight => BgLightGrey, + ColourPallet.Object0Light => Obj0LightGrey, + ColourPallet.Object1Light => Obj1LightGrey, + + ColourPallet.BackgroundWhite => BgWhite, + ColourPallet.Object0White => Obj0White, + ColourPallet.Object1White => Obj1White, + + _ => BgBlack, + }); + } + } + + if (texture is null) { + texture = ImageTexture.CreateFromImage(pixels); + } else { + texture.Update(pixels); + } + var screen = screens[CurrentTab]; + if (screen is not null) + screen.Texture = texture; + } +} diff --git a/Gameboy.Player.Godot/cart.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/cart.png similarity index 100% rename from Gameboy.Player.Godot/cart.png rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/cart.png diff --git a/Gameboy.Player.Godot/cart.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/cart.png.import similarity index 72% rename from Gameboy.Player.Godot/cart.png.import rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/cart.png.import index 6f9bd88..c5b9833 100644 --- a/Gameboy.Player.Godot/cart.png.import +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/cart.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://cip0keh15srys" -path="res://.godot/imported/cart.png-c0d5895155eac46d26ac9947ca1d6fe3.ctex" +path="res://.godot/imported/cart.png-b1ffef32367b56a7d679c49aeedea96a.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://cart.png" -dest_files=["res://.godot/imported/cart.png-c0d5895155eac46d26ac9947ca1d6fe3.ctex"] +source_file="res://Prefabs/GodotBoy/UI/cart.png" +dest_files=["res://.godot/imported/cart.png-b1ffef32367b56a7d679c49aeedea96a.ctex"] [params] diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg new file mode 100644 index 0000000..c8b8ca0 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg @@ -0,0 +1,146 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg.import new file mode 100644 index 0000000..31efa92 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/default_skin.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddnjngordtat1" +path="res://.godot/imported/default_skin.svg-857290d6a2fc00d89533e16813fdb109.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/default_skin.svg" +dest_files=["res://.godot/imported/default_skin.svg-857290d6a2fc00d89533e16813fdb109.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/generic_button_square.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/generic_button_square.png new file mode 100644 index 0000000..cb6e96f Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/generic_button_square.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/generic_button_square.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/generic_button_square.png.import new file mode 100644 index 0000000..0e7e3af --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/generic_button_square.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bfnisybj4frrr" +path="res://.godot/imported/generic_button_square.png-6a9685eebb102b869fea42b0dce63363.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/generic_button_square.png" +dest_files=["res://.godot/imported/generic_button_square.png-6a9685eebb102b869fea42b0dce63363.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/GodotBoy/UI/joyknobpng.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyknobpng.png new file mode 100644 index 0000000..9804335 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyknobpng.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyknobpng.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyknobpng.png.import new file mode 100644 index 0000000..0c8b811 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyknobpng.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dc2kgxdwvesdo" +path="res://.godot/imported/joyknobpng.png-766889e6568f60814cdefc24141a659f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/joyknobpng.png" +dest_files=["res://.godot/imported/joyknobpng.png-766889e6568f60814cdefc24141a659f.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/GodotBoy/UI/joyring.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyring.png new file mode 100644 index 0000000..d01d240 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyring.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyring.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyring.png.import new file mode 100644 index 0000000..2252687 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joyring.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cgwofjnfpool2" +path="res://.godot/imported/joyring.png-75f23ef480e465c6c29666903c73c30c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/joyring.png" +dest_files=["res://.godot/imported/joyring.png-75f23ef480e465c6c29666903c73c30c.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/GodotBoy/UI/joystick_knob.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joystick_knob.png new file mode 100644 index 0000000..2c8f6ef Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joystick_knob.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joystick_knob.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joystick_knob.png.import new file mode 100644 index 0000000..6947c1d --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/joystick_knob.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://hysemwm538jj" +path="res://.godot/imported/joystick_knob.png-b9bac1fbbee1293d0e037463c33bccc3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/joystick_knob.png" +dest_files=["res://.godot/imported/joystick_knob.png-b9bac1fbbee1293d0e037463c33bccc3.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/GodotBoy/UI/layout_screen.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg new file mode 100644 index 0000000..e0ed44e --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg @@ -0,0 +1,51 @@ + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg.import new file mode 100644 index 0000000..d358a19 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://trhljjy16es1" +path="res://.godot/imported/layout_screen.svg-9c20a382107844da3db940e465e3ee17.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/layout_screen.svg" +dest_files=["res://.godot/imported/layout_screen.svg-9c20a382107844da3db940e465e3ee17.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/layout_screen_control.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen_control.svg new file mode 100644 index 0000000..c25641a --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen_control.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen_control.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen_control.svg.import new file mode 100644 index 0000000..ab693ff --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_screen_control.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dma6vgdj4ntir" +path="res://.godot/imported/layout_screen_control.svg-28c640910c67d73b9b122d66a307dac8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/layout_screen_control.svg" +dest_files=["res://.godot/imported/layout_screen_control.svg-28c640910c67d73b9b122d66a307dac8.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/layout_skin.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_skin.svg new file mode 100644 index 0000000..2cfcb04 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_skin.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_skin.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_skin.svg.import new file mode 100644 index 0000000..77e2b93 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/layout_skin.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://5ynu1mc2abvi" +path="res://.godot/imported/layout_skin.svg-aeb2689fad80197666ddd19850c6c8ad.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/layout_skin.svg" +dest_files=["res://.godot/imported/layout_skin.svg-aeb2689fad80197666ddd19850c6c8ad.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/pause.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/pause.png similarity index 100% rename from Gameboy.Player.Godot/pause.png rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/pause.png diff --git a/Gameboy.Player.Godot/pause.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/pause.png.import similarity index 72% rename from Gameboy.Player.Godot/pause.png.import rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/pause.png.import index 66fa342..d6ff400 100644 --- a/Gameboy.Player.Godot/pause.png.import +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/pause.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://cm332so38crqs" -path="res://.godot/imported/pause.png-fae43b08edb08fdf891c9b1e11f62ed7.ctex" +path="res://.godot/imported/pause.png-0c74b45bcb2a7b010dfbf16cb92603da.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://pause.png" -dest_files=["res://.godot/imported/pause.png-fae43b08edb08fdf891c9b1e11f62ed7.ctex"] +source_file="res://Prefabs/GodotBoy/UI/pause.png" +dest_files=["res://.godot/imported/pause.png-0c74b45bcb2a7b010dfbf16cb92603da.ctex"] [params] diff --git a/Gameboy.Player.Godot/play.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/play.png similarity index 100% rename from Gameboy.Player.Godot/play.png rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/play.png diff --git a/Gameboy.Player.Godot/play.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/play.png.import similarity index 72% rename from Gameboy.Player.Godot/play.png.import rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/play.png.import index 72bc89c..5c3f037 100644 --- a/Gameboy.Player.Godot/play.png.import +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/play.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://byxxvldruom6j" -path="res://.godot/imported/play.png-50de493e98049fad870da9de2a2dfd65.ctex" +path="res://.godot/imported/play.png-5a76e886dd84c2d982bcbdd11f9b8e92.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://play.png" -dest_files=["res://.godot/imported/play.png-50de493e98049fad870da9de2a2dfd65.ctex"] +source_file="res://Prefabs/GodotBoy/UI/play.png" +dest_files=["res://.godot/imported/play.png-5a76e886dd84c2d982bcbdd11f9b8e92.ctex"] [params] diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select.png new file mode 100644 index 0000000..1be8106 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select.png.import new file mode 100644 index 0000000..a6675b7 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c8lu3cuownl6a" +path="res://.godot/imported/playstation3_button_select.png-432b11082d2051c5c14dd53b28c36682.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/playstation3_button_select.png" +dest_files=["res://.godot/imported/playstation3_button_select.png-432b11082d2051c5c14dd53b28c36682.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/GodotBoy/UI/playstation3_button_select_outline.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select_outline.png new file mode 100644 index 0000000..c02cab2 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select_outline.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select_outline.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select_outline.png.import new file mode 100644 index 0000000..3f4afd6 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_select_outline.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dkg85wdntlrvs" +path="res://.godot/imported/playstation3_button_select_outline.png-c41dd2c28359421a3e6ab197da940c61.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/playstation3_button_select_outline.png" +dest_files=["res://.godot/imported/playstation3_button_select_outline.png-c41dd2c28359421a3e6ab197da940c61.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/GodotBoy/UI/playstation3_button_start.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start.png new file mode 100644 index 0000000..6978fd0 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start.png.import new file mode 100644 index 0000000..a691b7a --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kifhudxotdni" +path="res://.godot/imported/playstation3_button_start.png-3db50ae6c64d3e1b44ddaa459076562e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/playstation3_button_start.png" +dest_files=["res://.godot/imported/playstation3_button_start.png-3db50ae6c64d3e1b44ddaa459076562e.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/GodotBoy/UI/playstation3_button_start_outline.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start_outline.png new file mode 100644 index 0000000..f3858a5 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start_outline.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start_outline.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start_outline.png.import new file mode 100644 index 0000000..6c3b26c --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation3_button_start_outline.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cynul7h3p1uih" +path="res://.godot/imported/playstation3_button_start_outline.png-564fae64ce529b1c6fd8da404bbf7d5d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/playstation3_button_start_outline.png" +dest_files=["res://.godot/imported/playstation3_button_start_outline.png-564fae64ce529b1c6fd8da404bbf7d5d.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/GodotBoy/UI/playstation4_button_options.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options.png new file mode 100644 index 0000000..e49914a Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options.png.import new file mode 100644 index 0000000..4c5e856 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bf8t51vbpk3o4" +path="res://.godot/imported/playstation4_button_options.png-1947639e8fbc4912e287e9d1d0831368.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/playstation4_button_options.png" +dest_files=["res://.godot/imported/playstation4_button_options.png-1947639e8fbc4912e287e9d1d0831368.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/GodotBoy/UI/playstation4_button_options_outline.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options_outline.png new file mode 100644 index 0000000..9a2ce96 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options_outline.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options_outline.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options_outline.png.import new file mode 100644 index 0000000..56e5b3f --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/playstation4_button_options_outline.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4cm8i3y40sa5" +path="res://.godot/imported/playstation4_button_options_outline.png-74749caa9be9b1c52107ccf267e99fc5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/playstation4_button_options_outline.png" +dest_files=["res://.godot/imported/playstation4_button_options_outline.png-74749caa9be9b1c52107ccf267e99fc5.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/reload.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png similarity index 100% rename from Gameboy.Player.Godot/reload.png rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png diff --git a/Gameboy.Player.Godot/reload.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png.import similarity index 72% rename from Gameboy.Player.Godot/reload.png.import rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png.import index f74724f..5c136c9 100644 --- a/Gameboy.Player.Godot/reload.png.import +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/reload.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://divrqb47wvwvt" -path="res://.godot/imported/reload.png-17f917f262bb5696bd39267f6e625492.ctex" +path="res://.godot/imported/reload.png-6ca0f34c56c00e5c4a6d91fa1bc2d54a.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://reload.png" -dest_files=["res://.godot/imported/reload.png-17f917f262bb5696bd39267f6e625492.ctex"] +source_file="res://Prefabs/GodotBoy/UI/reload.png" +dest_files=["res://.godot/imported/reload.png-6ca0f34c56c00e5c4a6d91fa1bc2d54a.ctex"] [params] diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/settings.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/settings.png new file mode 100644 index 0000000..b7f1ac7 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/settings.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/settings.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/settings.png.import new file mode 100644 index 0000000..368f690 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/settings.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://xgxq8becl7om" +path="res://.godot/imported/settings.png-97ca68a33b577529734dbbe54c3425e5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/settings.png" +dest_files=["res://.godot/imported/settings.png-97ca68a33b577529734dbbe54c3425e5.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/stop.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/stop.png similarity index 100% rename from Gameboy.Player.Godot/stop.png rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/stop.png diff --git a/Gameboy.Player.Godot/stop.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/stop.png.import similarity index 72% rename from Gameboy.Player.Godot/stop.png.import rename to Gameboy.Player.Godot/Prefabs/GodotBoy/UI/stop.png.import index 0a6e9c1..653a548 100644 --- a/Gameboy.Player.Godot/stop.png.import +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/stop.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://d0dpxbrvr2bo7" -path="res://.godot/imported/stop.png-5d09e73e8435d35574ee90426acf1e35.ctex" +path="res://.godot/imported/stop.png-b77317c01068aeaa2c46c9af8b9c1fb6.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://stop.png" -dest_files=["res://.godot/imported/stop.png-5d09e73e8435d35574ee90426acf1e35.ctex"] +source_file="res://Prefabs/GodotBoy/UI/stop.png" +dest_files=["res://.godot/imported/stop.png-b77317c01068aeaa2c46c9af8b9c1fb6.ctex"] [params] diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a.png new file mode 100644 index 0000000..2399fc2 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a.png.import new file mode 100644 index 0000000..e52b389 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bw2mb47r4ud7" +path="res://.godot/imported/switch_button_a.png-fdd102d8cf644c2cb522c024ba34f5f1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/switch_button_a.png" +dest_files=["res://.godot/imported/switch_button_a.png-fdd102d8cf644c2cb522c024ba34f5f1.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/GodotBoy/UI/switch_button_a_outline.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a_outline.png new file mode 100644 index 0000000..8dd7cb9 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a_outline.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a_outline.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a_outline.png.import new file mode 100644 index 0000000..9186e03 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_a_outline.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dubx3mrt06u5p" +path="res://.godot/imported/switch_button_a_outline.png-2e12467b8707e8fb628c03bdcec0d2b2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/switch_button_a_outline.png" +dest_files=["res://.godot/imported/switch_button_a_outline.png-2e12467b8707e8fb628c03bdcec0d2b2.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/GodotBoy/UI/switch_button_b.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b.png new file mode 100644 index 0000000..c66ce33 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b.png.import new file mode 100644 index 0000000..bd7a621 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddlfysh3yd7bd" +path="res://.godot/imported/switch_button_b.png-e39bb7bf5961bb57df4208f45d06e760.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/switch_button_b.png" +dest_files=["res://.godot/imported/switch_button_b.png-e39bb7bf5961bb57df4208f45d06e760.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/GodotBoy/UI/switch_button_b_outline.png b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b_outline.png new file mode 100644 index 0000000..474d894 Binary files /dev/null and b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b_outline.png differ diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b_outline.png.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b_outline.png.import new file mode 100644 index 0000000..d5bea88 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/switch_button_b_outline.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://it7a6pssev4k" +path="res://.godot/imported/switch_button_b_outline.png-c758ba8676e18bea51c46c6f0aa89445.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/switch_button_b_outline.png" +dest_files=["res://.godot/imported/switch_button_b_outline.png-c758ba8676e18bea51c46c6f0aa89445.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/GodotBoy/UI/title.svg b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/title.svg new file mode 100644 index 0000000..134c1fd --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/title.svg @@ -0,0 +1,59 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/title.svg.import b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/title.svg.import new file mode 100644 index 0000000..f977408 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/GodotBoy/UI/title.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://chk7wpjvrytcv" +path="res://.godot/imported/title.svg-c5badc06c92d6948be7a2bf7f0e68358.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Prefabs/GodotBoy/UI/title.svg" +dest_files=["res://.godot/imported/title.svg-c5badc06c92d6948be7a2bf7f0e68358.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/OnscreenControls/Joystick.cs b/Gameboy.Player.Godot/Prefabs/OnscreenControls/Joystick.cs new file mode 100644 index 0000000..88ccec2 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/OnscreenControls/Joystick.cs @@ -0,0 +1,71 @@ +using Godot; +using System; + +public partial class Joystick : Control { + + [Export] public int SnapSpeed = 25; + [Export] public int DeadZone = 5; + private Control knob; + + private bool pressing = false; + private int maxLength = 50; + + // Called when the node enters the scene tree for the first time. + public override void _Ready() { + knob = this.GetNode("Knob"); + maxLength = (int)this.Size.X/2; + } + + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(double delta) { + var center = this.GlobalPosition + this.Size * 0.5f; + var knob_offset = knob.Size * 0.5f; + + if (!this.pressing) { + knob.GlobalPosition = knob.GlobalPosition.Lerp(center - knob_offset, (float)delta*SnapSpeed); + return; + } + + var mouse_pos = this.GetGlobalMousePosition(); + var distance = mouse_pos.DistanceTo(center); + if (distance > maxLength) { + var angle = center.AngleToPoint(mouse_pos); + mouse_pos.X = center.X + Mathf.Cos(angle) * maxLength; + mouse_pos.Y = center.Y + Mathf.Sin(angle) * maxLength; + } + + knob.GlobalPosition = mouse_pos - knob_offset; + } + + public Vector2 GetInputVector() { + if (!this.pressing) { + return Vector2.Zero; + } + + var center = this.GlobalPosition + this.Size * 0.5f; + var mouse_pos = this.GetGlobalMousePosition(); + var distance = mouse_pos.DistanceTo(center); + var vector = mouse_pos - center; + + // Clamp values & Deadzone + if (Mathf.Abs(vector.X) <= this.DeadZone) { + vector.X = 0; + } + vector.X = Mathf.Clamp(vector.X, -maxLength, maxLength); + if (Mathf.Abs(vector.Y) <= this.DeadZone) { + vector.Y = 0; + } + vector.Y = Mathf.Clamp(vector.Y, -maxLength, maxLength); + + return vector / maxLength; + } + + public void OnButtonDown() { + pressing = true; + } + + public void OnButtonUp() { + pressing = false; + } + +} \ No newline at end of file diff --git a/Gameboy.Player.Godot/Prefabs/OnscreenControls/Joystick.tscn b/Gameboy.Player.Godot/Prefabs/OnscreenControls/Joystick.tscn new file mode 100644 index 0000000..d763c7c --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/OnscreenControls/Joystick.tscn @@ -0,0 +1,60 @@ +[gd_scene load_steps=4 format=3 uid="uid://bcvv8417hthjf"] + +[ext_resource type="Script" path="res://Prefabs/OnscreenControls/Joystick.cs" id="1_e6dly"] +[ext_resource type="Texture2D" uid="uid://cgwofjnfpool2" path="res://Prefabs/GodotBoy/UI/joyring.png" id="2_sd8fw"] +[ext_resource type="Texture2D" uid="uid://hysemwm538jj" path="res://Prefabs/GodotBoy/UI/joystick_knob.png" id="3_6sxs2"] + +[node name="Joystick" type="Control"] +custom_minimum_size = Vector2(200, 200) +layout_mode = 3 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 23.0 +offset_top = -223.0 +offset_right = 223.0 +offset_bottom = -23.0 +grow_vertical = 0 +pivot_offset = Vector2(100, 100) +script = ExtResource("1_e6dly") + +[node name="Border" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +texture = ExtResource("2_sd8fw") +expand_mode = 1 +stretch_mode = 6 + +[node name="Knob" type="TextureRect" parent="."] +custom_minimum_size = Vector2(80, 80) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 +pivot_offset = Vector2(40, 40) +mouse_filter = 2 +texture = ExtResource("3_6sxs2") +expand_mode = 1 +stretch_mode = 5 + +[node name="Button" type="Button" parent="."] +modulate = Color(1, 1, 1, 0) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_mode = 0 + +[connection signal="button_down" from="Button" to="." method="OnButtonDown"] +[connection signal="button_up" from="Button" to="." method="OnButtonUp"] diff --git a/Gameboy.Player.Godot/Prefabs/OnscreenControls/OnscreenControls.cs b/Gameboy.Player.Godot/Prefabs/OnscreenControls/OnscreenControls.cs new file mode 100644 index 0000000..5678165 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/OnscreenControls/OnscreenControls.cs @@ -0,0 +1,22 @@ +using Godot; +using System; + +public partial class OnscreenControls : Control { + + [Export] public Joystick Arrows; + [Export] public TextureButton A; + [Export] public TextureButton B; + [Export] public TextureButton Select; + [Export] public TextureButton Start; + + public bool IsLeftPressed() => Arrows.GetInputVector().X <= -0.5f; + public bool IsRightPressed() => Arrows.GetInputVector().X >= 0.5f; + + public bool IsDownPressed() => Arrows.GetInputVector().Y >= 0.5f; + public bool IsUpPressed() => Arrows.GetInputVector().Y <= -0.5f; + + public bool IsAPressed() => A is null ? false : A.ButtonPressed; + public bool IsBPressed() => B is null ? false : B.ButtonPressed; + public bool IsSelectPressed() => B is null ? false : Select.ButtonPressed; + public bool IsStartPressed() => B is null ? false : Start.ButtonPressed; +} diff --git a/Gameboy.Player.Godot/Prefabs/ThemePicker/ThemePicker.cs b/Gameboy.Player.Godot/Prefabs/ThemePicker/ThemePicker.cs new file mode 100644 index 0000000..2d0594d --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/ThemePicker/ThemePicker.cs @@ -0,0 +1,42 @@ +using Godot; +using System; +using System.Linq; +using System.Collections.Generic; +using Qkmaxware.Emulators.Gameboy.Hardware; + +namespace Qkmaxware.Emulators.Gameboy.Player; + +public partial class ThemePicker : OptionButton { + + [Export] public Screen Screen; + + // Called when the node enters the scene tree for the first time. + public override void _Ready() { + foreach (var theme in LcdColorTheme.Named) { + this.AddItem(theme.Name); + } + } + + public void SelectTheme(int index) { + if (Screen is null) + return; + + if (index >= 0 && index < LcdColorTheme.Named.Count) { + var theme = LcdColorTheme.Named[index]; + Screen.BgBlack = theme.BgBlack; + Screen.BgDarkGrey= theme.BgDarkGrey; + Screen.BgLightGrey = theme.BgLightGrey; + Screen.BgWhite = theme.BgWhite; + + Screen.Obj0Black = theme.Obj0Black; + Screen.Obj0DarkGrey = theme.Obj0DarkGrey; + Screen.Obj0LightGrey = theme.Obj0LightGrey; + Screen.Obj0White = theme.Obj0White; + + Screen.Obj1Black = theme.Obj1Black; + Screen.Obj1DarkGrey = theme.Obj1DarkGrey; + Screen.Obj1LightGrey = theme.Obj1LightGrey; + Screen.Obj1White = theme.Obj1White; + } + } +} diff --git a/Gameboy.Player.Godot/Prefabs/ThemePicker/ThemePicker.tscn b/Gameboy.Player.Godot/Prefabs/ThemePicker/ThemePicker.tscn new file mode 100644 index 0000000..d21e0a7 --- /dev/null +++ b/Gameboy.Player.Godot/Prefabs/ThemePicker/ThemePicker.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=2 format=3 uid="uid://7wdw54d0sdlj"] + +[ext_resource type="Script" path="res://Prefabs/ThemePicker/ThemePicker.cs" id="1_tdy1j"] + +[node name="OptionButton" type="OptionButton" node_paths=PackedStringArray("Screen")] +allow_reselect = true +script = ExtResource("1_tdy1j") +Screen = NodePath("") + +[connection signal="item_selected" from="." to="." method="SelectTheme"] diff --git a/Gameboy.Player.Godot/export_presets.cfg b/Gameboy.Player.Godot/export_presets.cfg index 18dffa3..a78c5ff 100644 --- a/Gameboy.Player.Godot/export_presets.cfg +++ b/Gameboy.Player.Godot/export_presets.cfg @@ -8,7 +8,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="" -export_path="Bin/Gameboy.Player.Godot.exe" +export_path="../bin/win-x64/BlazorBoy.exe" encryption_include_filters="" encryption_exclude_filters="" encrypt_pck=false diff --git a/Gameboy.Player.Godot/project.godot b/Gameboy.Player.Godot/project.godot index 9bf6d6a..8d92bd0 100644 --- a/Gameboy.Player.Godot/project.godot +++ b/Gameboy.Player.Godot/project.godot @@ -10,18 +10,81 @@ config_version=5 [application] -config/name="Gameboy.Player.Godot" -run/main_scene="res://Gameboy.tscn" +config/name="BlazorBoy" +run/main_scene="res://Prefabs/GodotBoy/GodotBoy.tscn" config/features=PackedStringArray("4.2", "C#", "GL Compatibility") run/max_fps=60 boot_splash/bg_color=Color(0.141176, 0.141176, 0.141176, 1) boot_splash/image="res://console.png" config/icon="res://icon.svg" +[display] + +window/subwindows/embed_subwindows=false + [dotnet] project/assembly_name="Gameboy.Player.Godot" +[input] + +move_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +move_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":14,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +move_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":11,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +move_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":12,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +button_a={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":88,"key_label":0,"unicode":120,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) +] +} +button_b={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":90,"key_label":0,"unicode":122,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) +] +} +button_start={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":true,"script":null) +] +} +button_select={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":4,"pressure":0.0,"pressed":true,"script":null) +] +} + [rendering] textures/canvas_textures/default_texture_filter=0 diff --git a/Gameboy/GameBoy.cs b/Gameboy/GameBoy.cs index 8928459..f61e2b3 100644 --- a/Gameboy/GameBoy.cs +++ b/Gameboy/GameBoy.cs @@ -11,9 +11,10 @@ public class Gameboy { public Input Input {get; init;} private Hardware.Timer timer {get; init;} private CartridgeAdapter cart {get; init;} + public Hardware.Sound Sound {get; init;} public SerialConnection Serial {get; init;} - public Gameboy(IPpu? gpu = null, Input? input = null, CartridgeAdapter? cartReader = null, Hardware.Timer? timer = null, SerialConnection? serialIO = null) { + public Gameboy(IPpu? gpu = null, Input? input = null, CartridgeAdapter? cartReader = null, Hardware.Timer? timer = null, Hardware.Sound? sound = null, SerialConnection? serialIO = null) { mmu = new MemoryMap(); Hardrive onboard = new Hardrive(); @@ -23,12 +24,14 @@ public Gameboy(IPpu? gpu = null, Input? input = null, CartridgeAdapter? cartRead this.cart = cartReader ?? new CartridgeAdapter(); SerialConnection sysio = serialIO ?? new SerialConnection(null, null); this.Serial = sysio; + this.Sound = sound ?? new Hardware.Sound(); mmu.SetMappedComponent(MemoryMap.INTERNAL_RAM, onboard); mmu.SetMappedComponent(MemoryMap.ZRAM, onboard); mmu.SetMappedComponent(MemoryMap.JOYSTICK, this.Input); mmu.SetMappedComponent(MemoryMap.TIMER, this.timer); + mmu.SetMappedComponent(MemoryMap.SOUND, this.Sound); mmu.SetMappedComponent(MemoryMap.OAM, this.GPU); mmu.SetMappedComponent(MemoryMap.VRAM, this.GPU); @@ -60,6 +63,7 @@ public void Reset(){ timer.Reset(); cart.Reset(); CPU.Reset(); + Sound.Reset(); } public Cartridge? GetCartridge() { diff --git a/Gameboy/MemoryMap.cs b/Gameboy/MemoryMap.cs index 0b2c484..0537566 100644 --- a/Gameboy/MemoryMap.cs +++ b/Gameboy/MemoryMap.cs @@ -34,11 +34,12 @@ FFFF Interrupt Enable Flag public static readonly int OAM = 5; public static readonly int GPU = 6; public static readonly int ZRAM = 7; + public static readonly int SERIALIO = 8; public static readonly int JOYSTICK = 9; public static readonly int TIMER = 10; - public static readonly int SERIALIO = 8; + public static readonly int SOUND = 11; - private IMemorySegment[] mappedComponents = new IMemorySegment[11]; + private IMemorySegment[] mappedComponents = new IMemorySegment[12]; public IMemorySegment GetMappedComponent(int i){ return this.mappedComponents[i]; } @@ -49,7 +50,7 @@ public void SetMappedComponent(int i, IMemorySegment map){ } #endregion - #region Interupts + #region Interrupts public int EnabledInterupts = 0; public int InteruptFlags = 0; public static readonly int INTERRUPT_VBLANK = 0b1; @@ -127,9 +128,9 @@ public int ReadByte(int addr) { //Interrupt Flags return InteruptFlags; } - else if(between(addr, 0xFF10, 0xFF39)){ + else if(between(addr, 0xFF10, 0xFF3F)){ //Sound control, envelope ect - return 0; + return mappedComponents[SOUND].ReadByte(addr); } else if(between(addr, 0xFF40, 0xFF7F)){ return mappedComponents[GPU].ReadByte(addr); @@ -193,8 +194,9 @@ public void WriteByte(int addr, int value) { //Interrupt Flags InteruptFlags = value; } - else if(between(addr, 0xFF10, 0xFF39)){ + else if(between(addr, 0xFF10, 0xFF3F)){ //Sound control, envelope ect + mappedComponents[SOUND].WriteByte(addr, value); } else if(between(addr, 0xFF40, 0xFF7F)){ mappedComponents[GPU].WriteByte(addr, value); diff --git a/Gameboy/hardware/gpu/Bitmap.cs b/Gameboy/hardware/gpu/Bitmap.cs index 5f3752d..0be0ed2 100644 --- a/Gameboy/hardware/gpu/Bitmap.cs +++ b/Gameboy/hardware/gpu/Bitmap.cs @@ -39,6 +39,37 @@ public Bitmap(ColourPallet[,] pixels) { } } + public Bitmap Invert() { + var bitmap = new Bitmap(this.Width, this.Height); + for (var y = 0; y < this.Height; y++) { + for (var x = 0; x < this.Width; x++) { + bitmap[x, y] = invert(this[x,y]); + } + } + return bitmap; + } + private static ColourPallet invert(ColourPallet x) { + return x switch { + ColourPallet.BackgroundDark => ColourPallet.BackgroundWhite, + ColourPallet.Object0Dark => ColourPallet.Object0White, + ColourPallet.Object1Dark => ColourPallet.Object1White, + + ColourPallet.BackgroundMedium => ColourPallet.BackgroundLight, + ColourPallet.Object0Medium => ColourPallet.Object0Light, + ColourPallet.Object1Medium => ColourPallet.Object1Light, + + ColourPallet.BackgroundLight => ColourPallet.BackgroundMedium, + ColourPallet.Object0Light => ColourPallet.Object0Medium , + ColourPallet.Object1Light => ColourPallet.Object1Medium, + + ColourPallet.BackgroundWhite => ColourPallet.BackgroundDark, + ColourPallet.Object0White => ColourPallet.Object0Dark, + ColourPallet.Object1White => ColourPallet.Object1Dark, + + _ => ColourPallet.BackgroundDark, + }; + } + public void Fill(ColourPallet colour) { Array.Fill(this.pixels, (byte)colour); } diff --git a/Gameboy/hardware/sound/Sound.cs b/Gameboy/hardware/sound/Sound.cs new file mode 100644 index 0000000..9317242 --- /dev/null +++ b/Gameboy/hardware/sound/Sound.cs @@ -0,0 +1,246 @@ +// Sound hardware created from the description on the site below +// https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware +// Might be able to be emulated in Godot using https://docs.godotengine.org/en/stable/classes/class_audiostreamgenerator.html +namespace Qkmaxware.Emulators.Gameboy.Hardware; + +public class Channel { + public int NRx0; + public int NRx1; + public int NRx2; + public int NRx3; + public int NRx4; + + public void Reset() { + NRx0 = NRx1 = NRx2 = NRx3 = NRx4 = 0; + } +} + +public class SquareWaveChannel : Channel { + public int SweepPeriod => (NRx0 >> 4) & (0b0000_0111); + public bool Negate => ((NRx0 >> 3) & (0b0000_0001)) != 0; + public int Shift => (NRx0 >> 0) & (0b0000_0111); + + public int Duty => (NRx1 >> 6) & (0b0000_0011); + public int LengthLoad => (NRx1 >> 0) & (0b0011_1111); + + public int StartingVolume => (NRx2 >> 4) & (0b0000_1111); + public bool EnvelopeAddMode => ((NRx2 >> 3) & (0b0000_0001)) != 0; + public int Period => (NRx2 >> 0) & (0b0000_0111); + + public int FrequencyLsb => NRx3; + + public bool Trigger => ((NRx4 >> 7) & 0b0000_0001) != 0; + public bool LengthEnable => ((NRx4 >> 6) & 0b0000_0001) != 0; + public int FrequencyMsb => NRx4 & 0b0000_0111; +} + +public enum VolumeCode { + Volume0 = 0b00, + Volume100 = 0b01, + Volume50 = 0b10, + Volume25 = 0b11 +} + +public class WaveChannel : Channel { + public bool DacPower => ((NRx0) & (0b1000_0000)) != 0; + public int LengthLoad => NRx1; + public VolumeCode Volume => ((NRx2 >> 5) & 0b0000_0011) switch { + 0b00 => VolumeCode.Volume0, + 0b01 => VolumeCode.Volume100, + 0b10 => VolumeCode.Volume50, + 0b11 => VolumeCode.Volume25, + _ => VolumeCode.Volume0 + }; + public int FrequencyLsb => NRx3; + public bool Trigger => ((NRx4 >> 7) & 0b0000_0001) != 0; + public bool LengthEnable => ((NRx4 >> 6) & 0b0000_0001) != 0; + public int FrequencyMsb => NRx4 & 0b0000_0111; +} + +public class NoiseChannel : Channel { + public int LengthLoad => ((NRx1 >> 0) & 0b0011_1111); + public int StartingVolume => (NRx2 >> 4) & (0b0000_1111); + public bool EnvelopeAddMode => ((NRx2 >> 3) & (0b0000_0001)) != 0; + public int Period => (NRx2 >> 0) & (0b0000_0111); + public int ClockShift => (NRx3 >> 4) & 0b0000_1111; + public bool WidthModeOfLfsr => ((NRx3 >> 0) & 0b0000_1000) != 0; + public int DivisorCode => (NRx3 >> 0) & 0b0000_0111; + public bool Trigger => ((NRx4 >> 7) & 0b0000_0001) != 0; + public bool LengthEnable => ((NRx4 >> 6) & 0b0000_0001) != 0; +} + +public class Sound : IMemorySegment { + + public SquareWaveChannel Channel1 = new SquareWaveChannel(); + public SquareWaveChannel Channel2 = new SquareWaveChannel(); + public WaveChannel Channel3 = new WaveChannel(); + public NoiseChannel Channel4 = new NoiseChannel(); + + public SquareWaveChannel Square1 => Channel1; + public SquareWaveChannel Square2 => Channel2; + public WaveChannel Wave => Channel3; + public NoiseChannel Noise => Channel4; + + private int FF24; + public bool VinLeftEnabled => (FF24 & 0b1000_0000) != 0; + public bool VinRightEnabled => (FF24 & 0b0000_1000) != 0; + public int LeftVolume => ((FF24 >> 4) & 0b0000_0111); + public int RightVolume => ((FF24 >> 0) & 0b0000_0111); + + private int FF25; + public bool IsLeftOn => (FF25 & 0b1111_0000) != 0; + public bool IsRightOn => (FF25 & 0b0000_1111) != 0; + + private int FF26; + public bool IsPoweredOn => (FF26 & 0b1000_0000) != 0; + public int ChannelLengthStatuses => (FF26 & 0b0000_1111); + + private int[] WaveTable = new int[0xFF40 - 0xFF30]; + + public void Reset() { + Channel1.Reset(); + Channel2.Reset(); + Channel3.Reset(); + Channel4.Reset(); + + FF24 = 0; + FF25 = 0; + FF26 = 0; + + Array.Fill(WaveTable, 0); + } + + private MemoryMap? mmu; + public void SetMMU(MemoryMap mmu) { + this.mmu = mmu; + } + + public int ReadByte(int addr) { + if (addr < 0xFF10 || addr > 0xFF3F) { + return 0; + } + + switch (addr) { + // Channels + case 0xFF10: return Channel1.NRx0; + case 0xFF11: return Channel1.NRx1; + case 0xFF12: return Channel1.NRx2; + case 0xFF13: return Channel1.NRx3; + case 0xFF14: return Channel1.NRx4; + + case 0xFF15: return Channel2.NRx0; + case 0xFF16: return Channel2.NRx1; + case 0xFF17: return Channel2.NRx2; + case 0xFF18: return Channel2.NRx3; + case 0xFF19: return Channel2.NRx4; + + case 0xFF1A: return Channel3.NRx0; + case 0xFF1B: return Channel3.NRx1; + case 0xFF1C: return Channel3.NRx2; + case 0xFF1D: return Channel3.NRx3; + case 0xFF1E: return Channel3.NRx4; + + case 0xFF1F: return Channel4.NRx0; + case 0xFF20: return Channel4.NRx1; + case 0xFF21: return Channel4.NRx2; + case 0xFF22: return Channel4.NRx3; + case 0xFF23: return Channel4.NRx4; + + // Control & Status + case 0xFF24: return FF24; + case 0xFF25: return FF25; + case 0xFF26: return FF26; + + // Unused + case >= 0xFF27 and 0xFF2F: + return 0xFF; + + // Wave Table + case >= 0xFF30 and <= 0xFF3F: + return WaveTable[addr - 0xFF30]; + + default: return 0; + } + } + + public void WriteByte(int addr, int value) { + if (addr < 0xFF10 || addr > 0xFF3F) { + return; + } + + var power = this.IsPoweredOn; + + switch (addr) { + // Channels + case 0xFF10: Channel1.NRx0 = value; break; + case 0xFF11: Channel1.NRx1 = value; break; + case 0xFF12: Channel1.NRx2 = value; break; + case 0xFF13: Channel1.NRx3 = value; break; + case 0xFF14: Channel1.NRx4 = value; break; + + case 0xFF15: Channel2.NRx0 = value; break; + case 0xFF16: Channel2.NRx1 = value; break; + case 0xFF17: Channel2.NRx2 = value; break; + case 0xFF18: Channel2.NRx3 = value; break; + case 0xFF19: Channel2.NRx4 = value; break; + + case 0xFF1A: Channel3.NRx0 = value; break; + case 0xFF1B: Channel3.NRx1 = value; break; + case 0xFF1C: Channel3.NRx2 = value; break; + case 0xFF1D: Channel3.NRx3 = value; break; + case 0xFF1E: Channel3.NRx4 = value; break; + + case 0xFF1F: Channel4.NRx0 = value; break; + case 0xFF20: Channel4.NRx1 = value; break; + case 0xFF21: Channel4.NRx2 = value; break; + case 0xFF22: Channel4.NRx3 = value; break; + case 0xFF23: Channel4.NRx4 = value; break; + + // Control & Status + case 0xFF24: FF24 = value; break; + case 0xFF25: FF25 = value; break; + case 0xFF26: FF26 = value; break; + + // Wave Table + case >= 0xFF30 and <= 0xFF3F: + WaveTable[addr - 0xFF30] = value; + break; + } + + // If power status has changed, power on or off + if (power != this.IsPoweredOn) { + switch (this.IsPoweredOn) { + case true: onPoweredOn(); break; + case false: onPoweredOff(); break; + } + } + } + + private void onPoweredOn() { + // GBC default wave table values + WaveTable[0] = 0x00; + WaveTable[1] = 0xFF; + WaveTable[2] = 0x00; + WaveTable[3] = 0xFF; + WaveTable[4] = 0x00; + WaveTable[5] = 0xFF; + WaveTable[6] = 0x00; + WaveTable[7] = 0xFF; + WaveTable[8] = 0x00; + WaveTable[9] = 0xFF; + WaveTable[10] = 0x00; + WaveTable[11] = 0xFF; + WaveTable[12] = 0x00; + WaveTable[13] = 0xFF; + WaveTable[14] = 0x00; + WaveTable[15] = 0xFF; + } + + private void onPoweredOff() { + Channel1.Reset(); + Channel2.Reset(); + Channel3.Reset(); + Channel4.Reset(); + } + +} diff --git a/TODO b/TODO index 276143d..9ea6b04 100644 --- a/TODO +++ b/TODO @@ -12,4 +12,7 @@ Controllers [ ] Double check MBC1 implementation [ ] Double check MBC2 implementation [ ] Double check MBC3 implementation -[ ] Double check MBC5 implementation \ No newline at end of file +[ ] Double check MBC5 implementation + +Front-ends +[ ] Make Godot front-end prettier \ No newline at end of file