diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 59bb39f..f3073c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,14 +16,22 @@ jobs: - name: Build run: (cd source && dotnet publish Burntime.MonoGame -c Release /p:DebugSymbols=false /p:PublishReadyToRun=false /p:TieredCompilation=false --self-contained /p:PublishSingleFile=true && cd ..) +# - name: Build Tools +# run: | +# cd source +# dotnet publish PakConverter -c Release /p:DebugSymbols=false /p:PublishReadyToRun=false /p:TieredCompilation=false --self-contained /p:PublishSingleFile=true +# cd .. + - name: Package run: | rm ./bin/Release/win-x64/publish/*.pdb - tar -caf burntime.zip -C ./bin/Release/win-x64/publish * + rm ./bin/tools/Release/win-x64/publish/*.pdb + tar -caf burntime-${{ github.ref_name }}.zip -C ./bin/Release/win-x64/publish * +# tar -caf burntime-tools.zip -C ./bin/tools/Release/win-x64/publish * - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: draft: true - files: ./burntime.zip \ No newline at end of file + files: ./burntime*.zip \ No newline at end of file diff --git a/resources/Changelog.md b/resources/Changelog.md index 03a5d2b..7575f76 100644 --- a/resources/Changelog.md +++ b/resources/Changelog.md @@ -1,5 +1,34 @@ # Burntime Changelog +## 0.7 - Options, Trader and Amiga (2023-10-26) + +### Changes + +- Improved trader + - Show both trader and player inventory on wide screens +- Reworked options + - Change language in options + - Toggle fullscreen in options + - Show fullscreen and remaster graphics shortcuts + - Jukebox to play all songs +- Remastered graphics + - Player flags + - Cursor animations + - Trader intro scenes + - Inventory UIs +- Added Amiga sounds, music + - Amiga version hit, barf and die sounds + - Switch to Amiga music in options +- Start game with language selection +- Save user settings +- Scroll maps with right mouse button + +### Fixes + +- Fixed draw order of characters on map +- Fixed music in monastery +- Ensure 4:3 screen portion is always on screen, independent of window size + ## 0.6 - Music Support (2023-10-21) ### Changes diff --git a/resources/game/classic/gfx/ui/options_bulb.png b/resources/game/classic/gfx/ui/options_bulb.png new file mode 100644 index 0000000..06c5583 Binary files /dev/null and b/resources/game/classic/gfx/ui/options_bulb.png differ diff --git a/resources/game/classic/lang/en/newburn.txt b/resources/game/classic/lang/en/newburn.txt index 2523137..07ac68c 100644 --- a/resources/game/classic/lang/en/newburn.txt +++ b/resources/game/classic/lang/en/newburn.txt @@ -15,22 +15,22 @@ no AI players easy normal hard -NewGfx ON -NewGfx OFF - - - - - - - - - - - - - - +Graphics REMASTER +Graphics ORIGINAL +Fullscreen ON +Fullscreen OFF +Saves +Settings +toggle in-game with F11 +toggle in-game with F8 +toggle in-game with F9 +Language ENGLISH +Give up +Start new game +Jukebox +Music AMIGA +Music DOS +Music REMASTER diff --git a/resources/game/classic/sounds/barf.ogg b/resources/game/classic/sounds/barf.ogg new file mode 100644 index 0000000..945388a Binary files /dev/null and b/resources/game/classic/sounds/barf.ogg differ diff --git a/resources/game/classic/sounds/camp.ogg b/resources/game/classic/sounds/camp.ogg new file mode 100644 index 0000000..5916ec4 Binary files /dev/null and b/resources/game/classic/sounds/camp.ogg differ diff --git a/resources/game/classic/sounds/change.ogg b/resources/game/classic/sounds/change.ogg new file mode 100644 index 0000000..c73872a Binary files /dev/null and b/resources/game/classic/sounds/change.ogg differ diff --git a/resources/game/classic/sounds/hit-barf.ogg b/resources/game/classic/sounds/hit-barf.ogg new file mode 100644 index 0000000..b6cf7e7 Binary files /dev/null and b/resources/game/classic/sounds/hit-barf.ogg differ diff --git a/resources/game/classic/sounds/hit-die.ogg b/resources/game/classic/sounds/hit-die.ogg new file mode 100644 index 0000000..327b100 Binary files /dev/null and b/resources/game/classic/sounds/hit-die.ogg differ diff --git a/resources/game/classic/sounds/hit.ogg b/resources/game/classic/sounds/hit.ogg new file mode 100644 index 0000000..325252d Binary files /dev/null and b/resources/game/classic/sounds/hit.ogg differ diff --git a/resources/game/classic/sounds/trader.ogg b/resources/game/classic/sounds/trader.ogg new file mode 100644 index 0000000..e1bb450 Binary files /dev/null and b/resources/game/classic/sounds/trader.ogg differ diff --git a/resources/game/classic_de/highres-font_de.png b/resources/game/classic_de/highres-font_de.png new file mode 100644 index 0000000..ad96bd3 Binary files /dev/null and b/resources/game/classic_de/highres-font_de.png differ diff --git a/resources/game/classic_de/highres-font_de.txt b/resources/game/classic_de/highres-font_de.txt new file mode 100644 index 0000000..19adb50 --- /dev/null +++ b/resources/game/classic_de/highres-font_de.txt @@ -0,0 +1,10 @@ +image=highres-font.png +factor=0.5 +offset=-2 +lines=1 +height=12 +char0=" !""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyzüäÄöÖÜß" +// ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z ü ä Ä ö Ö Ãœ ß" +width0=6 2 4 4 6 6 4 3 4 4 6 4 3 5 2 6 5 3 5 5 5 5 5 5 5 5 2 4 5 5 5 5 2 5 5 5 5 5 5 5 5 2 5 5 4 6 5 5 5 5 5 5 5 5 5 6 5 5 5 3 6 3 6 5 3 6 5 6 5 5 4 5 5 2 5 5 3 6 5 5 5 5 5 5 4 5 5 6 6 5 5 6 6 5 6 6 6 5 +y0=0 + diff --git a/resources/game/classic_de/lang/de/newburn.txt b/resources/game/classic_de/lang/de/newburn.txt index be49605..c83476c 100644 --- a/resources/game/classic_de/lang/de/newburn.txt +++ b/resources/game/classic_de/lang/de/newburn.txt @@ -15,22 +15,22 @@ Ohne Computergegner Einfach Normal Schwer -NewGfx AN -NewGfx AUS - - - - - - - - - - - - - - +Grafik REMASTER +Grafik ORIGINAL +Vollbild AN +Vollbild AUS +Spielst„nde +Optionen +im Spiel mit F11 wechseln +im Spiel mit F8 wechseln +im Spiel mit F9 wechseln +Sprache DEUTSCH +Aufgeben +Neues Spiel starten +Jukebox +Musik AMIGA +Musik DOS +Musik REMASTER diff --git a/resources/game/classic_newgfx/gfx/backgrounds/film10.png b/resources/game/classic_newgfx/gfx/backgrounds/film10.png new file mode 100644 index 0000000..ae7c154 Binary files /dev/null and b/resources/game/classic_newgfx/gfx/backgrounds/film10.png differ diff --git a/resources/game/classic_newgfx/gfx/backgrounds/film10_ani.png b/resources/game/classic_newgfx/gfx/backgrounds/film10_ani.png new file mode 100644 index 0000000..56ffb4e Binary files /dev/null and b/resources/game/classic_newgfx/gfx/backgrounds/film10_ani.png differ diff --git a/resources/game/classic_newgfx/gfx/backgrounds/film5.png b/resources/game/classic_newgfx/gfx/backgrounds/film5.png new file mode 100644 index 0000000..8fe0b5a Binary files /dev/null and b/resources/game/classic_newgfx/gfx/backgrounds/film5.png differ diff --git a/resources/game/classic_newgfx/gfx/backgrounds/film5_ani.png b/resources/game/classic_newgfx/gfx/backgrounds/film5_ani.png new file mode 100644 index 0000000..cdcb5de Binary files /dev/null and b/resources/game/classic_newgfx/gfx/backgrounds/film5_ani.png differ diff --git a/resources/game/classic_newgfx/gfx/backgrounds/film5_ani2.png b/resources/game/classic_newgfx/gfx/backgrounds/film5_ani2.png new file mode 100644 index 0000000..d1acd49 Binary files /dev/null and b/resources/game/classic_newgfx/gfx/backgrounds/film5_ani2.png differ diff --git a/resources/game/classic_newgfx/gfx/backgrounds/film5_ani3.png b/resources/game/classic_newgfx/gfx/backgrounds/film5_ani3.png new file mode 100644 index 0000000..b1975ff Binary files /dev/null and b/resources/game/classic_newgfx/gfx/backgrounds/film5_ani3.png differ diff --git a/resources/game/classic_newgfx/gfx/backgrounds/statistics2.png b/resources/game/classic_newgfx/gfx/backgrounds/statistics2.png deleted file mode 100644 index 448dd9e..0000000 Binary files a/resources/game/classic_newgfx/gfx/backgrounds/statistics2.png and /dev/null differ diff --git a/resources/game/classic_newgfx/gfx/backgrounds/trader.png b/resources/game/classic_newgfx/gfx/backgrounds/trader.png new file mode 100644 index 0000000..b87b845 Binary files /dev/null and b/resources/game/classic_newgfx/gfx/backgrounds/trader.png differ diff --git a/resources/game/classic_newgfx/gfx/inventory_buttons.png b/resources/game/classic_newgfx/gfx/inventory_buttons.png new file mode 100644 index 0000000..adff6f9 Binary files /dev/null and b/resources/game/classic_newgfx/gfx/inventory_buttons.png differ diff --git a/resources/game/classic_newgfx/gfx/inventory_left.png b/resources/game/classic_newgfx/gfx/inventory_left.png new file mode 100644 index 0000000..ab066fe Binary files /dev/null and b/resources/game/classic_newgfx/gfx/inventory_left.png differ diff --git a/resources/game/classic_newgfx/gfx/inventory_middle.png b/resources/game/classic_newgfx/gfx/inventory_middle.png new file mode 100644 index 0000000..5b5e647 Binary files /dev/null and b/resources/game/classic_newgfx/gfx/inventory_middle.png differ diff --git a/resources/game/classic_newgfx/gfx/inventory_right.png b/resources/game/classic_newgfx/gfx/inventory_right.png new file mode 100644 index 0000000..2729e8d Binary files /dev/null and b/resources/game/classic_newgfx/gfx/inventory_right.png differ diff --git a/resources/game/classic_newgfx/gfx/munt_daytime.png b/resources/game/classic_newgfx/gfx/munt_daytime.png new file mode 100644 index 0000000..c42fdaa Binary files /dev/null and b/resources/game/classic_newgfx/gfx/munt_daytime.png differ diff --git a/resources/game/classic_newgfx/gfx/munt_fight.png b/resources/game/classic_newgfx/gfx/munt_fight.png new file mode 100644 index 0000000..248db42 Binary files /dev/null and b/resources/game/classic_newgfx/gfx/munt_fight.png differ diff --git a/resources/game/classic_newgfx/gfx/munt_info.png b/resources/game/classic_newgfx/gfx/munt_info.png new file mode 100644 index 0000000..2cddae4 Binary files /dev/null and b/resources/game/classic_newgfx/gfx/munt_info.png differ diff --git a/resources/game/classic_newgfx/gfx/objects/flags.png b/resources/game/classic_newgfx/gfx/objects/flags.png new file mode 100644 index 0000000..9a34f6d Binary files /dev/null and b/resources/game/classic_newgfx/gfx/objects/flags.png differ diff --git a/resources/game/classic_newgfx/gfx/objects/player_icons.png b/resources/game/classic_newgfx/gfx/objects/player_icons.png index d8002f5..6ba534f 100644 Binary files a/resources/game/classic_newgfx/gfx/objects/player_icons.png and b/resources/game/classic_newgfx/gfx/objects/player_icons.png differ diff --git a/resources/game/classic_newgfx/newgfx.txt b/resources/game/classic_newgfx/newgfx.txt index 068516c..e4e0633 100644 --- a/resources/game/classic_newgfx/newgfx.txt +++ b/resources/game/classic_newgfx/newgfx.txt @@ -1,4 +1,11 @@ [replacement_60x72] +' Remastered graphics are ratio corrected from 8:5 to 4:3 - 1 horizontal to 1.2 vertical. +' Upscale is not exactly 2x but ratio correct to ensure clean pixel sizes and positions. +' 32x32 -> 60x72 +' 16x16 -> 30x36 +' 8x8 -> 15x18 +' Ratio correction factor without upscale: 0.9375x1.125 + sprite_scale=1.875x2.25 zei_004.raw=pngsheet@maps/shared/set_4.png?{0}?60x72 zei_005.raw=pngsheet@maps/shared/set_5.png?{0}?60x72 @@ -36,22 +43,47 @@ maps/mat_033_tiles.png=pngsheet@maps/mat_033_tiles_2x.png?{0}?60x72 film_02.pac=gfx/backgrounds/film2.png film_03.pac=gfx/backgrounds/film3.png film_04.pac=gfx/backgrounds/film4.png +film_05.pac=gfx/backgrounds/film5.png film_09.pac=gfx/backgrounds/film9.png +film_10.pac=gfx/backgrounds/film10.png info.pac=gfx/backgrounds/info.png +hint1.pac=gfx/backgrounds/trader.png +film_05.ani?0-17?p=pngsheet@gfx/backgrounds/film5_ani.png?0-17?135x126 +film_05.ani?18-19=pngsheet@gfx/backgrounds/film5_ani2.png?0-1?90x72 +film_05.ani?20-21=pngsheet@gfx/backgrounds/film5_ani3.png?0-1?90x72 +film_10.ani=pngsheet@gfx/backgrounds/film10_ani.png?0-12?60x72 syssze.raw=pngsheet@gfx/syssze.png?{0}?30x36 munt.raw?3=pngsheet@gfx/objects/munt.png?0?15x18 munt.raw?4=pngsheet@gfx/objects/munt.png?1?15x18 +munt.raw?22=gfx/munt_daytime.png gfx/up.png=gfx/up.png gfx/down.png=gfx/down.png +gfx/inventory_left.png=gfx/inventory_left.png +inv.raw?1=gfx/inventory_middle.png +inv.raw?2=gfx/inventory_right.png +munt.raw?5=pngsheet@gfx/inventory_buttons.png?0?30x36 +munt.raw?6=pngsheet@gfx/inventory_buttons.png?1?30x36 +munt.raw?7=pngsheet@gfx/inventory_buttons.png?2?30x36 +munt.raw?8=pngsheet@gfx/inventory_buttons.png?3?30x36 +munt.raw?9=pngsheet@gfx/inventory_buttons.png?4?30x36 syst.raw?16=pngsheet@gfx/objects/player_icon_colors.png?0?36x38 syst.raw?17=pngsheet@gfx/objects/player_icon_colors.png?1?36x38 syst.raw?18=pngsheet@gfx/objects/player_icon_colors.png?2?36x38 syst.raw?19=pngsheet@gfx/objects/player_icon_colors.png?3?36x38 +burngfxani@syst.raw?24-27=pngsheet@gfx/syssze.png?18-19?30x36 +burngfxani@syst.raw?20-23=pngsheet@gfx/munt_info.png?0-3?23x30 +burngfxani@munt.raw?14-17=pngsheet@gfx/munt_fight.png?0-3?30x54 +burngfxani@syst.raw?0-3=pngsheet@gfx/objects/flags.png?0-11?30x36 +burngfxani@syst.raw?4-7=pngsheet@gfx/objects/flags.png?12-23?30x36 +burngfxani@syst.raw?8-11=pngsheet@gfx/objects/flags.png?24-35?30x36 +burngfxani@syst.raw?12-15=pngsheet@gfx/objects/flags.png?36-47?30x36 [newgfx_native] +' Graphics only used in remastered mode + sprite_scale=1.875x2.25 gfx/char_shadow.png=gfx/char_shadow.png gfx/objects/player_icons.png=pngsheet@gfx/objects/player_icons.png?{0}?36x38 @@ -63,12 +95,16 @@ gfx/backgrounds/statistics.png=gfx/backgrounds/statistics.png gfx/backgrounds/statistics2.png=gfx/backgrounds/statistics2.png [replacement] +' Old upscale graphics. They should be replaced by 60x72 verisons eventually. + sprite_scale=2x2 burngfxani@munt.raw=pngani@gfx/munt_{0}.png munt.raw=gfx/munt_{0}.png sta.pac=gfx/sta.png [replacement_stretched] +' Old upscale graphics. They should be replaced by 60x72 verisons eventually. + sprite_scale=2x2.375 ges_00.ani=pngsheet@gfx/faces/ges_00.png?0-39?136x152 ges_01.ani=pngsheet@gfx/faces/ges_01.png?0?136x133 diff --git a/source/BurnGfxRipper/AnimationExporter.cs b/source/BurnGfxRipper/AnimationExporter.cs index d38040c..97e7f3b 100644 --- a/source/BurnGfxRipper/AnimationExporter.cs +++ b/source/BurnGfxRipper/AnimationExporter.cs @@ -3,6 +3,8 @@ using System.Drawing.Imaging; using System.Drawing; using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Linq; namespace BurnGfxRipper; @@ -12,7 +14,8 @@ public void Export(string file, string dir, CommandParameter parameter) { if (!parameter.MegaTexture) { - ExportAsSeparateFiles(file, dir, parameter); + ExportAsSpriteSheet(file, dir, parameter); + //ExportAsSeparateFiles(file, dir, parameter); } else { @@ -43,11 +46,43 @@ private void ExportAsSeparateFiles(string file, string dir, CommandParameter par } } + void ExportAsSpriteSheet(string originalFilePath, string dir, CommandParameter parameter) + { + SpriteLoaderAni loader = new(); + loader.Process(originalFilePath); + System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(dir)); + string outputFilePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(dir), System.IO.Path.GetFileNameWithoutExtension(originalFilePath)); + + var animations = Enumerable.Range(0, loader.FrameCount) + .Select(index => { loader.SetFrame(index); return loader.FrameSize; }) + .GroupBy(size => size) + .Select(group => new { Size = group.Key, Count = group.Count() }); + + int frameCount = 0; + int animCount = 1; + foreach (var anim in animations) + { + loader.Process($"{originalFilePath}?{frameCount}-{frameCount + anim.Count}"); + loader.SetFrame(0); + var progressive = new SpriteSheet(loader.Size.x * anim.Count, loader.Size.x, loader.Size.y, anim.Count, 0); + progressive.Render(loader, nonProgressive: false, heightPadding: 1); + TextureUtils.Save(progressive.Bitmap, $"{outputFilePath}_{animCount}_p.png", parameter); + + loader.Process($"{originalFilePath}?{frameCount}-{frameCount + anim.Count}?p"); + loader.SetFrame(0); + var fullImage = new SpriteSheet(loader.Size.x * anim.Count, loader.Size.x, loader.Size.y, anim.Count, 0); + fullImage.Render(loader, nonProgressive: false, heightPadding: 1); + TextureUtils.Save(fullImage.Bitmap, $"{outputFilePath}_{animCount}_f.png", parameter); + + frameCount += anim.Count; + animCount++; + } + } + private void ExportAsSingleFile(string originalFilePath, string dir, CommandParameter parameter) { SpriteLoaderAni ani = new(); ani.Process(originalFilePath); - System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(dir)); ani.SetFrame(0); @@ -57,10 +92,9 @@ private void ExportAsSingleFile(string originalFilePath, string dir, CommandPara progressive.Render(ani, false); TextureUtils.Save(progressive.Bitmap, outputFilePath + "_progressive.png", parameter); - var baseImage = new SpriteSheet(parameter.TextureWidth, ani.Size.x, ani.Size.y, ani.FrameCount); + var baseImage = new SpriteSheet(parameter.TextureWidth, ani.Size.x, ani.Size.y, ani.FrameCount, parameter.Padding ? 1 : 0); baseImage.Render(ani, true); TextureUtils.Save(baseImage.Bitmap, outputFilePath + "_base.png", parameter); - TextureUtils.Save(baseImage.Bitmap, progressive.Bitmap, outputFilePath + "_full.png", parameter); } } diff --git a/source/BurnGfxRipper/Program.cs b/source/BurnGfxRipper/Program.cs index 98fb791..68c7f04 100644 --- a/source/BurnGfxRipper/Program.cs +++ b/source/BurnGfxRipper/Program.cs @@ -14,8 +14,8 @@ class CommandParameter public int TextureWidth = 0; public bool RatioCorrection = false; public bool MegaTexture = false; - public bool Padding = true; - public bool Palette = true; + public bool Padding = false; + public bool Palette = false; public bool HandleArg(string arg) { diff --git a/source/BurnGfxRipper/SpriteSheet.cs b/source/BurnGfxRipper/SpriteSheet.cs index d2cd425..dbbeb4a 100644 --- a/source/BurnGfxRipper/SpriteSheet.cs +++ b/source/BurnGfxRipper/SpriteSheet.cs @@ -57,38 +57,49 @@ public void RenderSingle(MemoryStream memory, int frameWidth, int frameHeight, i Bitmap.UnlockBits(loc); } - public void Render(ISpriteAnimationProcessor ani, bool baseImage) + public void Render(ISpriteAnimationProcessor ani, bool nonProgressive, int heightPadding = TextureUtils.HEIGHT_PADDING, int startFrame = 0, int frameCount = 0) { - ani.SetFrame(0); + ani.SetFrame(startFrame); + frameCount = frameCount == 0 ? (ani.FrameCount - startFrame - 1) : System.Math.Min(frameCount, (ani.FrameCount - startFrame - 1)); + int columns = (int)Math.Floor(Bitmap.Width / (float)(ani.Size.x + _padding * 2)); - int rows = (int)Math.Ceiling(ani.FrameCount / (float)columns); + int rows = (int)Math.Ceiling(frameCount / (float)columns); - int rowHeight = TextureUtils.MakeMultipleOf(ani.Size.y, TextureUtils.HEIGHT_PADDING) + _padding * 2; + int rowHeight = TextureUtils.MakeMultipleOf(ani.Size.y, heightPadding) + _padding * 2; int columnWidth = ani.FrameSize.x + _padding * 2; - int i = 0; + int frame = startFrame; for (int y = 0; y < rows; y++) { - for (int x = 0; x < columns && i < ani.FrameCount; x++) + for (int x = 0; x < columns && frame < frameCount; x++) { - ani.SetFrame(baseImage ? 0 : i); + ani.SetFrame(frame); BitmapData loc = Bitmap.LockBits(new Rectangle(x * columnWidth, y * rowHeight, ani.FrameSize.x, ani.FrameSize.y), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); System.IO.MemoryStream mem = new(); + + if (nonProgressive) + { + for (int baseFrame = startFrame; baseFrame < frame; baseFrame++) + { + ani.SetFrame(baseFrame); + ani.Render(mem, ani.FrameSize.x * 4); + } + } + + ani.SetFrame(frame); ani.Render(mem, ani.FrameSize.x * 4); byte[] buffer = mem.ToArray(); - for (int py = 0; py < ani.FrameSize.y; py++) Marshal.Copy(buffer, py * ani.FrameSize.x * 4, loc.Scan0 + py * loc.Stride, ani.FrameSize.x * 4); - Bitmap.UnlockBits(loc); - i++; + frame++; } } diff --git a/source/Burntime.BurnGfx/BurnGfxData.cs b/source/Burntime.BurnGfx/BurnGfxData.cs index b146ddc..9164d07 100644 --- a/source/Burntime.BurnGfx/BurnGfxData.cs +++ b/source/Burntime.BurnGfx/BurnGfxData.cs @@ -83,6 +83,9 @@ public void AddPacColorTable(String Pac, ColorTable Table) void InitializeColorTables() { +//#warning OPTIMIZE remove, as it is only needed in development +// _ = PacImageFileReader.Read("film_05.pac"); +// _ = PacImageFileReader.Read("film_10.pac"); } #endregion diff --git a/source/Burntime.BurnGfx/PacImageFileReader.cs b/source/Burntime.BurnGfx/PacImageFileReader.cs index 923a7d4..226bb87 100644 --- a/source/Burntime.BurnGfx/PacImageFileReader.cs +++ b/source/Burntime.BurnGfx/PacImageFileReader.cs @@ -76,7 +76,7 @@ static public PacImageFileReader Read(String filePath) //ByteBuffer b = new ByteBuffer(1, 1, data); //g.Draw(ref b); - // TODO: optimize loading sequence +#warning // TODO: optimize loading sequence g.bytes = new PixelColor[g.width * g.height]; ByteBuffer buffer = new ByteBuffer(g.width, g.height, g.bytes); diff --git a/source/Burntime.Framework/Event/CommandEvent.cs b/source/Burntime.Framework/Event/CommandEvent.cs index 51a0665..336137c 100644 --- a/source/Burntime.Framework/Event/CommandEvent.cs +++ b/source/Burntime.Framework/Event/CommandEvent.cs @@ -67,8 +67,14 @@ public class CommandHandler { CommandMethodInterface method1; CommandIndexMethodInterface method2; + Action _action; int index; + public CommandHandler(Action action) + { + _action = action; + } + public CommandHandler(CommandMethodInterface Method) { method1 = Method; @@ -97,7 +103,9 @@ public static implicit operator CommandHandler(CommandIndexMethodInterface Right public void Execute() { - if (method1 != null) + if (_action is not null) + _action.Invoke(); + else if (method1 != null) method1.Invoke(); else method2.Invoke(index); @@ -105,7 +113,9 @@ public void Execute() public void Execute(int Index) { - if (method1 != null) + if (_action is not null) + _action.Invoke(); + else if (method1 != null) method1.Invoke(); else method2.Invoke(Index); diff --git a/source/Burntime.Framework/Event/MouseClickEvent.cs b/source/Burntime.Framework/Event/MouseClickEvent.cs index 108aacc..0aecbf1 100644 --- a/source/Burntime.Framework/Event/MouseClickEvent.cs +++ b/source/Burntime.Framework/Event/MouseClickEvent.cs @@ -6,6 +6,8 @@ namespace Burntime.Framework.Event { + public delegate void ClickHandler(Vector2 position, MouseButton button); + public class MouseClickEvent : List { public static MouseClickEvent operator +(MouseClickEvent Left, MouseClickHandler Right) diff --git a/source/Burntime.Framework/GUI/Button.cs b/source/Burntime.Framework/GUI/Button.cs index 43cffaa..7f8fdb1 100644 --- a/source/Burntime.Framework/GUI/Button.cs +++ b/source/Burntime.Framework/GUI/Button.cs @@ -1,192 +1,197 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using Burntime.Platform; +using Burntime.Platform; using Burntime.Platform.Graphics; -using Burntime.Framework.Event; +using System; -namespace Burntime.Framework.GUI -{ - public class Button : Window - { - public VerticalTextAlignment TextVerticalAlign = VerticalTextAlignment.Center; - public TextAlignment TextHorizontalAlign = TextAlignment.Center; +namespace Burntime.Framework.GUI; - protected bool isDown = false; - protected bool isHover; - - public bool IsHover - { - get { return isHover; } - } +public class Button : Window +{ + public VerticalTextAlignment TextVerticalAlign = VerticalTextAlignment.Default; + public TextAlignment TextHorizontalAlign = TextAlignment.Default; - bool sizeSet = false; + public bool IsHover { get; private set; } + public bool IsEnabled { get; set; } = true; - public CommandEvent Command; + public CommandEvent? Command; - public GuiImage Image { get; set; } - public GuiImage HoverImage { get; set; } - public GuiImage DownImage { get; set; } + public GuiImage? Image { get; set; } + public GuiImage? HoverImage { get; set; } + public GuiImage? DownImage { get; set; } - public GuiString Text - { - get { return text_; } - set - { - text_ = value; - if (isTextOnly_) RefreshTextSize_(); - } - } - GuiString text_; + public GuiFont? HoverFont { get; set; } + public GuiFont? DisabledFont { get; set; } + public GuiString? ToolTipText { get; set; } + public GuiFont? ToolTipFont { get; set; } - public GuiFont Font - { - get { return font_; } - set - { - font_ = value; - if (isTextOnly_) RefreshTextSize_(); - } - } - GuiFont font_; + public GuiString? Text + { + get => _text; + set { if (_text?.ID != value?.ID) { _text = value; if (_isTextOnly) RefreshTextSize(); } } + } + GuiString? _text; - public GuiFont HoverFont { get; set; } + public GuiFont? Font + { + get => _font; + set { _font = value; if (_isTextOnly) RefreshTextSize(); } + } + GuiFont? _font; - public GuiString ToolTipText { get; set; } - public GuiFont ToolTipFont { get; set; } + [Obsolete("use IsTextOnly instead")] + public void SetTextOnly() => IsTextOnly = true; - public bool IsTextOnly + public bool IsTextOnly + { + get => _isTextOnly; + set { - get { return isTextOnly_; } - set + if (value) { - if (value) - { - RefreshTextSize_(); + RefreshTextSize(); + if (TextVerticalAlign == VerticalTextAlignment.Default) TextVerticalAlign = VerticalTextAlignment.Top; + if (TextHorizontalAlign == TextAlignment.Default) TextHorizontalAlign = TextAlignment.Left; - } - isTextOnly_ = value; } + _isTextOnly = value; } - private bool isTextOnly_; + } + private bool _isTextOnly; - private void RefreshTextSize_() + void RefreshTextSize() + { + if (Font != null && Text != null) { - if (Font != null && Text != null) - { - Size = Font.GetRect(0, 0, Text).Size; - sizeSet = true; - } + Size = Font.GetRect(0, 0, Text).Size; + sizeSet = true; } + } + bool sizeSet = false; - public void SetTextOnly() + protected bool _isDown = false; + + public Button(Module app, Action? command = null) + : base(app) + { + if (command is not null) { - IsTextOnly = true; + Command = new CommandEvent(); + Command += new CommandHandler(command); } + } - public Button(Module App) - : base(App) + private string _lastLanguage = string.Empty; + public override void OnRender(RenderTarget Target) + { + if (IsTextOnly && _lastLanguage != app.Language) { + RefreshTextSize(); + _lastLanguage = app.Language; } - public override void OnRender(RenderTarget Target) + if (!sizeSet) { - if (!sizeSet) + if (Image != null && Image.IsLoaded) { - if (Image != null && Image.IsLoaded) - { - sizeSet = true; - Size = new Vector2(Image.Width, Image.Height); - } - else if (HoverImage != null && HoverImage.IsLoaded) - { - sizeSet = true; - Size = new Vector2(HoverImage.Width, HoverImage.Height); - } - else if (DownImage != null && DownImage.IsLoaded) - { - sizeSet = true; - Size = new Vector2(DownImage.Width, DownImage.Height); - } - } - - // preload hover and down images - if (HoverImage != null) - HoverImage.Touch(); - if (DownImage != null) - DownImage.Touch(); - - if (isHover && HoverImage != null) - { - Target.DrawSprite(HoverImage); - } - else if (isDown) - { - Target.DrawSprite(DownImage); + sizeSet = true; + Size = new Vector2(Image.Width, Image.Height); } - else + else if (HoverImage != null && HoverImage.IsLoaded) { - Target.DrawSprite(Image); + sizeSet = true; + Size = new Vector2(HoverImage.Width, HoverImage.Height); } - - if (Text != null && Font != null) + else if (DownImage != null && DownImage.IsLoaded) { - Vector2 textpos; - if (TextHorizontalAlign == TextAlignment.Left) - textpos.x = 0; - else if (TextHorizontalAlign == TextAlignment.Center) - textpos.x = Size.x / 2; - else - textpos.x = Size.x; - if (TextVerticalAlign == VerticalTextAlignment.Top) - textpos.y = 0; - else if (TextVerticalAlign == VerticalTextAlignment.Center) - textpos.y = Size.y / 2; - else - textpos.y = Size.y; - - if (isHover && HoverFont != null) - HoverFont.DrawText(Target, textpos, Text, TextHorizontalAlign, TextVerticalAlign); - else - Font.DrawText(Target, textpos, Text, TextHorizontalAlign, TextVerticalAlign); + sizeSet = true; + Size = new Vector2(DownImage.Width, DownImage.Height); } + } - if (IsHover && ToolTipText is not null && ToolTipFont is not null) - { - Vector2 textpos; - textpos.x = Size.x / 2; - textpos.y = -2; + // preload hover and down images + HoverImage?.Touch(); + DownImage?.Touch(); - ToolTipFont.DrawText(Target, textpos, ToolTipText, TextAlignment.Center, VerticalTextAlignment.Bottom); - } + if (IsHover && HoverImage != null) + { + Target.DrawSprite(HoverImage); } - - public override void OnMouseEnter() + else if (_isDown) { - isHover = true; + Target.DrawSprite(DownImage); } - - public override void OnMouseLeave() + else { - isHover = false; + Target.DrawSprite(Image); } - public override bool OnMouseClick(Vector2 Position, MouseButton Button) + if (Text != null && Font != null) { - return OnButtonClick(); + Vector2 textpos; + if (TextHorizontalAlign == TextAlignment.Left) + textpos.x = 0; + else if (TextHorizontalAlign == TextAlignment.Center + || TextHorizontalAlign == TextAlignment.Default) + textpos.x = Size.x / 2; + else + textpos.x = Size.x; + if (TextVerticalAlign == VerticalTextAlignment.Top) + textpos.y = 0; + else if (TextVerticalAlign == VerticalTextAlignment.Center + || TextVerticalAlign == VerticalTextAlignment.Default) + textpos.y = Size.y / 2; + else + textpos.y = Size.y; + + GuiFont font; + if (!IsEnabled && DisabledFont is not null) + font = DisabledFont; + else if (IsEnabled && IsHover && HoverFont is not null) + font = HoverFont; + else + font = Font; + font.DrawText(Target, textpos, Text, TextHorizontalAlign, TextVerticalAlign); } - public virtual bool OnButtonClick() + if (IsHover && ToolTipText is not null && ToolTipFont is not null) { - if (Command != null) - { - Command.Execute(); - return false; - } + Vector2 textpos; + textpos.x = Size.x / 2; + textpos.y = -2; + ToolTipFont.DrawText(Target, textpos, ToolTipText, TextAlignment.Center, VerticalTextAlignment.Bottom); + } + } + + public override void OnMouseEnter() + { + if (!IsEnabled) return; + + IsHover = true; + } + + public override void OnMouseLeave() + { + if (!IsEnabled) return; + + IsHover = false; + } + + public override bool OnMouseClick(Vector2 Position, MouseButton Button) + { + if (!IsEnabled) return true; + + return OnButtonClick(); + } + + public virtual bool OnButtonClick() + { + if (Command != null) + { + Command.Execute(); return false; } + + return false; } } diff --git a/source/Burntime.Framework/GUI/Container.cs b/source/Burntime.Framework/GUI/Container.cs index e8fdfbf..5e13f43 100644 --- a/source/Burntime.Framework/GUI/Container.cs +++ b/source/Burntime.Framework/GUI/Container.cs @@ -70,19 +70,15 @@ public Window[] GetGroup(int Group) public class Container : Window { - bool sizeSet = false; - - protected GuiImage background; - public GuiImage Background + protected GuiImage? background; + public GuiImage? Background { - get { return background; } - set - { + get => background; + set + { background = value; - if (background != null) - { - Size = new Vector2(background.Width, background.Height); - } + if (background is not null) + base.Size = background.Size; } } @@ -106,51 +102,58 @@ public Container(Module App) windows.Layer = layer; } + bool _autoSize = true; + public override Vector2 Size + { + get => base.Size; + set { base.Size = value; _autoSize = value == Vector2.Zero; } + } + internal override void Render(RenderTarget Target) { if (!visible) return; + Target.Layer = Layer; base.Render(Target); + Target.Layer = Layer; RenderTarget thisTarget = Target.GetSubBuffer(Boundings); if (background != null) { - if (!sizeSet) - { - if (background.IsLoaded) - { - Size = new Vector2(background.Width, background.Height); - sizeSet = true; - } - } - thisTarget.DrawSprite(background); + if (_autoSize && background.IsLoaded) + base.Size = background.Size; + + if (Size.x != 0) + thisTarget.DrawSprite((Size - background.Size) / 2, background); + else + thisTarget.DrawSprite(background); } lock (windows) { foreach (Window window in windows) { - window.Render(thisTarget); + if (window.IsVisible) + window.Render(thisTarget); } } } - internal override void Update(float Elapsed) + internal override void Update(float elapsed) { if (!visible) return; - if (background != null) - background.Update(Elapsed); + background?.Update(elapsed); foreach (Window window in windows) { - window.Update(Elapsed); + window.Update(elapsed); } - base.Update(Elapsed); + base.Update(elapsed); } internal override bool MouseClick(Vector2 Position, MouseButton Button) @@ -160,7 +163,7 @@ internal override bool MouseClick(Vector2 Position, MouseButton Button) foreach (Window window in windows) { - if (window.MouseClick(Position - this.Position, Button)) + if (window.IsVisible && window.MouseClick(Position - this.Position, Button)) return true; } @@ -173,9 +176,7 @@ internal override bool MouseMove(Vector2 Position) return false; foreach (Window window in windows) - { window.MouseMove(Position - this.Position); - } return base.MouseMove(Position); } @@ -186,6 +187,34 @@ internal override void ModalLeave() window.ModalLeave(); } + internal override bool MouseDown(Vector2 position, MouseButton button) + { + if (!visible) + return false; + + foreach (Window window in windows) + { + if (window.IsVisible && window.MouseDown(position - Position, button)) + return true; + } + + return base.MouseDown(position, button); + } + + internal override bool MouseUp(Vector2 position, MouseButton button) + { + if (!visible) + return false; + + foreach (Window window in windows) + { + if (window.IsVisible && window.MouseUp(position - Position, button)) + return true; + } + + return base.MouseUp(position, button); + } + internal override void KeyPress(char Key) { if (!visible) @@ -193,7 +222,8 @@ internal override void KeyPress(char Key) foreach (Window window in windows) { - window.KeyPress(Key); + if (window.IsVisible) + window.KeyPress(Key); } base.KeyPress(Key); diff --git a/source/Burntime.Framework/GUI/GuiString.cs b/source/Burntime.Framework/GUI/GuiString.cs index d582262..6c8d994 100644 --- a/source/Burntime.Framework/GUI/GuiString.cs +++ b/source/Burntime.Framework/GUI/GuiString.cs @@ -1,33 +1,31 @@ -using System; +using System.Collections.Generic; -using Burntime.Platform; -using Burntime.Platform.Graphics; -using Burntime.Platform.Resource; +namespace Burntime.Framework.GUI; -namespace Burntime.Framework.GUI +public class GuiString { - public class GuiString - { - protected string str; + public string ID { get; private set; } + protected Dictionary _text; - public static implicit operator GuiString(string id) - { - if (id?.StartsWith("@") == true) - return new GuiString(Module.Instance.ResourceManager.GetString(id.Substring(1))); - return new GuiString(id); - } + public static implicit operator GuiString(string id) => new GuiString(id); - public GuiString(string str) + public GuiString(string? str) + { + _text = new Dictionary(); + ID = str ?? ""; + } + + public static implicit operator string(GuiString right) + { + if (!right._text.TryGetValue(Module.Instance.Language, out string? text)) { - if (str?.StartsWith("@") == true) - this.str = Module.Instance.ResourceManager.GetString(str.Substring(1)); + if (right.ID.StartsWith("@") == true) + text = Module.Instance.ResourceManager.GetString(right.ID[1..]); else - this.str = str; + text = right.ID; + right._text[Module.Instance.Language] = text; } - public static implicit operator string(GuiString right) - { - return right.str; - } + return text; } } diff --git a/source/Burntime.Framework/GUI/Radio.cs b/source/Burntime.Framework/GUI/Radio.cs index 65bd582..834ef8a 100644 --- a/source/Burntime.Framework/GUI/Radio.cs +++ b/source/Burntime.Framework/GUI/Radio.cs @@ -65,11 +65,11 @@ public override bool OnButtonClick() { if (Mode == RadioMode.Normal) { - isDown = true; + _isDown = true; } else if (Mode == RadioMode.Round) { - if (isDown || parent == null) + if (_isDown || parent == null) return true; bool found = false; diff --git a/source/Burntime.Framework/GUI/Switch.cs b/source/Burntime.Framework/GUI/Switch.cs index 25b0f07..468187c 100644 --- a/source/Burntime.Framework/GUI/Switch.cs +++ b/source/Burntime.Framework/GUI/Switch.cs @@ -18,11 +18,11 @@ public Switch(Module App) public bool IsDown { - get { return isDown; } + get { return _isDown; } set { - isDown = value; - if (isDown) + _isDown = value; + if (_isDown) OnSwitchDown(); else OnSwitchUp(); @@ -31,8 +31,8 @@ public bool IsDown public override bool OnMouseClick(Burntime.Platform.Vector2 Position, Burntime.Platform.MouseButton Button) { - isDown = !isDown; - if (isDown) + _isDown = !_isDown; + if (_isDown) OnSwitchDown(); else OnSwitchUp(); diff --git a/source/Burntime.Framework/GUI/Window.cs b/source/Burntime.Framework/GUI/Window.cs index 2fe671a..a7e29dd 100644 --- a/source/Burntime.Framework/GUI/Window.cs +++ b/source/Burntime.Framework/GUI/Window.cs @@ -1,298 +1,322 @@ -using System; +using Burntime.Platform; +using Burntime.Platform.Graphics; +using System; using System.Collections.Generic; using System.Text; -using Burntime.Platform; -using Burntime.Platform.Graphics; +namespace Burntime.Framework.GUI; + +public enum PositionAlignment +{ + Left, + Right, + Center +} -namespace Burntime.Framework.GUI +public class Window { - public enum PositionAlignment + internal Container parent; + public Container Parent { - Left, - Right, - Center + get { return parent; } } - public class Window + public Vector2 PositionOnScreen { - internal Container parent; - public Container Parent + get { - get { return parent; } + if (parent == null) + return Position; + else + return Position + parent.PositionOnScreen; } + } - public Vector2 PositionOnScreen - { - get - { - if (parent == null) - return Position; - else - return Position + parent.PositionOnScreen; - } - } + protected Module app; + + public event EventHandler WindowShow; + public event EventHandler WindowHide; + + // attributes + protected bool visible = true; + //bool active = false; + bool modal; + bool hasFocus = false; + Vector2 pos = new Vector2(); + Vector2 size = new Vector2(); + PositionAlignment vertAlign; + PositionAlignment horizAlign; + bool isEntered = false; + bool captureAllMouseMove = false; + bool captureAllMouseClicks = false; + + public bool CaptureAllMouseMove + { + get { return captureAllMouseMove; } + set { captureAllMouseMove = value; } + } - protected Module app; - - public event EventHandler WindowShow; - public event EventHandler WindowHide; - - // attributes - protected bool visible = true; - //bool active = false; - bool modal; - bool hasFocus = false; - Vector2 pos = new Vector2(); - Vector2 size = new Vector2(); - PositionAlignment vertAlign; - PositionAlignment horizAlign; - bool isEntered = false; - bool captureAllMouseMove = false; - bool captureAllMouseClicks = false; - - public bool CaptureAllMouseMove - { - get { return captureAllMouseMove; } - set { captureAllMouseMove = value; } - } + public bool CaptureAllMouseClicks + { + get { return captureAllMouseClicks; } + set { captureAllMouseClicks = value; } + } - public bool CaptureAllMouseClicks - { - get { return captureAllMouseClicks; } - set { captureAllMouseClicks = value; } - } + public bool HasFocus + { + get { return hasFocus; } + set { hasFocus = value; } + } - public bool HasFocus - { - get { return hasFocus; } - set { hasFocus = value; } - } + public PositionAlignment VerticalAlignment + { + get { return vertAlign; } + set { vertAlign = value; RefreshBoundings(); } + } - public PositionAlignment VerticalAlignment - { - get { return vertAlign; } - set { vertAlign = value; RefreshBoundings(); } - } + public PositionAlignment HorizontalAlignment + { + get { return horizAlign; } + set { horizAlign = value; RefreshBoundings(); } + } - public PositionAlignment HorizontalAlignment - { - get { return horizAlign; } - set { horizAlign = value; RefreshBoundings(); } - } + private int group = 0; + public int Group + { + get { return group; } + set { group = value; } + } - private int group = 0; - public int Group - { - get { return group; } - set { group = value; } - } + protected int layer = 0; + public virtual int Layer + { + get { return layer; } + set { layer = value; } + } - protected int layer = 0; - public virtual int Layer + public Vector2 Position + { + get { return pos; } + set { - get { return layer; } - set { layer = value; } + pos = value; + RefreshBoundings(); } + } - public Vector2 Position + public virtual Vector2 Size + { + get { return size; } + set { - get { return pos; } - set - { - pos = value; - RefreshBoundings(); - } + size = value; + RefreshBoundings(); } + } - public Vector2 Size - { - get { return size; } - set - { - size = value; - RefreshBoundings(); - } - } + public bool IsVisible + { + get { return visible; } + set { if (value) Show(); else Hide(); } + } - public bool IsVisible - { - get { return visible; } - set { if (value) Show(); else Hide(); } - } + public bool IsModal + { + get { return modal; } + set { Hide(); modal = value; } + } - public bool IsModal - { - get { return modal; } - set { Hide(); modal = value; } - } + // public attributes + Rect boundings = new Rect(0, 0, 0, 0); - // public attributes - Rect boundings = new Rect(0, 0, 0, 0); - - void RefreshBoundings() - { - boundings.Left = pos.x; - if (horizAlign == PositionAlignment.Center) - boundings.Left -= size.x / 2; - else if (horizAlign == PositionAlignment.Right) - boundings.Left -= size.x; - boundings.Right = boundings.Left + size.x; - boundings.Top = pos.y; - if (vertAlign == PositionAlignment.Center) - boundings.Top -= size.y / 2; - else if (vertAlign == PositionAlignment.Right) - boundings.Top -= size.y; - boundings.Bottom = boundings.Top + size.y; - } + void RefreshBoundings() + { + boundings.Left = pos.x; + if (horizAlign == PositionAlignment.Center) + boundings.Left -= size.x / 2; + else if (horizAlign == PositionAlignment.Right) + boundings.Left -= size.x; + boundings.Right = boundings.Left + size.x; + boundings.Top = pos.y; + if (vertAlign == PositionAlignment.Center) + boundings.Top -= size.y / 2; + else if (vertAlign == PositionAlignment.Right) + boundings.Top -= size.y; + boundings.Bottom = boundings.Top + size.y; + } - public Rect Boundings - { - get { return boundings; } - //set { pos = value.Position; size = value.Size; } - } + public Rect Boundings + { + get { return boundings; } + //set { pos = value.Position; size = value.Size; } + } - public Window(Module App) - { - app = App; - } + public Window(Module App) + { + app = App; + } - public void Show() - { - if (!visible && modal) - app.SceneManager.PushModalStack(this); - visible = true; + public void Show() + { + if (!visible && modal) + app.SceneManager.PushModalStack(this); + visible = true; - OnShow(); - if (WindowShow != null) - WindowShow.Invoke(this, new EventArgs()); - } + OnShow(); + if (WindowShow != null) + WindowShow.Invoke(this, new EventArgs()); + } - public void Hide() - { - if (visible && modal) - app.SceneManager.PopModalStack(); - visible = false; + public void Hide() + { + if (visible && modal) + app.SceneManager.PopModalStack(); + visible = false; - OnHide(); - if (WindowHide != null) - WindowHide.Invoke(this, new EventArgs()); - } + OnHide(); + if (WindowHide != null) + WindowHide.Invoke(this, new EventArgs()); + } - public void MoveInside(Rect Rectangle) - { - pos.Min(Rectangle.Position); - pos.Max(Rectangle.Position + Rectangle.Size - Size); - RefreshBoundings(); - } + public void MoveInside(Rect rectangle) + { + pos.Min(rectangle.Position); + pos.Max(rectangle.Position + rectangle.Size - Size); + RefreshBoundings(); + } - // internal message handling - internal virtual bool MouseClick(Vector2 Position, MouseButton Button) + // internal message handling + internal virtual bool MouseClick(Vector2 position, MouseButton button) + { + if (!visible) + return false; + + if (Boundings.PointInside(position)) + return OnMouseClick(position - this.Position, button); + else if (captureAllMouseClicks) + return OnMouseClick(position - this.Position, button); + + return false; + } + + internal virtual void ModalLeave() + { + if (isEntered) { - if (!visible) - return false; + OnMouseLeave(); + isEntered = false; + } + } - if (Boundings.PointInside(Position)) - return OnMouseClick(Position - this.Position, Button); - else if (captureAllMouseClicks) - return OnMouseClick(Position - this.Position, Button); + internal virtual bool MouseMove(Vector2 position) + { + if (!visible) return false; - } - internal virtual void ModalLeave() + if (Boundings.PointInside(position)) { - if (isEntered) + if (!isEntered) { - OnMouseLeave(); - isEntered = false; + OnMouseEnter(); + isEntered = true; } - } - internal virtual bool MouseMove(Vector2 position) + OnMouseMove(position - this.Position); + } + else if (isEntered) { - if (!visible) - return false; + OnMouseLeave(); + isEntered = false; + } - if (Boundings.PointInside(position)) - { - if (!isEntered) - { - OnMouseEnter(); - isEntered = true; - } + if (captureAllMouseMove && !isEntered) + OnMouseMove(position - this.Position); - OnMouseMove(position - this.Position); - } - else if (isEntered) - { - OnMouseLeave(); - isEntered = false; - } + return true; + } - if (captureAllMouseMove && !isEntered) - OnMouseMove(position - this.Position); + internal virtual bool MouseDown(Vector2 position, MouseButton button) + { + if (!visible) + return false; - return true; - } + if (Boundings.PointInside(position)) + return OnMouseDown(position - this.Position, button); + else if (captureAllMouseClicks) + return OnMouseDown(position - this.Position, button); - internal virtual bool MouseDown(Vector2 Position, MouseButton Button) { return true; } - internal virtual bool MouseUp(Vector2 Position, MouseButton Button) { return true; } - internal virtual void KeyPress(char Key) - { - if (hasFocus) - OnKeyPress(Key); - } + return false; + } - internal virtual void VKeyPress(SystemKey key) - { - if (hasFocus) - OnVKeyPress(key); - } + internal virtual bool MouseUp(Vector2 position, MouseButton button) + { + if (!visible) + return false; - internal virtual void Render(RenderTarget Target) - { - if (!visible) - return; + if (Boundings.PointInside(position)) + return OnMouseUp(position - this.Position, button); + else if (captureAllMouseClicks) + return OnMouseUp(position - this.Position, button); - Target.Layer = layer; - OnRender(Target.GetSubBuffer(Boundings)); - } + return false; + } - internal virtual void Update(float Elapsed) - { - if (!visible) - return; + internal virtual void KeyPress(char key) + { + if (hasFocus) + OnKeyPress(key); + } - OnUpdate(Elapsed); - } + internal virtual void VKeyPress(SystemKey key) + { + if (hasFocus) + OnVKeyPress(key); + } - internal Rect LocalToGlobal(Rect rc) - { - if (parent == null) - return rc; - return parent.LocalToGlobal(rc + pos); - } + internal virtual void Render(RenderTarget target) + { + if (!visible) + return; - // message handling - // input - public virtual bool OnMouseClick(Vector2 Position, MouseButton Button) { return false; } - public virtual bool OnMouseMove(Vector2 Position) { return false; } - public virtual void OnMouseEnter() { } - public virtual void OnMouseLeave() { } - public virtual bool OnMouseDown(Vector2 Position, MouseButton Button) { return false; } - public virtual bool OnMouseUp(Vector2 Position, MouseButton Button) { return false; } - public virtual bool OnKeyPress(char Key) { return false; } - public virtual bool OnVKeyPress(SystemKey key) { return false; } - // state - public virtual void OnActivate() { } - public virtual void OnShow() { } - public virtual void OnHide() { } - public virtual void OnInactivate() { } - // frame update - public virtual void OnUpdate(float Elapsed) { } - // render - public virtual void OnRender(RenderTarget Target) { } - - public virtual void OnResizeScreen() { } + target.Layer = layer; + OnRender(target.GetSubBuffer(Boundings)); + target.Layer = layer; } + + internal virtual void Update(float elapsed) + { + if (!visible) + return; + + OnUpdate(elapsed); + } + + internal Rect LocalToGlobal(Rect rc) + { + if (parent == null) + return rc; + return parent.LocalToGlobal(rc + pos); + } + + // message handling + // input + public virtual bool OnMouseClick(Vector2 position, MouseButton button) { return false; } + public virtual bool OnMouseMove(Vector2 position) { return false; } + public virtual void OnMouseEnter() { } + public virtual void OnMouseLeave() { } + public virtual bool OnMouseDown(Vector2 position, MouseButton button) { return false; } + public virtual bool OnMouseUp(Vector2 position, MouseButton button) { return false; } + public virtual bool OnKeyPress(char key) { return false; } + public virtual bool OnVKeyPress(SystemKey key) { return false; } + // state + public virtual void OnActivate() { } + public virtual void OnShow() { } + public virtual void OnHide() { } + public virtual void OnInactivate() { } + // frame update + public virtual void OnUpdate(float elapsed) { } + // render + public virtual void OnRender(RenderTarget target) { } + + public virtual void OnResizeScreen() { } } diff --git a/source/Burntime.Framework/Module.cs b/source/Burntime.Framework/Module.cs index 32bebb1..53f11bb 100644 --- a/source/Burntime.Framework/Module.cs +++ b/source/Burntime.Framework/Module.cs @@ -83,6 +83,7 @@ public void Run() protected virtual void OnInitialize() { } protected virtual void OnRun() { } protected virtual void OnClose() { } + protected virtual void OnProcess(float elapsed) { } public virtual void AddProcessor(String Extension, ISpriteProcessor Processor) { @@ -104,6 +105,8 @@ public void AddProcessor(IDataProcessor processor) public virtual String Title { get { return ""; } } //public virtual Vector2[] Resolutions { get { return new Vector2[] { new Vector2(640, 480) }; } } public virtual int MaxVerticalResolution => 480; + public virtual Vector2 MinResolution { get; } = new Vector2(320, 200); + public virtual Vector2 MaxResolution { get; } = new Vector2(320, 200); public virtual Vector2f RatioCorrection => Vector2f.One; public virtual System.Drawing.Icon? Icon => null; @@ -127,6 +130,8 @@ public Nullable MouseBoundings public DeviceManager DeviceManager; public ConfigFile Settings; + public ConfigFile? UserSettings { get; protected set; } + public WorldState GameState { get @@ -145,7 +150,7 @@ public WorldState GameState public virtual void Start() { } - + public void StopGame() { if (Server != null) @@ -171,7 +176,7 @@ public void Render(RenderTarget target) } } - public void Process(float Elapsed) + public void Process(float elapsed) { if (!running) { @@ -182,9 +187,10 @@ public void Process(float Elapsed) else { // process input + OnProcess(elapsed); // process frame - SceneManager.Process(Elapsed); + SceneManager.Process(elapsed); DeviceManager.Clear(); } @@ -204,9 +210,12 @@ public void Close() Engine.ExitApplication(); } - public virtual void ToggleNewGfx() { } - - public virtual bool IsNewGfx { get; set; } = false; + public virtual bool IsNewGfx { get; set; } = true; + public virtual string Language + { + get => FileSystem.LocalizationCode; + set => FileSystem.LocalizationCode = value; + } } public class ApplicationInternal : IApplication @@ -220,6 +229,8 @@ public ApplicationInternal(Module Wrap) public string Title => wrap.Title; public int MaxVerticalResolution => wrap.MaxVerticalResolution; + public Vector2 MinResolution => wrap.MinResolution; + public Vector2 MaxResolution => wrap.MaxResolution; public System.Drawing.Icon? Icon => wrap.Icon; public void Render(RenderTarget Target) diff --git a/source/Burntime.Framework/Scene.cs b/source/Burntime.Framework/Scene.cs index 2935063..429fbfb 100644 --- a/source/Burntime.Framework/Scene.cs +++ b/source/Burntime.Framework/Scene.cs @@ -34,6 +34,9 @@ internal void ActivateScene(object parameter) OnResizeScreen(); OnActivateScene(parameter); + foreach (var window in Windows) + window.OnActivate(); + if (music != null) app.Engine.Music.Play(music); } diff --git a/source/Burntime.Framework/SceneManager.cs b/source/Burntime.Framework/SceneManager.cs index ab5bc8a..09474ff 100644 --- a/source/Burntime.Framework/SceneManager.cs +++ b/source/Burntime.Framework/SceneManager.cs @@ -134,15 +134,18 @@ internal void Process(float Elapsed) // handle clicks foreach (MouseClickInfo click in app.DeviceManager.Mouse.Clicks) { - handle.MouseClick(click.Position - parentPos, click.Button); + if (click.Down) + handle.MouseDown(click.Position - parentPos, click.Button); + else if (!click.Down) + handle.MouseClick(click.Position - parentPos, click.Button); } // handle keys Key[] keys = app.DeviceManager.Keyboard.Keys; foreach (Key key in keys) { - if (key.IsVirtual && key.VirtualKey == SystemKey.F2) - app.ToggleNewGfx(); + if (key.IsVirtual && key.VirtualKey == SystemKey.F8) + app.IsNewGfx = !app.IsNewGfx; else if (key.IsVirtual) handle.VKeyPress(key.VirtualKey); else diff --git a/source/Burntime.Framework/States/StateManager.cs b/source/Burntime.Framework/States/StateManager.cs index 3c16d80..ca00b55 100644 --- a/source/Burntime.Framework/States/StateManager.cs +++ b/source/Burntime.Framework/States/StateManager.cs @@ -654,7 +654,7 @@ public void RemoveNotifycationHandler(ILogicNotifycationHandler handler) handlerList.Remove(handler); } - public void FireNotifycation(ILogicNotifycation notify) + public void Notify(ILogicNotifycation notify) { lock (handlerList) { diff --git a/source/Burntime.MonoGame/Burntime.MonoGame.csproj b/source/Burntime.MonoGame/Burntime.MonoGame.csproj index 159ce20..ec3192d 100644 --- a/source/Burntime.MonoGame/Burntime.MonoGame.csproj +++ b/source/Burntime.MonoGame/Burntime.MonoGame.csproj @@ -18,6 +18,7 @@ Jakob Harder Jakob Harder Burntime + enable diff --git a/source/Burntime.MonoGame/BurntimeGame.cs b/source/Burntime.MonoGame/BurntimeGame.cs index 4c61488..bfc772a 100644 --- a/source/Burntime.MonoGame/BurntimeGame.cs +++ b/source/Burntime.MonoGame/BurntimeGame.cs @@ -20,7 +20,7 @@ public class BurntimeGame : Game, IEngine, ILoadingCounter public Resolution Resolution { get; } = new(); public DeviceManager DeviceManager { get; set; } public ResourceManager ResourceManager { get; set; } - public int Layer { get; set; } + public float Layer { get; set; } public RenderDevice RenderDevice { get; private set; } public RenderTarget MainTarget { get; private set; } @@ -56,11 +56,14 @@ public void DecreaseLoadingCount() public bool IsLoading { get; set; } - // #if (DEBUG) - public bool FullScreen { get; set; } = false; - // #else - // public bool FullScreen { get; set; } = true; - // #endif + bool _isFullscreen = false; + bool _requestFullscreen = false; + public bool IsFullscreen + { + get => _isFullscreen; + set => _requestFullscreen = value; // value will be handled in render thread + } + bool _initialized = false; public BurntimeGame() @@ -91,7 +94,8 @@ protected override void Initialize() _burntimeApp = new(); Resolution.RatioCorrection = _burntimeApp.RatioCorrection; - Resolution.MaxVerticalResolution = _burntimeApp.MaxVerticalResolution; + Resolution.MinResolution = _burntimeApp.MinResolution; + Resolution.MaxResolution = _burntimeApp.MaxResolution; _burntimeApp.Engine = this; _burntimeApp.SceneManager = new SceneManager(_burntimeApp); @@ -124,19 +128,13 @@ private void OnResize(object sender, EventArgs e) ApplyGraphicsDeviceResolution(initialize: false); } - private void ToggleFullscreen() - { - FullScreen = !FullScreen; - ApplyGraphicsDeviceResolution(initialize: false, resetWindowSize: true); - } - bool _resizing = false; private void ApplyGraphicsDeviceResolution(bool initialize, bool resetWindowSize = false) { if ((!_initialized && !initialize) || _resizing) return; _resizing = true; - if (FullScreen) + if (IsFullscreen) { Resolution.Native = new Platform.Vector2(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height); @@ -152,6 +150,7 @@ private void ApplyGraphicsDeviceResolution(bool initialize, bool resetWindowSize { Resolution.Native = new Platform.Vector2(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height) / 2; + //Resolution.Native = new Platform.Vector2(2560, 1440); } else { @@ -209,15 +208,28 @@ private void HandleMouseInput() _rightClicked = false; DeviceManager.MouseLeave(); } - else + //else { + DeviceManager.IsRightDown = mouseState.RightButton == ButtonState.Pressed; + var mousePosition = new Vector2f(mouseState.X, mouseState.Y) * (Vector2f)Resolution.Game / (Vector2f)Resolution.Native; DeviceManager.MouseMove(mousePosition); + // ignore clicks when not shown and active + if (!IsActive) return; + if (mouseState.LeftButton == ButtonState.Pressed) + { + if (!_leftClicked) + DeviceManager.MouseDown(mousePosition, MouseButton.Left); _leftClicked = true; + } if (mouseState.RightButton == ButtonState.Pressed) + { + if (!_rightClicked) + DeviceManager.MouseDown(mousePosition, MouseButton.Right); _rightClicked = true; + } if (_leftClicked && mouseState.LeftButton == ButtonState.Released) { @@ -254,7 +266,7 @@ static char ConvertKeyToChar(Microsoft.Xna.Framework.Input.Keys key, Microsoft.X private void Window_TextInput(object sender, TextInputEventArgs e) { if (e.Key == Keys.Escape || e.Key == Keys.Pause || e.Key == Keys.Enter - || e.Key == Keys.F1 || e.Key == Keys.F2 || e.Key == Keys.F3 || e.Key == Keys.F4) + || e.Key == Keys.F1 || e.Key == Keys.F2 || e.Key == Keys.F3 || e.Key == Keys.F4 || e.Key == Keys.F8 || e.Key == Keys.F9) { // handled in Update } @@ -266,6 +278,8 @@ private void Window_TextInput(object sender, TextInputEventArgs e) private void HandleKeyboardInput() { + if (!IsActive) return; + var keyboard = Microsoft.Xna.Framework.Input.Keyboard.GetState(); var keys = keyboard.GetPressedKeys(); @@ -280,13 +294,13 @@ private void HandleKeyboardInput() if (key == Keys.F11 || (key == Keys.Enter && (modifier & ModifierKeys.LeftAlt) == ModifierKeys.LeftAlt)) { - ToggleFullscreen(); + IsFullscreen = !IsFullscreen; DeviceManager.Clear(); break; } if (key == Keys.Escape || key == Keys.Pause || key == Keys.Enter - || key == Keys.F1 || key == Keys.F2 || key == Keys.F3 || key == Keys.F4) + || key == Keys.F1 || key == Keys.F2 || key == Keys.F3 || key == Keys.F4 || key == Keys.F8 || key == Keys.F9) { DeviceManager?.VKeyPress(key switch { @@ -297,6 +311,8 @@ private void HandleKeyboardInput() Keys.F2 => SystemKey.F2, Keys.F3 => SystemKey.F3, Keys.F4 => SystemKey.F4, + Keys.F8 => SystemKey.F8, + Keys.F9 => SystemKey.F9, _ => SystemKey.Other }); } @@ -308,10 +324,13 @@ private void HandleKeyboardInput() protected override void Update(Microsoft.Xna.Framework.GameTime gameTime) { - if (IsActive) + HandleMouseInput(); + HandleKeyboardInput(); + + if (_requestFullscreen != _isFullscreen) { - HandleMouseInput(); - HandleKeyboardInput(); + _isFullscreen = _requestFullscreen; + ApplyGraphicsDeviceResolution(initialize: false, resetWindowSize: true); } RenderDevice.Update(); @@ -339,8 +358,10 @@ void IEngine.CenterMouse() protected override void OnExiting(object sender, EventArgs args) { - Music.StopThread(); base.OnExiting(sender, args); + + Music.StopThread(); + _burntimeApp.Close(); } void IEngine.ExitApplication() @@ -358,6 +379,7 @@ void IEngine.ReloadGraphics() #region render methods const float MAX_LAYERS = 256.0f; + public float MaxLayers => MAX_LAYERS; const float popInSpeed = 16.0f; static float CalcZ(float Layer) => 0.05f + (Layer / MAX_LAYERS) * 0.9f; diff --git a/source/Burntime.MonoGame/Music/MusicPlayback.cs b/source/Burntime.MonoGame/Music/MusicPlayback.cs new file mode 100644 index 0000000..7f227c2 --- /dev/null +++ b/source/Burntime.MonoGame/Music/MusicPlayback.cs @@ -0,0 +1,188 @@ +using Burntime.Platform; +using Burntime.Platform.IO; +using System.Collections.Generic; +using System.Threading; +using static System.Net.Mime.MediaTypeNames; + +namespace Burntime.MonoGame; + +public sealed class MusicPlayback : IMusic +{ + readonly List _playlist = new(); + readonly Dictionary _songMapping = new(); + public ICollection Songlist => _songMapping.Keys; + + Music.LoopableSong? _music; + bool _repeat = false; + Thread? _musicThread; + bool _requestStop; + + public bool Enabled { get; set; } + public string? Playing { get; private set; } + + bool isMuted = true; + public bool IsMuted + { + get => isMuted; + set { isMuted = value; Volume = _volume; } + } + + float _volume = 0; + public float Volume + { + get { if (_music == null) return 0; return _music.Volume; } + set { _volume = value; if (_music != null) _music.Volume = isMuted ? 0 : value; } + } + + public void ClearPlayList() + { + _playlist.Clear(); + Stop(); + } + + public void AddPlayList(string fileName) + { + if (!Enabled) + return; + + _playlist.Add(fileName); + } + + public void LoadSonglist(string fileName) + { + _songMapping.Clear(); + + var songlist = new ConfigFile(); + if (!songlist.Open(fileName)) + return; + + var section = songlist.GetSection(""); + if (section is null) + return; + + foreach (var value in section.Values) + _songMapping[value.Key] = value.Value; + + if (_music is not null && Playing is not null) + { + _playlist.Insert(0, Playing); + _music.Stop(); + } + } + + public void Play(string fileName, bool loop = true) + { + if (!Enabled) + return; + + _playlist.Clear(); + Stop(); + _repeat = loop; + _playlist.Add(fileName); + } + + public void PlayOnce(string fileName) => Play(fileName, false); + + public void Stop() + { + _playlist.Clear(); + + lock (this) + { + if (_music != null) + { + _music.Stop(); + _music.Dispose(); + _music = null; + } + } + } + + public void RunThread() + { + _requestStop = false; + _musicThread = new Thread(new ThreadStart(MusicThread)); + _musicThread.Start(); + } + + public void StopThread() + { + Stop(); + + if (_musicThread != null) + { + _requestStop = true; + _musicThread.Join(); + _musicThread = null; + } + } + + private string? GetNextTitle() + { + if (_playlist.Count == 0) + return null; + + var next = _playlist[0]; + _playlist.RemoveAt(0); + if (_repeat) + _playlist.Add(next); + + Playing = next; + + if (FileSystem.ExistsFile(next)) + return next; + + if (_songMapping.TryGetValue(next, out string? songFilePath)) + return songFilePath; + + return null; + } + + private void MusicThread() + { + int sleep = 0; + + while (!_requestStop) + { + if (sleep > 0) + Thread.Sleep(sleep); + +#warning THREADING lock playlist + lock (this) + { + if (_music == null) + { + sleep = 200; + + string? next = GetNextTitle(); + if (next == null) + continue; + + _music = Music.LoopableSong.FromFileName(next, _repeat); + if (_music is null) + continue; + + _music.Volume = _volume; + _music.Play(); + } + else + { + if (!_music.IsPlaying) + { + Playing = null; + _music.Dispose(); + _music = null; + sleep = 0; + } + else + sleep = 50; + } + } + } + + lock (this) + { + _music?.Dispose(); + } + } +} diff --git a/source/Burntime.MonoGame/Music/OggSoundEffect.cs b/source/Burntime.MonoGame/Music/OggSoundEffect.cs index 23988d8..a61b6f2 100644 --- a/source/Burntime.MonoGame/Music/OggSoundEffect.cs +++ b/source/Burntime.MonoGame/Music/OggSoundEffect.cs @@ -9,17 +9,28 @@ public static class OggSoundEffect { public static SoundEffect FromStream(Stream fileStream) { - using (var reader = new VorbisReader(fileStream, false)) - { - var sampleCount = (int)reader.TotalSamples; - var soundData = new float[sampleCount * reader.Channels]; - int readCount = reader.ReadSamples(soundData, 0, sampleCount * reader.Channels); + using var reader = new VorbisReader(fileStream, false); - var byteData = new byte[sampleCount * 2 * reader.Channels]; - CastBuffer(soundData, byteData, sampleCount * reader.Channels); + var sampleCount = (int)reader.TotalSamples; + var soundData = new float[sampleCount * reader.Channels]; + _ = reader.ReadSamples(soundData, 0, sampleCount * reader.Channels); - return new SoundEffect(byteData, reader.SampleRate, reader.Channels == 2 ? AudioChannels.Stereo : AudioChannels.Mono); - } + var byteData = new byte[sampleCount * 2 * reader.Channels]; + CastBuffer(soundData, byteData, sampleCount * reader.Channels); + + return new SoundEffect(byteData, reader.SampleRate, reader.Channels == 2 ? AudioChannels.Stereo : AudioChannels.Mono); + } + + public static byte[] GetBuffer(this VorbisReader reader) + { + var sampleCount = (int)reader.TotalSamples; + var soundData = new float[sampleCount * reader.Channels]; + _ = reader.ReadSamples(soundData, 0, sampleCount * reader.Channels); + + var byteData = new byte[sampleCount * 2 * reader.Channels]; + CastBuffer(soundData, byteData, sampleCount * reader.Channels); + + return byteData; } static void CastBuffer(float[] inBuffer, byte[] outBuffer, int length) diff --git a/source/Burntime.MonoGame/Music/Song.cs b/source/Burntime.MonoGame/Music/Song.cs new file mode 100644 index 0000000..0d2d02b --- /dev/null +++ b/source/Burntime.MonoGame/Music/Song.cs @@ -0,0 +1,96 @@ +using Burntime.Platform; +using Burntime.Platform.IO; +using Microsoft.Xna.Framework.Audio; +using NVorbis; +using System; + +namespace Burntime.MonoGame.Music; + +internal class LoopableSong : IDisposable +{ + public static LoopableSong? FromFileName(string fileName, bool repeat = false) + { + string? intro = null; + string loop = fileName; + + if (fileName.Contains(':')) + { + var split = loop.Split(':'); + loop = split[1]; + intro = split[0]; + } + + var loopFile = FileSystem.GetFile(loop); + if (loopFile is null) + return null; + + if (intro is not null && repeat) + return new LoopableSong(loopFile, FileSystem.GetFile(intro)); + + return new LoopableSong(loopFile, null, repeat); + } + + readonly byte[]? _loopBuffer; + SoundEffect? _effect; + SoundEffectInstance? _music; + + public LoopableSong(File loop, File? intro = null, bool repeat = false) + { + if (intro is not null) + { + using var ogg = new VorbisReader((intro ?? loop).Stream, false); + var music = new DynamicSoundEffectInstance(ogg.SampleRate, ogg.Channels == 2 ? AudioChannels.Stereo : AudioChannels.Mono); + music.SubmitBuffer(ogg.GetBuffer()); + + if (intro is not null) + { + using var introOgg = new VorbisReader(loop.Stream, false); + + if (introOgg.SampleRate != ogg.SampleRate || introOgg.Channels != ogg.Channels) + return; + + _loopBuffer = introOgg.GetBuffer(); + music.BufferNeeded += BufferNeeded; + } + + _music = music; + } + else + { + _effect = OggSoundEffect.FromStream(loop.Stream); + _music = _effect.CreateInstance(); + _music.IsLooped = repeat; + } + } + + private void BufferNeeded(object? sender, EventArgs e) + { + if (_loopBuffer is not null && _music is DynamicSoundEffectInstance music) + music.SubmitBuffer(_loopBuffer); + } + + public void Dispose() + { + if (_music is not null) + { + _music.Stop(); + if (_loopBuffer is not null && _music is DynamicSoundEffectInstance music) + music.BufferNeeded -= BufferNeeded; + _music.Dispose(); + _music = null; + } + _effect?.Dispose(); + _effect = null; + } + + public void Play() => _music?.Play(); + public void Stop() => _music?.Stop(); + + public float Volume + { + get => _music?.Volume ?? 0; + set { if (_music is not null) _music.Volume = value; } + } + + public bool IsPlaying => _music?.State == SoundState.Playing; +} diff --git a/source/Burntime.MonoGame/MusicPlayback.cs b/source/Burntime.MonoGame/MusicPlayback.cs deleted file mode 100644 index 87869fa..0000000 --- a/source/Burntime.MonoGame/MusicPlayback.cs +++ /dev/null @@ -1,181 +0,0 @@ -using Burntime.Platform; -using Microsoft.Xna.Framework.Audio; -using Microsoft.Xna.Framework.Media; -using System.Collections.Generic; -using System.IO; -using System.Threading; - -namespace Burntime.MonoGame -{ - public sealed class MusicPlayback : IMusic - { - SoundEffect _effect; - SoundEffectInstance _music; - List playList = new List(); - Thread musicThread; - - bool stop; - bool enabled; - bool playOnce; - - public bool Enabled - { - get { return enabled; } - set { enabled = value; } - } - - bool isMuted = true; - public bool IsMuted - { - get { return isMuted; } - set - { - isMuted = value; - Volume = _volume; - } - } - - float _volume = 0; - public float Volume - { - get { if (_music == null) return 0; return _music.Volume; } - set - { - _volume = value; - if (_music != null) - _music.Volume = isMuted ? 0 : value; - } - } - - public void ClearPlayList() - { - playList.Clear(); - Stop(); - } - - public void AddPlayList(string fileName) - { - if (!enabled) - return; - - playList.Add(fileName); - } - - public void Play(string fileName) - { - if (!enabled) - return; - - playOnce = false; - playList.Clear(); - Stop(); - playList.Add(fileName); - } - - public void PlayOnce(string fileName) - { - if (!enabled) - return; - - playList.Clear(); - Stop(); - playList.Add(fileName); - playOnce = true; - } - - public void Stop() - { - playList.Clear(); - - lock (this) - { - if (_music != null) - { - _music.Stop(); - _music.Dispose(); - _music = null; - } - } - } - - public void RunThread() - { - stop = false; - musicThread = new Thread(new ThreadStart(MusicThread)); - musicThread.Start(); - } - - public void StopThread() - { - Stop(); - - if (musicThread != null) - { - stop = true; - musicThread.Join(); - musicThread = null; - } - } - - private string GetNextTitle() - { - if (playList.Count == 0) - return null; - - string next = playList[0]; - playList.RemoveAt(0); - if (!playOnce) - playList.Add(next); - - return next; - } - - private void MusicThread() - { - int sleep = 0; - - while (!stop) - { - if (sleep > 0) - Thread.Sleep(sleep); - - lock (this) - { - if (_music == null) - { - sleep = 200; - - string next = GetNextTitle(); - if (next == null) - continue; - - _effect = OggSoundEffect.FromStream(Burntime.Platform.IO.FileSystem.GetFile(next).Stream); - _music = _effect.CreateInstance(); - _music.Volume = _volume; - _music.Play(); - } - else - { - if (_music.State == SoundState.Stopped) - { - _music.Dispose(); - _effect.Dispose(); - _music = null; - sleep = 0; - } - else - sleep = 50; - } - } - } - - lock (this) - { - _music?.Dispose(); - _effect?.Dispose(); - } - - //SlimDXOggStreamingSample.DirectSoundWrapper.Device.Dispose(); - } - } -} diff --git a/source/Burntime.Platform/DeviceManager.cs b/source/Burntime.Platform/DeviceManager.cs index 3f0452f..0b04492 100644 --- a/source/Burntime.Platform/DeviceManager.cs +++ b/source/Burntime.Platform/DeviceManager.cs @@ -1,4 +1,5 @@ using Burntime.Platform.Utils; +using System.Net; namespace Burntime.Platform; @@ -11,6 +12,7 @@ public enum MouseButton public struct MouseClickInfo { + public bool Down; public Vector2 Position; public MouseButton Button; } @@ -19,6 +21,7 @@ public interface IMouseDevice { Vector2 Position { get; } Nullable Boundings { get; set; } + bool IsRightDown { get; } /// /// thread-safe @@ -33,7 +36,8 @@ sealed class MouseDevice : IMouseDevice private Vector2 current = Vector2.Zero; private Nullable previous; private List clicks = new List(); - + + public bool IsRightDown { get; set; } public Rect? Boundings { get; set; } public MouseDevice(Resolution resolution) @@ -114,6 +118,8 @@ public enum SystemKey F2 = 2,//System.Windows.Forms.Keys.F2, F3 = 3,//System.Windows.Forms.Keys.F3, F4 = 4,//System.Windows.Forms.Keys.F4, + F8 = 8, + F9 = 9, Escape = 5,//System.Windows.Forms.Keys.Escape, Pause = 6,//System.Windows.Forms.Keys.Pause Enter, @@ -191,6 +197,13 @@ public class DeviceManager private readonly MouseDevice _mouse; public IMouseDevice Mouse => _mouse; +#warning TODO implement proper mouse state + public bool IsRightDown + { + get => _mouse.IsRightDown; + set => _mouse.IsRightDown = value; + } + public Keyboard Keyboard { get; } = new(); private readonly Resolution _resolution; @@ -207,12 +220,23 @@ public void MouseMove(Vector2 Position) _mouse.Position = new Vector2(Position); } - public void MouseClick(Vector2 Position, MouseButton Button) + public void MouseDown(Vector2 position, MouseButton button) + { + _mouse.AddClick(new() + { + Position = new Vector2(position), + Button = button, + Down = true + }); + } + + public void MouseClick(Vector2 position, MouseButton button) { _mouse.AddClick(new() { - Position = new Vector2(Position), - Button = Button + Position = new Vector2(position), + Button = button, + Down = false }); } diff --git a/source/Burntime.Platform/Graphics/Font.cs b/source/Burntime.Platform/Graphics/Font.cs index bdfb958..03ddfd1 100644 --- a/source/Burntime.Platform/Graphics/Font.cs +++ b/source/Burntime.Platform/Graphics/Font.cs @@ -15,14 +15,16 @@ public enum TextAlignment { Left, Center, - Right + Right, + Default } public enum VerticalTextAlignment { Top, Center, - Bottom + Bottom, + Default } public enum TextBorders diff --git a/source/Burntime.Platform/Graphics/RenderTarget.cs b/source/Burntime.Platform/Graphics/RenderTarget.cs index f5d2087..a9e69f3 100644 --- a/source/Burntime.Platform/Graphics/RenderTarget.cs +++ b/source/Burntime.Platform/Graphics/RenderTarget.cs @@ -3,7 +3,7 @@ public class RenderTarget { public Vector2 Offset { get; set; } - public int Layer + public float Layer { get => _engine.Layer; set => _engine.Layer = value; diff --git a/source/Burntime.Platform/IApplication.cs b/source/Burntime.Platform/IApplication.cs index 39cede9..56df3d9 100644 --- a/source/Burntime.Platform/IApplication.cs +++ b/source/Burntime.Platform/IApplication.cs @@ -6,6 +6,8 @@ public interface IApplication { string Title { get; } int MaxVerticalResolution { get; } + Vector2 MinResolution { get; } + Vector2 MaxResolution { get; } void Render(RenderTarget Target); void Process(float Elapsed); diff --git a/source/Burntime.Platform/IEngine.cs b/source/Burntime.Platform/IEngine.cs index 1a82e65..8751a82 100644 --- a/source/Burntime.Platform/IEngine.cs +++ b/source/Burntime.Platform/IEngine.cs @@ -11,7 +11,8 @@ public interface ILoadingCounter public interface IEngine { DeviceManager DeviceManager { get; set; } - int Layer { get; set; } + float Layer { get; set; } + float MaxLayers { get; } BlendOverlayBase BlendOverlay { get; } Resolution Resolution { get; } @@ -20,6 +21,7 @@ public interface IEngine bool MusicBlend { get; set; } bool IsLoading { get; set; } + bool IsFullscreen { get; set; } void CenterMouse(); void ExitApplication(); diff --git a/source/Burntime.Platform/IMusic.cs b/source/Burntime.Platform/IMusic.cs index 13c8be1..b4af480 100644 --- a/source/Burntime.Platform/IMusic.cs +++ b/source/Burntime.Platform/IMusic.cs @@ -5,8 +5,12 @@ public interface IMusic bool Enabled { get; set; } bool IsMuted { get; set; } float Volume { get; set; } + string Playing { get; } - void Play(string song); + void Play(string song, bool loop = true); void PlayOnce(string sound); void Stop(); + + void LoadSonglist(string filePath); + ICollection Songlist { get; } } diff --git a/source/Burntime.Platform/IO/ConfigFile.cs b/source/Burntime.Platform/IO/ConfigFile.cs index c549cc5..734b1a8 100644 --- a/source/Burntime.Platform/IO/ConfigFile.cs +++ b/source/Burntime.Platform/IO/ConfigFile.cs @@ -2,12 +2,12 @@ public class ConfigFile { - Dictionary sections; - List order; + readonly Dictionary sections = new(); + readonly List order = new(); - public bool Open(String name) + public bool Open(string name) { - Burntime.Platform.IO.File file = FileSystem.GetFile(name); + File file = FileSystem.GetFile(name); if (file == null) return false; bool result = Open(file.Stream); @@ -20,14 +20,12 @@ public bool Open(Stream stream) if (stream == null) return false; - sections = new Dictionary(); - order = new List(); + sections.Clear(); + order.Clear(); ConfigSectionTemplate currentTemplate = new ConfigSectionTemplate(); - List lines = new List(); - - StreamReader reader = new StreamReader(stream); - String line; + var reader = new StreamReader(stream); + string? line; while (null != (line = reader.ReadLine())) { ConfigLineTemplate templateLine = new ConfigLineTemplate(line); @@ -61,9 +59,9 @@ public bool Open(Stream stream) return true; } - public bool Save(String name) + public bool Save(string name) { - Burntime.Platform.IO.File file = FileSystem.CreateFile(name); + File file = FileSystem.CreateFile(name); if (file == null) return false; @@ -90,15 +88,25 @@ public bool Save(Stream stream) return true; } - public ConfigSection GetSection(String name) + public ConfigSection GetSection(string name, bool add = false) { if (!sections.ContainsKey(name.ToLower())) - return ConfigSection.NullSection; + { + if (add) + { + var section = new ConfigSection(); + sections.Add(name.ToLower(), section); + order.Add(section); + return section; + } + else + return ConfigSection.NullSection; + } return sections[name.ToLower()]; } - public ConfigSection this[String name] + public ConfigSection this[string name] { get { diff --git a/source/Burntime.Platform/IO/ConfigSection.cs b/source/Burntime.Platform/IO/ConfigSection.cs index 09d18f0..28b7e17 100644 --- a/source/Burntime.Platform/IO/ConfigSection.cs +++ b/source/Burntime.Platform/IO/ConfigSection.cs @@ -13,7 +13,7 @@ public static ConfigSection NullSection } ConfigSectionTemplate template; - Dictionary values; + Dictionary values = new(); public String Name { @@ -22,12 +22,13 @@ public String Name internal ConfigSection() { + template = new ConfigSectionTemplate(); } internal bool Open(ConfigSectionTemplate template) { this.template = template; - values = new Dictionary(); + values.Clear(); foreach (ConfigLineTemplate line in template.Lines) { @@ -170,6 +171,8 @@ public String[] GetStrings(String key) return new string[0]; } + public IEnumerable> Values => values; + public int GetInt(String key) { if (this == NullSection) @@ -181,13 +184,15 @@ public int GetInt(String key) return res; } - public bool GetBool(String key) + public bool GetBool(string key, bool defaultResult = false) { - String str = Get(key); - if (str == null || str == "") - return false; + string? str = Get(key)?.ToLower(); + if (string.IsNullOrWhiteSpace(str)) + return defaultResult; - if (str.ToLower() == "1" || str.ToLower() == "yes" || str.ToLower() == "true" || str.ToLower() == "on") + if (str == "1" || str == "yes" || str == "true" || str == "on") + return true; + if (defaultResult && (str != "0" && str != "no" && str != "false" && str != "off")) return true; return false; diff --git a/source/Burntime.Platform/IO/File.cs b/source/Burntime.Platform/IO/File.cs index e0d8315..970f995 100644 --- a/source/Burntime.Platform/IO/File.cs +++ b/source/Burntime.Platform/IO/File.cs @@ -41,7 +41,7 @@ public Encoding Encoding public static implicit operator Stream(File Right) { - return Right.Stream; + return Right?.Stream; } // access diff --git a/source/Burntime.Platform/IO/FileSystem.cs b/source/Burntime.Platform/IO/FileSystem.cs index eb5f875..d775510 100644 --- a/source/Burntime.Platform/IO/FileSystem.cs +++ b/source/Burntime.Platform/IO/FileSystem.cs @@ -290,29 +290,10 @@ static public File CreateFile(FilePath path) return FileSystem.GetFile(path, FileOpenMode.Write); } - // add virtual file - static public bool AddFile(FilePath path) - { - return vfs.AddFile(path); - } - - // remove virtual file - static public bool RemoveFile(FilePath path) - { - return vfs.RemoveFile(path); - } - - // check if virtual folder is loaded - static public bool IsPackageLoaded(string package) - { - return vfs.ExistsMount(package); - } - - // unload all virtual folders - static public void Clear() - { - vfs.UnmountAll(); - } + static public bool AddFile(FilePath path) => vfs.AddFile(path); + static public bool RemoveFile(FilePath path) => vfs.RemoveFile(path); + static public bool IsPackageLoaded(string package) => vfs.ExistsMount(package); + static public void Clear() => vfs.UnmountAll(); public delegate void ConvertFeedback(float percentage); diff --git a/source/Burntime.Platform/IO/Package.cs b/source/Burntime.Platform/IO/Package.cs index a013056..0b1cc7b 100644 --- a/source/Burntime.Platform/IO/Package.cs +++ b/source/Burntime.Platform/IO/Package.cs @@ -15,6 +15,11 @@ public interface IPackage File GetFile(FilePath filePath, FileOpenMode mode); bool ExistsFile(FilePath filePath); bool AddFile(FilePath filePath); - bool RemoveFile(FilePath filePath); void Close(); + + // not supported by all packages + bool RemoveFile(FilePath filePath) => false; + bool ExistsFolder(FilePath folderPath) => false; + bool RemoveFolder(FilePath folderPath) => false; + bool MoveFolder(FilePath sourcePath, FilePath targetPath) => false; } diff --git a/source/Burntime.Platform/IO/PackageFolder.cs b/source/Burntime.Platform/IO/PackageFolder.cs index 89e78c3..96deed0 100644 --- a/source/Burntime.Platform/IO/PackageFolder.cs +++ b/source/Burntime.Platform/IO/PackageFolder.cs @@ -24,14 +24,14 @@ public PackageFolder(String name, String path, string subPath) this.subPath = subPath; dicFiles = new Dictionary(); - process("", path + "/" + subPath); + ParseFolder("", path + "/" + subPath); } public void Close() { } - void process(String relpath, String path) + void ParseFolder(string relpath, string path) { try { @@ -57,7 +57,7 @@ void process(String relpath, String path) if (name.StartsWith(".")) continue; - process(relpath + name.ToLower() + "/", dir); + ParseFolder(relpath + name.ToLower() + "/", dir); } } @@ -83,6 +83,11 @@ public bool ExistsFile(FilePath filePath) return dicFiles.ContainsKey(filePath.PathWithoutPackage); } + public bool ExistsFolder(FilePath filePath) + { + return System.IO.Directory.Exists(path + "/" + subPath + filePath.PathWithoutPackage); + } + public bool AddFile(FilePath filePath) { if (dicFiles.ContainsKey(filePath.PathWithoutPackage)) @@ -124,4 +129,39 @@ public bool RemoveFile(FilePath filePath) return true; } + + public bool RemoveFolder(FilePath filePath) + { + try + { + System.IO.Directory.Delete(path + "/" + subPath + filePath.PathWithoutPackage, true); + } + catch + { + return false; + } + + dicFiles.Clear(); + ParseFolder("", path + "/" + subPath); + + return true; + } + + public bool MoveFolder(FilePath sourcePath, FilePath targetPath) + { + try + { + Directory.Move(path + "/" + subPath + sourcePath.PathWithoutPackage, path + "/" + subPath + targetPath.PathWithoutPackage); + } + catch + { + return false; + } + + dicFiles.Clear(); + ParseFolder("", path + "/" + subPath); + + return true; + } + } diff --git a/source/Burntime.Platform/IO/PackageSystem.cs b/source/Burntime.Platform/IO/PackageSystem.cs index b992574..6a2fe3c 100644 --- a/source/Burntime.Platform/IO/PackageSystem.cs +++ b/source/Burntime.Platform/IO/PackageSystem.cs @@ -332,24 +332,55 @@ public bool AddFile(FilePath path) /// True if file doesn't exist or has been removed. public bool RemoveFile(FilePath path) { - // always remove files in user path package + // only remove files in user path package if (path.PackageSpecified && path.Package != "user") return false; - if (!ExistsMount("user")) return false; MountInfo mount = nameMountMap["user"]; - - // remove from package if (!mount.Package.ExistsFile(path.PathWithoutPackage)) return false; - if (!mount.Package.RemoveFile(path.PathWithoutPackage)) return false; RefreshFileMountMap(); + return true; + } + + public bool RemoveFolder(FilePath path) + { + if (path.PackageSpecified && path.Package != "user") + return false; + if (!ExistsMount("user")) + return false; + + MountInfo mount = nameMountMap["user"]; + if (!mount.Package.ExistsFolder(path.PathWithoutPackage)) + return false; + if (!mount.Package.RemoveFolder(path.PathWithoutPackage)) + return false; + + RefreshFileMountMap(); + return true; + } + public bool MoveFolder(FilePath sourcePath, FilePath targetPath) + { + if (sourcePath.PackageSpecified && sourcePath.Package != "user") + return false; + if (targetPath.PackageSpecified && targetPath.Package != "user") + return false; + if (!ExistsMount("user")) + return false; + + MountInfo mount = nameMountMap["user"]; + if (!mount.Package.ExistsFolder(sourcePath.PathWithoutPackage)) + return false; + if (!mount.Package.MoveFolder(sourcePath.PathWithoutPackage, targetPath.PathWithoutPackage)) + return false; + + RefreshFileMountMap(); return true; } diff --git a/source/Burntime.Platform/Math/Math.cs b/source/Burntime.Platform/Math/Math.cs index f38e47f..ec84761 100644 --- a/source/Burntime.Platform/Math/Math.cs +++ b/source/Burntime.Platform/Math/Math.cs @@ -11,5 +11,43 @@ static public Random Random { get { return random; } } + + static public int Min(int val1, int val2) + { + return val1 < val2 ? val1 : val2; + } + + static public int Min(int val1, int val2, int val3) + { + int min = (val1 < val2 ? val1 : val2); + return val3 < min ? val3 : min; + } + + static public int Min(params int[] values) + { + int min = values[0]; + foreach (int val in values) + if (val < min) min = val; + return min; + } + + static public int Max(int val1, int val2) + { + return val1 > val2 ? val1 : val2; + } + + static public int Max(int val1, int val2, int val3) + { + int max = (val1 > val2 ? val1 : val2); + return val3 > max ? val3 : max; + } + + static public int Max(params int[] values) + { + int max = values[0]; + foreach (int val in values) + if (val > max) max = val; + return max; + } } } diff --git a/source/Burntime.Platform/Math/Vector2.cs b/source/Burntime.Platform/Math/Vector2.cs index 302753b..7ac5336 100644 --- a/source/Burntime.Platform/Math/Vector2.cs +++ b/source/Burntime.Platform/Math/Vector2.cs @@ -150,6 +150,12 @@ public void Max(int max) if (y > max) y = max; } + public void Max(int maxX, int maxY) + { + if (x > maxX) x = maxX; + if (y > maxY) y = maxY; + } + public void Max(Vector2 max) { if (x > max.x) x = max.x; @@ -302,6 +308,11 @@ public static implicit operator Vector2 (Vector2f left) return new Vector2((int)(left.x + 0.5f), (int)(left.y + 0.5f)); } + public Vector2 Floor() + { + return new Vector2((int)x, (int)y); + } + public static implicit operator Vector2f (float right) { return new Vector2f(right, right); diff --git a/source/Burntime.Platform/Resource/IResourceManager.cs b/source/Burntime.Platform/Resource/IResourceManager.cs index a1fc336..45a6e9c 100644 --- a/source/Burntime.Platform/Resource/IResourceManager.cs +++ b/source/Burntime.Platform/Resource/IResourceManager.cs @@ -36,4 +36,6 @@ public interface IResourceManager void SetResourceReplacement(string file); //LoadingCounter LoadingCounter { get; } + + void ClearText(); } diff --git a/source/Burntime.Platform/Resource/ResourceManagerBase.cs b/source/Burntime.Platform/Resource/ResourceManagerBase.cs index 682a5f1..ede1a6b 100644 --- a/source/Burntime.Platform/Resource/ResourceManagerBase.cs +++ b/source/Burntime.Platform/Resource/ResourceManagerBase.cs @@ -218,6 +218,11 @@ public string GetString(string file, int index) return res.Substring(0, res.Length - 1); return res; } + + public void ClearText() + { + txtDB.Clear(); + } #endregion #region DataProcessor diff --git a/source/Burntime.Platform/Utils/Resolution.cs b/source/Burntime.Platform/Utils/Resolution.cs index 17fc09e..a2e87a3 100644 --- a/source/Burntime.Platform/Utils/Resolution.cs +++ b/source/Burntime.Platform/Utils/Resolution.cs @@ -13,18 +13,29 @@ public Vector2 Native SelectBestGameResolution(); } } - Vector2 _native = new(960, 540); + Vector2 _native = Vector2.Zero; - public int MaxVerticalResolution + public Vector2 MinResolution { - get => _maxVerticalResolution; + get => _minResolution; set { - _maxVerticalResolution = value; + _minResolution = value; SelectBestGameResolution(); } } - int _maxVerticalResolution = 0; + Vector2 _minResolution = Vector2.Zero; + + public Vector2 MaxResolution + { + get => _maxResolution; + set + { + _maxResolution = value; + SelectBestGameResolution(); + } + } + Vector2 _maxResolution = Vector2.Zero; Vector2f _ratioCorrection = Vector2f.One; public Vector2f RatioCorrection @@ -41,28 +52,28 @@ public Vector2f RatioCorrection public Vector2 Game { get; private set; } = new(960, 640); public Vector2 BackBuffer { get; private set; } = new(960, 540); - static bool IsCleanZoom(float zoom) - { - return zoom == 4 || zoom == 3 || zoom == 2 || zoom == 1 || zoom == 0.5f; - } - void SelectBestGameResolution() { - if (_maxVerticalResolution == 0) return; + if (MinResolution.x == 0 || MaxResolution.x == 0 || Native.x == 0) return; + + const int DOUBLED_RESOLUTION = 2; + + Vector2f min = (Vector2f)MinResolution * DOUBLED_RESOLUTION * _ratioCorrection; + Vector2f max = (Vector2f)MaxResolution * DOUBLED_RESOLUTION * _ratioCorrection; - const float DOUBLED_RESOLUTION = 2; - float MaxHeight = _maxVerticalResolution * DOUBLED_RESOLUTION; - int verticalFactor = 1; + Vector2 maxFactor = ((Vector2f)_native / max).Floor(); + Vector2 minFactor = ((Vector2f)_native / min).Floor(); + int verticalFactor = Math.Max(1, maxFactor.y, minFactor.y); + int horizontalFactor = Math.Max(1, maxFactor.x, minFactor.x); - while (_native.y > MaxHeight * verticalFactor) - verticalFactor++; + int factor = Math.Min(verticalFactor, horizontalFactor); - BackBuffer = _native / verticalFactor; + BackBuffer = _native / factor; Scale = DOUBLED_RESOLUTION * _ratioCorrection; - Game = (Vector2)((Vector2f)BackBuffer / Scale); + Game = (Vector2f)BackBuffer / Scale; #warning use render to texture instead and strech that by verticalfactor? - Scale *= verticalFactor; + Scale *= factor; } } diff --git a/source/Burntime.Remaster/Application.cs b/source/Burntime.Remaster/Application.cs index 1c14a23..c2ec627 100644 --- a/source/Burntime.Remaster/Application.cs +++ b/source/Burntime.Remaster/Application.cs @@ -4,6 +4,8 @@ using Burntime.Platform.IO; using System; using System.Text; +using Burntime.Remaster; +using static Burntime.Remaster.BurntimeClassic; namespace Burntime.Remaster { @@ -33,6 +35,11 @@ public class BurntimeClassic : Module //public override Vector2[] Resolutions { get { return new Vector2[] { new Vector2(400, 188), new Vector2(384, 240) }; } } //public override Vector2[] Resolutions { get { return new Vector2[] { new Vector2(640, 300), new Vector2(384, 240) }; } } + // original size + public override Vector2 MinResolution { get; } = new Vector2(320, 200); + //public override Vector2 MinResolution { get; } = new Vector2(352, 220); + public override Vector2 MaxResolution { get; } = new Vector2(680, 320); + public override int MaxVerticalResolution => 320; //public override int MaxVerticalResolution => 370; @@ -41,7 +48,7 @@ public class BurntimeClassic : Module // Burntime's ratio is 8:5. We need to scale height by 1.2 (320x200 where screens today would be multiple of 320x240). // But to get a clean tile resolution of 32x38 use 1.1875 //public override Vector2f RatioCorrection => new(1, 1.0f / 32.0f * 38.0f); - public override Vector2f RatioCorrection => new(1.0f/ 64.0f * 60.0f, 1.0f / 64.0f * 72.0f); + public override Vector2f RatioCorrection => new(1.0f / 64.0f * 60.0f, 1.0f / 64.0f * 72.0f); public override System.Drawing.Icon Icon { @@ -58,48 +65,48 @@ public BurntimeClassic() public override void Start() { - Engine.Music.Enabled = (!DisableMusic) & MusicPlayback; + Engine.Music.Enabled = (!DisableMusic) && (MusicMode != MusicModes.Off); MouseImage = ResourceManager.GetImage("munt.raw"); - SceneManager.SetScene("IntroScene"); + + SceneManager.SetScene(string.IsNullOrEmpty(FileSystem.LocalizationCode) ? "LanguageScene" : "IntroScene"); } protected override void OnRun() { - FileSystem.AddPackage("music", "game/classic_music"); - // set user folder to "burntime/" to get systems settings.txt for language code FileSystem.SetUserFolder("Burntime"); Settings = new ConfigFile(); Settings.Open("settings.txt"); - // legacy clean up - _ = FileSystem.RemoveFile("user:settings.txt"); + // set user folder to game specific location + FileSystem.SetUserFolder("Burntime"); + + // read user settings + UserSettings = new ConfigFile(); + UserSettings.Open("user.txt"); + FileSystem.LocalizationCode = UserSettings[""].GetString("language"); + Engine.IsFullscreen = UserSettings[""].GetBool("fullscreen", false); + base.IsNewGfx = UserSettings[""].GetBool("newgfx", true); // set language code - FileSystem.LocalizationCode = Settings["system"].GetString("language"); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); ResourceManager.Encoding = Encoding.GetEncoding(852); // DOS central europe - // set user folder to game specific location - FileSystem.SetUserFolder("Burntime/Classic"); - - // reload settings -#warning TODO settings - Settings.Open("settings.txt"); // legacy clean up _ = FileSystem.RemoveFile("user:settings.txt"); + _ = FileSystem.VFS.RemoveFolder("user:deluxe"); + _ = FileSystem.VFS.MoveFolder("user:classic/savegame", "user:saves"); + _ = FileSystem.VFS.RemoveFolder("user:classic"); - //Engine.Resolution.Native = new Vector2(512, 200);//Settings["system"].GetVector2("resolution"); -#warning TODO SlimDX/Mono full screen - //Engine.FullScreen = !Settings["system"].GetBool("windowmode"); - //Engine.UseTextureFilter = Settings["system"].GetBool("filter"); + FileSystem.AddPackage("music", "game/classic_music"); + FileSystem.AddPackage("music_fix", "game/music_fix"); + FileSystem.AddPackage("amiga", "game/amiga"); + HasDosMusic = FileSystem.ExistsFile("songs_dos.txt") && FileSystem.ExistsFile("01_MUS 01_HSC.ogg"); + HasAmigaMusic = FileSystem.ExistsFile("songs_amiga.txt"); - // check music playback settings - MusicPlayback = Settings["system"].GetBool("music"); - // check if ogg files are available - DisableMusic = !FileSystem.ExistsFile("01_MUS 01_HSC.ogg"); // || System.IntPtr.Size != 4 + SetMusicMode(UserSettings[""].GetString("music")); bool useHighResFont = Settings["system"].GetBool("highres_font"); @@ -130,10 +137,32 @@ protected override void OnRun() } } + protected override void OnProcess(float elapsed) + { + if (MusicMode != MusicModes.Off) + { + Key[] keys = DeviceManager.Keyboard.Keys; + foreach (Key key in keys) + { + if (key.IsVirtual && key.VirtualKey == SystemKey.F9) + { + ToggleMusicMode(); + break; + } + } + } + } + protected override void OnClose() { - //Settings["system"].Set("music", MusicPlayback); - //Settings.Save("settings.txt"); + // ensure section is created + UserSettings.GetSection("", true); + + UserSettings[""].Set("music", GetMusicMode()); + UserSettings[""].Set("fullscreen", Engine.IsFullscreen); + UserSettings[""].Set("newgfx", IsNewGfx); + UserSettings[""].Set("language", FileSystem.LocalizationCode); + UserSettings.Save("user.txt"); } // internal use @@ -144,27 +173,126 @@ protected override void OnClose() public String ImageScene = null; public PickItemList PickItems = null; public ActionAfterImageScene ActionAfterImageScene = ActionAfterImageScene.None; - public bool MusicPlayback; - public bool DisableMusic; + public int PreviousPlayerId = -1; public bool NewGui = false; -#warning TODO make this a setting - public override bool IsNewGfx { get; set; } = true; + public override bool IsNewGfx + { + get => base.IsNewGfx; + set { base.IsNewGfx = value; RefreshNewGfx(); } + } + + #region Music + public bool DisableMusic => !HasAmigaMusic && !HasDosMusic; + public bool HasAmigaMusic { get; private set; } + public bool HasDosMusic { get; private set; } + private string _lastPlayingSong; - public Character SelectedCharacter + public enum MusicModes { - get { return ((Player)GameState.CurrentPlayer).SelectedCharacter; } + Off = 0, + Amiga = 1, + Dos = 2, + Remaster = 3 } - public ClassicGame Game + public MusicModes MusicMode { get; private set; } = MusicModes.Remaster; + + public void SetMusicMode(string mode) { - get { return GameState as ClassicGame; } + mode = mode?.ToLower(); + + if (DisableMusic) + MusicMode = MusicModes.Off; + else if ((mode == "amiga" && HasAmigaMusic) + || (!HasDosMusic && HasAmigaMusic)) + MusicMode = MusicModes.Amiga; + else if (mode == "off") + MusicMode = MusicModes.Off; + else if (HasDosMusic) + MusicMode = MusicModes.Remaster; + else + MusicMode = MusicModes.Off; + + if (MusicMode != MusicModes.Off) + Engine.Music.LoadSonglist(MusicMode == MusicModes.Amiga ? "songs_amiga.txt" : "songs_dos.txt"); + } + + public string GetMusicMode() => MusicMode switch + { + MusicModes.Off => "off", + MusicModes.Amiga => "amiga", + _ => "remaster" + }; + + /// + /// Toggle between Amiga and remaster. + /// + public void ToggleMusicMode() + { + if (DisableMusic) return; + + if (MusicMode == MusicModes.Amiga && HasDosMusic) + { + MusicMode = MusicModes.Remaster; + Engine.Music.Enabled = true; + Engine.Music.LoadSonglist("songs_dos.txt"); + } + else if ((MusicMode == MusicModes.Dos || MusicMode == MusicModes.Remaster) + && HasAmigaMusic) + { + MusicMode = MusicModes.Amiga; + Engine.Music.Enabled = true; + Engine.Music.LoadSonglist("songs_amiga.txt"); + } + } + + /// + /// Cycle through Amiga, DOS, remaster and off. + /// + public void CycleMusicMode() + { + if (DisableMusic) return; + + if (MusicMode == MusicModes.Off && HasDosMusic) + { + MusicMode = MusicModes.Remaster; + Engine.Music.Enabled = true; + Engine.Music.LoadSonglist("songs_dos.txt"); + if (_lastPlayingSong is not null) + Engine.Music.Play(_lastPlayingSong); + } + else if ((MusicMode == MusicModes.Off && HasAmigaMusic) + || (MusicMode == MusicModes.Remaster && HasAmigaMusic)) + { + MusicMode = MusicModes.Amiga; + Engine.Music.Enabled = true; + Engine.Music.LoadSonglist("songs_amiga.txt"); + } + else if (MusicMode == MusicModes.Amiga + || (MusicMode == MusicModes.Remaster && !HasAmigaMusic)) + { + // we cycle over off mode, so we need to save the song to replay + _lastPlayingSong = Engine.Music.Playing; + MusicMode = MusicModes.Off; + Engine.Music.Enabled = false; + Engine.Music.Stop(); + } + } + #endregion + + public override string Language + { + get => base.Language; + set { if (base.Language != value) { base.Language = value; ResourceManager.ClearText(); Engine.ReloadGraphics(); } } } - public override void ToggleNewGfx() + public Character SelectedCharacter => ((Player)GameState.CurrentPlayer).SelectedCharacter; + public ClassicGame Game => GameState as ClassicGame; + + void RefreshNewGfx() { - IsNewGfx = !IsNewGfx; if (IsNewGfx) { FileSystem.AddPackage("newgfx", "game/classic_newgfx"); @@ -180,14 +308,15 @@ public override void ToggleNewGfx() { ResourceManager.SetResourceReplacement(null); } - Engine.ReloadGraphics(); } else { FileSystem.RemovePackage("newgfx"); ResourceManager.SetResourceReplacement(null); - Engine.ReloadGraphics(); } + + Engine.ReloadGraphics(); + SceneManager.ResizeScene(); } } } diff --git a/source/Burntime.Remaster/GUI/DialogWindow.cs b/source/Burntime.Remaster/GUI/DialogWindow.cs index e0983da..ad59b19 100644 --- a/source/Burntime.Remaster/GUI/DialogWindow.cs +++ b/source/Burntime.Remaster/GUI/DialogWindow.cs @@ -61,7 +61,7 @@ public override void OnShow() hover = -1; base.OnShow(); - BurntimeClassic.Instance.Engine.Music.Play("18_MUS 18_HSC.ogg"); + BurntimeClassic.Instance.Engine.Music.Play("talking"); } public override void OnHide() diff --git a/source/Burntime.Remaster/GUI/MapView.cs b/source/Burntime.Remaster/GUI/MapView.cs index 620d2c8..1a3348c 100644 --- a/source/Burntime.Remaster/GUI/MapView.cs +++ b/source/Burntime.Remaster/GUI/MapView.cs @@ -8,7 +8,6 @@ using Burntime.Platform.Graphics; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; namespace Burntime.Remaster.GUI; @@ -66,8 +65,6 @@ public class MapView : Window public event EventHandler Scroll; Vector2 mousePosition = new Vector2(); - ParticleEngine particles = new ParticleEngine(); - protected bool enabled; List overlays = new List(); public List Overlays @@ -77,7 +74,6 @@ public List Overlays } IMapEntranceHandler handler; - GuiFont font; MouseClickEvent mouseClickEvent; public MouseClickEvent MouseClickEvent { @@ -85,26 +81,17 @@ public MouseClickEvent MouseClickEvent set { mouseClickEvent = value; } } - public ParticleEngine Particles - { - get { return particles; } - } - - public bool Enabled - { - get { return enabled; } - set { enabled = value; } - } + public ParticleEngine Particles { get; } = new ParticleEngine(); + public bool Enabled { get; set; } + public event ClickHandler ContextMenu; public MapView(IMapEntranceHandler Handler, Module App) : base(App) { - enabled = true; + Enabled = true; handler = Handler; CaptureAllMouseMove = true; - font = new GuiFont(BurntimeClassic.FontName, new PixelColor(212, 212, 212)); - BurntimeClassic classic = app as BurntimeClassic; DebugView = classic.Settings["debug"].GetBool("show_routes") && classic.Settings["debug"].GetBool("enable_cheats"); } @@ -139,11 +126,11 @@ public Remaster.Logic.Player Player public bool DebugView = false; - Vector2f position = new Vector2f(); + Vector2f _position = new Vector2f(); public Vector2 ScrollPosition { - get { return new Vector2(position); } - set { position = value; } + get { return new Vector2(_position); } + set { _position = value; } } float scrollSpeed = 70; @@ -217,7 +204,7 @@ public override void OnRender(RenderTarget Target) Target.Layer++; - int old = Target.Layer; + var old = Target.Layer; Target.Layer += 30; foreach (Maps.IMapViewOverlay overlay in overlays) @@ -228,15 +215,15 @@ public override void OnRender(RenderTarget Target) Target.Layer++; Target.Offset += ScrollPosition; - particles.Render(Target); + Particles.Render(Target); Target.Offset -= ScrollPosition; Target.Layer = old; } - public override bool OnMouseMove(Vector2 Position) + public override bool OnMouseMove(Vector2 position) { - if (!enabled) + if (!Enabled) return false; const int Margin = 4; @@ -245,19 +232,19 @@ public override bool OnMouseMove(Vector2 Position) border.x = 0; border.y = 0; - border.x += (Position.x < Margin) ? 1 : 0; - border.x -= (Position.x > Size.x - Margin) ? 1 : 0; - border.x += (Position.x < BigMargin) ? 1 : 0; - border.x -= (Position.x > Size.x - BigMargin) ? 1 : 0; - border.y += (Position.y < Margin) ? 1 : 0; - border.y -= (Position.y > Size.y - Margin) ? 1 : 0; - border.y += (Position.y < BigMargin) ? 1 : 0; - border.y -= (Position.y > Size.y - BigMargin) ? 1 : 0; + border.x += (position.x < Margin) ? 1 : 0; + border.x -= (position.x > Size.x - Margin) ? 1 : 0; + border.x += (position.x < BigMargin) ? 1 : 0; + border.x -= (position.x > Size.x - BigMargin) ? 1 : 0; + border.y += (position.y < Margin) ? 1 : 0; + border.y -= (position.y > Size.y - Margin) ? 1 : 0; + border.y += (position.y < BigMargin) ? 1 : 0; + border.y -= (position.y > Size.y - BigMargin) ? 1 : 0; bool found = false; for (int i = 0; i < map.Entrances.Length; i++) { - if (map.Entrances[i].Area.PointInside(Position - (Vector2)position)) + if (map.Entrances[i].Area.PointInside(position - (Vector2)this._position)) { entrance = i; found = true; @@ -266,16 +253,42 @@ public override bool OnMouseMove(Vector2 Position) if (!found) entrance = -1; - mousePosition = Position - ScrollPosition; + mousePosition = position - ScrollPosition; + + if (_rightClickMove.HasValue) + { + _position += (Vector2f)(position - _rightClickMove.Value); + _position.Max(0); + _position.Min(Boundings.Size - map.TileSize * map.Size); + + _moveTotal += (position - _rightClickMove.Value).Length; + _rightClickMove = position; + } return true; } + public override void OnMouseEnter() + { + if (_rightClickMove.HasValue && !app.Engine.DeviceManager.Mouse.IsRightDown) + _rightClickMove = null; + + base.OnMouseEnter(); + } + public override bool OnMouseClick(Vector2 position, MouseButton button) { - if (!enabled) + if (!Enabled) return false; + if (button == MouseButton.Right) + { + if (_moveTotal < 10) + ContextMenu?.Invoke(position, button); + _rightClickMove = null; + _moveTotal = 0; + } + if (entrance != -1 && handler != null) if (handler.OnClickEntrance(entrance, button)) return true; @@ -304,20 +317,34 @@ public override bool OnMouseClick(Vector2 position, MouseButton button) return false; } - public override void OnUpdate(float Elapsed) + float _moveTotal; + Vector2? _rightClickMove = null; + public override bool OnMouseDown(Vector2 position, MouseButton button) { - position += border * Elapsed * scrollSpeed; - if (border != Vector2f.Zero && Scroll != null) - Scroll.Invoke(this, new MapScrollArgs(position)); + if (button == MouseButton.Right) + { + _moveTotal = 0; + _rightClickMove = position; + } + + return true; + } - position.Max(0); - position.Min(Boundings.Size - map.TileSize * map.Size); + public override void OnUpdate(float Elapsed) + { + if (!_rightClickMove.HasValue && border != Vector2f.Zero) + { + _position += border * Elapsed * scrollSpeed; + _position.Max(0); + _position.Min(Boundings.Size - map.TileSize * map.Size); + Scroll?.Invoke(this, new MapScrollArgs(_position)); + } // map is smaller than screen, center it if (map.TileSize.x * map.Size.x < Boundings.Size.x) - position.x = (Boundings.Size.x - map.TileSize.x * map.Size.x) / 2; + _position.x = (Boundings.Size.x - map.TileSize.x * map.Size.x) / 2; if (map.TileSize.y * map.Size.y < Boundings.Size.y) - position.y = (Boundings.Size.y - map.TileSize.y * map.Size.y) / 2; + _position.y = (Boundings.Size.y - map.TileSize.y * map.Size.y) / 2; if (handler != null) { @@ -344,13 +371,14 @@ public override void OnUpdate(float Elapsed) } } - particles.Update(Elapsed); + Particles.Update(Elapsed); } public void CenterTo(Vector2 centerTo) { - position = -centerTo + (Boundings.Size / 2); - - OnUpdate(0); + _position = -centerTo + (Boundings.Size / 2); + _position.Max(0); + _position.Min(Boundings.Size - map.TileSize * map.Size); + Scroll?.Invoke(this, new MapScrollArgs(_position)); } } diff --git a/source/Burntime.Remaster/Logic/AttackNotify.cs b/source/Burntime.Remaster/Logic/AttackNotify.cs index c1a32f0..ce62258 100644 --- a/source/Burntime.Remaster/Logic/AttackNotify.cs +++ b/source/Burntime.Remaster/Logic/AttackNotify.cs @@ -1,27 +1,15 @@ -using Burntime.Platform; -using Burntime.Framework.States; +using Burntime.Framework.States; -namespace Burntime.Remaster.Logic -{ - class AttackEvent : ILogicNotifycation - { - Vector2 attacker; - Vector2 defender; - - public Vector2 Attacker - { - get { return attacker; } - } +namespace Burntime.Remaster.Logic; - public Vector2 Defender - { - get { return defender; } - } +class AttackEvent : ILogicNotifycation +{ + public Character Attacker { get; init; } + public Character Defender { get; init; } - public AttackEvent(Vector2 attacker, Vector2 defender) - { - this.attacker = attacker; - this.defender = defender; - } + public AttackEvent(Character attacker, Character defender) + { + Attacker = attacker; + Defender = defender; } } diff --git a/source/Burntime.Remaster/Logic/Character/Character.cs b/source/Burntime.Remaster/Logic/Character/Character.cs index aed6a5f..ea31493 100644 --- a/source/Burntime.Remaster/Logic/Character/Character.cs +++ b/source/Burntime.Remaster/Logic/Character/Character.cs @@ -485,7 +485,7 @@ public void Attack(Character target) if (target.Weapon != null) target.Weapon.Use(); - container.FireNotifycation(new AttackEvent(Player.Group[i].Position, target.Position)); + container.Notify(new AttackEvent(Player.Group[i], target)); if (target.IsDead) break; @@ -518,7 +518,7 @@ public void Attack(Character target) if (target.Weapon != null) target.Weapon.Use(); - container.FireNotifycation(new AttackEvent(this.Position, target.Position)); + container.Notify(new AttackEvent(this, target)); } } diff --git a/source/Burntime.Remaster/Logic/Character/Trader.cs b/source/Burntime.Remaster/Logic/Character/Trader.cs index 4058bb9..329d064 100644 --- a/source/Burntime.Remaster/Logic/Character/Trader.cs +++ b/source/Burntime.Remaster/Logic/Character/Trader.cs @@ -26,7 +26,7 @@ public class Trader : Character, ITestIF int counter = 0; StateLinkList itemRefreshs; int itemRefreshRange = 0; - int itemCount = 10; + int _maxStockItemCount = 7; protected int traderId; public Location HomeArea @@ -117,8 +117,9 @@ protected virtual void NextSellLocation() protected virtual void RefreshItems() { - int remove = rnd.Next(3); - int add = rnd.Next(3); + int remove = System.Math.Min(Items.Count, rnd.Next(2, 3)); + int maxAdd = System.Math.Max(0, _maxStockItemCount - Items.Count + remove); + int add = rnd.Next(System.Math.Min(3, maxAdd), maxAdd); for (int i = 0; i < remove && Items.Count > 0; i++) { @@ -128,7 +129,7 @@ protected virtual void RefreshItems() Items.Remove(Items[item]); } - for (int i = 0; i < add && Items.Count < itemCount; i++) + for (int i = 0; i < add && Items.Count < _maxStockItemCount; i++) { ItemType type = GetNextItem(); if (type == null) diff --git a/source/Burntime.Remaster/Maps/MapViewOverlayCharacters.cs b/source/Burntime.Remaster/Maps/MapViewOverlayCharacters.cs index 6a8fa99..e60cb4f 100644 --- a/source/Burntime.Remaster/Maps/MapViewOverlayCharacters.cs +++ b/source/Burntime.Remaster/Maps/MapViewOverlayCharacters.cs @@ -32,6 +32,8 @@ public bool IsVisible set { } } + private bool RenderShadow => app.IsNewGfx; + public MapViewOverlayCharacters(Module App) { app = App; @@ -93,7 +95,7 @@ public void RenderOverlay(RenderTarget target, Vector2 offset, Vector2 size) characters.Add(player.Group[i]); } - if (app.IsNewGfx) + if (RenderShadow) { foreach (Character chr in characters) { @@ -113,11 +115,14 @@ public void RenderOverlay(RenderTarget target, Vector2 offset, Vector2 size) } } + float layer = target.Layer; + float mapHeight = (mapState.Map.MapData.Height * mapState.Map.MapData.TileSize.y); + foreach (Character chr in characters) { // skip if dead - if (chr.IsDead && chr.DeadAnimationFinished || - chr.IsPlayerCharacter && chr.Player.IsDead) + if (chr.IsDead && chr.DeadAnimationFinished + || chr.IsPlayerCharacter && chr.Player.IsDead) { continue; } @@ -131,6 +136,7 @@ public void RenderOverlay(RenderTarget target, Vector2 offset, Vector2 size) chr.Body.Object.Animation.Frame = ani.Frame; else chr.Body.Object.Animation.Frame = chr.Animation; + target.Layer = layer + (chr.Position.y / mapHeight); target.DrawSprite(pos, chr.Body); if (debugRender) @@ -140,6 +146,8 @@ public void RenderOverlay(RenderTarget target, Vector2 offset, Vector2 size) chr.Path.DebugRender(lineTarget); } } + + target.Layer = layer; } } diff --git a/source/Burntime.Remaster/Maps/MapViewOverlayFlags.cs b/source/Burntime.Remaster/Maps/MapViewOverlayFlags.cs index 79d6da7..43c745a 100644 --- a/source/Burntime.Remaster/Maps/MapViewOverlayFlags.cs +++ b/source/Burntime.Remaster/Maps/MapViewOverlayFlags.cs @@ -9,8 +9,7 @@ namespace Burntime.Remaster.Maps class MapViewOverlayFlags : IMapViewOverlay { ClassicGame state; - Module app; - SpriteAnimation ani; + readonly SpriteAnimation _ani; public bool IsVisible { @@ -18,10 +17,10 @@ public bool IsVisible set { } } - public MapViewOverlayFlags(Module app) + public MapViewOverlayFlags(Module _) { - this.app = app; - ani = new SpriteAnimation(4); + _ani = new SpriteAnimation(12); + _ani.Speed = 15; } public void MouseMoveOverlay(Vector2 position) @@ -31,7 +30,7 @@ public void MouseMoveOverlay(Vector2 position) public void UpdateOverlay(WorldState world, float elapsed) { state = world as ClassicGame; - ani.Update(elapsed); + _ani.Update(elapsed); } public void RenderOverlay(RenderTarget target, Vector2 offset, Vector2 size) @@ -45,7 +44,7 @@ public void RenderOverlay(RenderTarget target, Vector2 offset, Vector2 size) if (l.Player != null) { Rect area = state.World.Map.Entrances[i].Area; - l.Player.Flag.Object.Animation.Frame = ani.Frame; + l.Player.Flag.Object.Animation.Frame = _ani.Frame * l.Player.Flag.Object.Animation.FrameCount / _ani.FrameCount; target.DrawSprite(new Vector2(area.Right, area.Top) + offset, l.Player.Flag); } } diff --git a/source/Burntime.Remaster/Scenes/ChurchScene.cs b/source/Burntime.Remaster/Scenes/ChurchScene.cs index d995d04..9f7f274 100644 --- a/source/Burntime.Remaster/Scenes/ChurchScene.cs +++ b/source/Burntime.Remaster/Scenes/ChurchScene.cs @@ -16,7 +16,7 @@ class ChurchScene : Scene public ChurchScene(Module app) : base(app) { - Music = "14_MUS 14_HSC.ogg"; + Music = "church"; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; Image ani = new Image(app); diff --git a/source/Burntime.Remaster/Scenes/DeathScene.cs b/source/Burntime.Remaster/Scenes/DeathScene.cs index 398b623..375a0e4 100644 --- a/source/Burntime.Remaster/Scenes/DeathScene.cs +++ b/source/Burntime.Remaster/Scenes/DeathScene.cs @@ -19,7 +19,7 @@ public DeathScene(Module app) : base(app) { Background = "film_00.pac"; - Music = "09_MUS 09_HSC.ogg"; + Music = "death"; CaptureAllMouseClicks = true; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; diff --git a/source/Burntime.Remaster/Scenes/DoctorScene.cs b/source/Burntime.Remaster/Scenes/DoctorScene.cs index 9b3a2e3..acd168f 100644 --- a/source/Burntime.Remaster/Scenes/DoctorScene.cs +++ b/source/Burntime.Remaster/Scenes/DoctorScene.cs @@ -22,9 +22,9 @@ public DoctorScene(Module app) : base(app) { Background = "arzt.pac"; - Music = "03_MUS 03_HSC.ogg"; + Music = "doctor"; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; - + Image ani = new Image(app); ani.Position = new Vector2(211, 65); ani.Background = "arzt.ani??p"; diff --git a/source/Burntime.Remaster/Scenes/ImageScene.cs b/source/Burntime.Remaster/Scenes/ImageScene.cs index 9ffbe50..e4484d7 100644 --- a/source/Burntime.Remaster/Scenes/ImageScene.cs +++ b/source/Burntime.Remaster/Scenes/ImageScene.cs @@ -36,7 +36,7 @@ protected override void OnActivateScene(object parameter) if (game.ImageScene == "film_06.pac") { - Music = "19_MUS 19_HSC.ogg"; + Music = "pub"; ani1 = new Image(app); ani1.Background = "film_06.ani?0-5"; @@ -53,11 +53,11 @@ protected override void OnActivateScene(object parameter) } else if (game.ImageScene == "film_05.pac") { - Music = "21_MUS 21_HSC.ogg"; + Music = "sounds/trader.ogg"; ani1 = new Image(app); ani1.Background = "film_05.ani?0-17?p"; - ani1.Position = new Vector2(98, 120); + ani1.Position = app.IsNewGfx ? new Vector2(76, 120) : new Vector2(98, 120); ani1.Background.Animation.Speed = 6.5f; ani1.Background.Animation.IntervalMargin = 4; ani1.Background.Animation.Progressive = false; @@ -65,7 +65,7 @@ protected override void OnActivateScene(object parameter) ani2 = new Image(app); ani2.Background = "film_05.ani?18-19"; - ani2.Position = new Vector2(77, 89); + ani2.Position = app.IsNewGfx ? new Vector2(52, 88) : new Vector2(77, 89); ani2.Background.Animation.Speed = 6.5f; ani2.Background.Animation.IntervalMargin = 5; ani2.Background.Animation.ReverseAnimation = true; @@ -74,37 +74,37 @@ protected override void OnActivateScene(object parameter) ani3 = new Image(app); ani3.Background = "film_05.ani?20-21"; - ani3.Position = new Vector2(106, 59); + ani3.Position = app.IsNewGfx ? new Vector2(84, 48) : new Vector2(106, 59); ani3.Background.Animation.Speed = 6.5f; ani3.Background.Animation.Progressive = false; Windows += ani3; } else if (game.ImageScene == "film_10.pac") { - Music = "21_MUS 21_HSC.ogg"; + Music = "sounds/trader.ogg"; ani1 = new Image(app); ani1.Background = "film_10.ani"; - ani1.Position = new Vector2(125, 92); + ani1.Position = app.IsNewGfx ? new Vector2(108, 89) : new Vector2(125, 92); ani1.Background.Animation.IntervalMargin = 3; - ani1.Background.Animation.Speed = 5.0f; + ani1.Background.Animation.Speed = app.IsNewGfx ? 9.0f : 5.0f; Windows += ani1; } else if (game.ImageScene == "film_02.pac") { - Music = "07_MUS 07_HSC.ogg"; + Music = "ruin"; } else if (game.ImageScene == "film_03.pac") { - Music = "09_MUS 09_HSC.ogg"; + Music = "death"; } else if (game.ImageScene == "film_04.pac") { - Music = "05_MUS 05_HSC.ogg"; + Music = "building"; } else if (game.ImageScene == "film_09.pac") { - Music = "02_MUS 02_HSC.ogg"; + Music = "death"; } } @@ -113,6 +113,18 @@ public override void OnResizeScreen() base.OnResizeScreen(); Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; + + if (BurntimeClassic.Instance.ImageScene == "film_10.pac" && ani1 is not null) + { + ani1.Position = app.IsNewGfx ? new Vector2(108, 89) : new Vector2(125, 92); + ani1.Background.Animation.Speed = app.IsNewGfx ? 9.0f : 5.0f; + } + else if (BurntimeClassic.Instance.ImageScene == "film_05.pac" && ani1 is not null) + { + ani1.Position = app.IsNewGfx ? new Vector2(76, 120) : new Vector2(98, 120); + ani2.Position = app.IsNewGfx ? new Vector2(52, 88) : new Vector2(77, 89); + ani3.Position = app.IsNewGfx ? new Vector2(84, 48) : new Vector2(106, 59); + } } public override bool OnMouseClick(Vector2 Position, MouseButton Button) @@ -135,8 +147,11 @@ public override bool OnKeyPress(char Key) return true; } - public override bool OnVKeyPress(SystemKey Key) + public override bool OnVKeyPress(SystemKey key) { + if (key == SystemKey.F8 || key == SystemKey.F9) + return false; + if (!handled) { PreviousScene(); @@ -170,5 +185,24 @@ protected override void OnInactivateScene() { app.RenderMouse = true; } + + public override void OnRender(RenderTarget target) + { + base.OnRender(target); + + target.Layer = app.Engine.MaxLayers - 1; + + const int MARGIN = 32; + + target.RenderRect(-Position, + new Vector2(app.Engine.Resolution.Game.x, MARGIN), new PixelColor(0, 0, 0)); + target.RenderRect(new Vector2(-Position.x, app.Engine.Resolution.Game.y - MARGIN - Position.y), + new Vector2(app.Engine.Resolution.Game.x, MARGIN + 1), new PixelColor(0, 0, 0)); + + target.RenderRect(new Vector2(-Position.x, -Position.y + MARGIN), + new Vector2(MARGIN, app.Engine.Resolution.Game.y - MARGIN * 2), new PixelColor(0, 0, 0)); + target.RenderRect(new Vector2(-Position.x + app.Engine.Resolution.Game.x - MARGIN, -Position.y + MARGIN), + new Vector2(MARGIN + 1, app.Engine.Resolution.Game.y - MARGIN * 2), new PixelColor(0, 0, 0)); + } } } diff --git a/source/Burntime.Remaster/Scenes/InfoScene.cs b/source/Burntime.Remaster/Scenes/InfoScene.cs index 1bbd341..1da508e 100644 --- a/source/Burntime.Remaster/Scenes/InfoScene.cs +++ b/source/Burntime.Remaster/Scenes/InfoScene.cs @@ -36,7 +36,7 @@ public InfoScene(Module App) : base(App) { Background = "info.pac"; - Music = "13_MUS 13_HSC.ogg"; + Music = "info"; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; diff --git a/source/Burntime.Remaster/Scenes/IntroScene.cs b/source/Burntime.Remaster/Scenes/IntroScene.cs index ebf29be..2f6a9f1 100644 --- a/source/Burntime.Remaster/Scenes/IntroScene.cs +++ b/source/Burntime.Remaster/Scenes/IntroScene.cs @@ -50,7 +50,7 @@ public IntroScene(Module App) : base(App) { timeout = new FadingHelper(); - Music = "20_MUS 20_HSC.ogg"; + Music = "intro"; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; Size = new Vector2(320, 200); @@ -141,6 +141,9 @@ public override bool OnMouseClick(Vector2 position, MouseButton button) public override bool OnVKeyPress(SystemKey key) { + if (key == SystemKey.F8 || key == SystemKey.F9) + return false; + NextScene(); return true; } diff --git a/source/Burntime.Remaster/Scenes/InventoryScene.cs b/source/Burntime.Remaster/Scenes/InventoryScene.cs index f0624cb..6106cd8 100644 --- a/source/Burntime.Remaster/Scenes/InventoryScene.cs +++ b/source/Burntime.Remaster/Scenes/InventoryScene.cs @@ -134,7 +134,7 @@ protected override void OnActivateScene(object parameter) if (classic.InventoryRoom != null) { - Music = classic.InventoryRoom.IsWaterSource ? "22_MUS 22_HSC.ogg" : "04_MUS 04_HSC.ogg"; + Music = classic.InventoryRoom.IsWaterSource ? "water" : "room"; grid = new ItemGridWindow(app); grid.LockPositions = true; @@ -158,7 +158,7 @@ protected override void OnActivateScene(object parameter) } else if (classic.PickItems != null) { - Music = "04_MUS 04_HSC.ogg"; + Music = "room"; grid = new ItemGridWindow(app); grid.Position = new Vector2(170, 10); @@ -172,7 +172,7 @@ protected override void OnActivateScene(object parameter) grid.Add(classic.PickItems); } else - Music = "04_MUS 04_HSC.ogg"; + Music = "room"; } void OnButtonExit() diff --git a/source/Burntime.Remaster/Scenes/LanguageScene.cs b/source/Burntime.Remaster/Scenes/LanguageScene.cs new file mode 100644 index 0000000..957bb2f --- /dev/null +++ b/source/Burntime.Remaster/Scenes/LanguageScene.cs @@ -0,0 +1,61 @@ +using Burntime.Framework; +using Burntime.Framework.GUI; +using Burntime.Platform; +using Burntime.Platform.Graphics; +using Burntime.Remaster; +using Microsoft.VisualBasic; + +namespace Burntime.Classic.Scenes; + +internal class LanguageScene : Scene +{ + readonly Button _german; + readonly GuiFont _hintFont; + + public LanguageScene(Module app) : base(app) + { + var center = app.Engine.Resolution.Game / 2; + var font = new GuiFont(BurntimeClassic.FontName, PixelColor.White) { Borders = Platform.Graphics.TextBorders.None }; + var hoverFont = new GuiFont(BurntimeClassic.FontName, new PixelColor(240, 64, 56)) { Borders = Platform.Graphics.TextBorders.None }; + + _hintFont = new GuiFont("highres-font_de.txt", new PixelColor(128, 128, 128)) { Borders = Platform.Graphics.TextBorders.None }; + + Windows += _german = new Button(app, () => SelectLanguage("de")) + { + Font = font, + HoverFont = hoverFont, + Position = center + new Vector2(-5, -10), + Text = "Deutsch", + HorizontalAlignment = PositionAlignment.Right, + IsTextOnly = true + }; + + Windows += new Button(app, () => SelectLanguage("en")) + { + Font = font, + HoverFont = hoverFont, + Position = center + new Vector2(5, -10), + Text = "English", + HorizontalAlignment = PositionAlignment.Left, + IsTextOnly = true + }; + } + + void SelectLanguage(string language) + { + app.Language = language; + app.SceneManager.SetScene("IntroScene"); + } + + public override void OnRender(RenderTarget target) + { + base.OnRender(target); + + var center = app.Engine.Resolution.Game / 2; + + _hintFont.DrawText(target, center + new Vector2(0, 20), _german.IsHover + ? "Tipp: drücke F11 für Vollbild" + : "Hint: use F11 for fullscreen", + TextAlignment.Center); + } +} diff --git a/source/Burntime.Remaster/Scenes/LocationScene.cs b/source/Burntime.Remaster/Scenes/LocationScene.cs index e765f98..ff75b8f 100644 --- a/source/Burntime.Remaster/Scenes/LocationScene.cs +++ b/source/Burntime.Remaster/Scenes/LocationScene.cs @@ -13,6 +13,7 @@ using Burntime.Remaster.Logic; using Burntime.Remaster.Logic.Interaction; using Burntime.Remaster.Maps; +using System.Security.Claims; namespace Burntime.Remaster { @@ -48,6 +49,7 @@ public LocationScene(Module App) view.Overlays.Add(hoverInfo = new Maps.MapViewOverlayHoverText(App)); view.ClickObject += new EventHandler(view_ClickObject); view.Scroll += new EventHandler(view_Scroll); + view.ContextMenu += View_ContextMenu; Windows += view; menu = new MenuWindow(App); @@ -75,6 +77,11 @@ public LocationScene(Module App) Windows += dialog; } + private void View_ContextMenu(Vector2 position, MouseButton button) + { + ShowMenu(position); + } + public override void OnResizeScreen() { base.OnResizeScreen(); @@ -233,22 +240,13 @@ public override bool OnKeyPress(char key) return false; } - public override bool OnMouseClick(Vector2 position, MouseButton button) - { - if (button == MouseButton.Right) - { - ShowMenu(position); - } - return true; - } - public override void OnRender(RenderTarget Target) { if (app.MouseImage != null) { cursorAni.Position = app.DeviceManager.Mouse.Position + new Vector2(8, 11); - int layer = Target.Layer; + var layer = Target.Layer; Target.Layer = gui.Layer - 1; Target.DrawSprite(app.DeviceManager.Mouse.Position, app.MouseImage); Target.Layer = layer; @@ -284,7 +282,7 @@ protected override void OnActivateScene(object parameter) BurntimeClassic.Instance.PreviousPlayerId != game.CurrentPlayerIndex) { // play player changed sound - BurntimeClassic.Instance.Engine.Music.PlayOnce("06_MUS 06_HSC.ogg"); + BurntimeClassic.Instance.Engine.Music.PlayOnce("sounds/change.ogg"); } BurntimeClassic.Instance.PreviousPlayerId = game.CurrentPlayerIndex; @@ -292,10 +290,10 @@ protected override void OnActivateScene(object parameter) view.Location = game.World.ActiveLocationObj; view.Player = game.World.ActivePlayerObj; - if (view.Player.RefreshScrollPosition) + //if (view.Player.RefreshScrollPosition) view.CenterTo(view.Player.Character.Position); - else - view.ScrollPosition = view.Player.LocationScrollPosition; + //else + // view.ScrollPosition = view.Player.LocationScrollPosition; gui.UpdatePlayer(); view.Player.OnMainMap = false; @@ -449,7 +447,7 @@ public void OnMenuMakeCamp() charOverlay.SelectedCharacter.JoinCamp(); view.Location.Player = view.Player; - BurntimeClassic.Instance.Engine.Music.PlayOnce("08_MUS 08_HSC.ogg"); + BurntimeClassic.Instance.Engine.Music.PlayOnce("sounds/camp.ogg"); } } @@ -655,8 +653,28 @@ void ILogicNotifycationHandler.Handle(ILogicNotifycation notify) var sprite = (GuiImage)"burngfxani@syssze.raw?208-213"; sprite.Animation.Speed = 20; - view.Particles.Add(new StaticAnimationParticle(sprite, eventArgs.Attacker)); - view.Particles.Add(new StaticAnimationParticle(sprite.Clone(), eventArgs.Defender)); + view.Particles.Add(new StaticAnimationParticle(sprite, eventArgs.Attacker.Position)); + view.Particles.Add(new StaticAnimationParticle(sprite.Clone(), eventArgs.Defender.Position)); + + // play sounds only for human player interactions + if (eventArgs.Attacker.Player?.Type != PlayerType.Human && + eventArgs.Defender.Player?.Type != PlayerType.Human) + return; + + if ((eventArgs.Attacker.IsDead && eventArgs.Attacker.Class != CharClass.Dog) + || (eventArgs.Defender.IsDead && eventArgs.Defender.Class != CharClass.Dog)) + { + app.Engine.Music.PlayOnce("sounds/hit-die.ogg"); + } + else if ((!eventArgs.Attacker.IsDead && eventArgs.Attacker.Class == CharClass.Dog) + || (!eventArgs.Defender.IsDead && eventArgs.Defender.Class == CharClass.Dog)) + { + app.Engine.Music.PlayOnce("sounds/hit-barf.ogg"); + } + else + { + app.Engine.Music.PlayOnce("sounds/hit.ogg"); + } } } } diff --git a/source/Burntime.Remaster/Scenes/MapScene.cs b/source/Burntime.Remaster/Scenes/MapScene.cs index 0bea0ec..921007f 100644 --- a/source/Burntime.Remaster/Scenes/MapScene.cs +++ b/source/Burntime.Remaster/Scenes/MapScene.cs @@ -38,6 +38,7 @@ public MapScene(Module App) view.Overlays.Add(new Maps.MapViewOverlayPlayer(app)); view.Overlays.Add(new Maps.MapViewOverlayHoverText(app)); view.Scroll += new EventHandler(view_Scroll); + view.ContextMenu += View_OnContextMenu; Windows += view; menu = new MenuWindow(App); @@ -62,6 +63,11 @@ public MapScene(Module App) debugNoTravel = classic.Settings["debug"].GetBool("no_travel") && classic.Settings["debug"].GetBool("enable_cheats"); } + private void View_OnContextMenu(Vector2 position, MouseButton button) + { + menu.Show(position, view.Boundings); + } + public override void OnResizeScreen() { base.OnResizeScreen(); @@ -77,15 +83,6 @@ void view_Scroll(object sender, MapScrollArgs e) game.World.ActivePlayerObj.MapScrollPosition = e.Offset; } - public override bool OnMouseClick(Vector2 Position, MouseButton Button) - { - if (Button == MouseButton.Right) - { - menu.Show(Position, view.Boundings); - } - return true; - } - public override bool OnKeyPress(char key) { if (app.Settings["debug"].GetBool("enable_cheats") && key == '9') @@ -107,7 +104,7 @@ public override void OnRender(RenderTarget Target) if (!BurntimeClassic.Instance.NewGui) { - int layer = Target.Layer; + var layer = Target.Layer; Target.Layer = gui.Layer - 1; Target.DrawSprite(app.DeviceManager.Mouse.Position, app.MouseImage); Target.Layer = layer; @@ -141,17 +138,17 @@ protected override void OnActivateScene(object parameter) BurntimeClassic.Instance.PreviousPlayerId != game.CurrentPlayerIndex) { // play player changed sound - BurntimeClassic.Instance.Engine.Music.PlayOnce("06_MUS 06_HSC.ogg"); + BurntimeClassic.Instance.Engine.Music.PlayOnce("sounds/change.ogg"); } BurntimeClassic.Instance.PreviousPlayerId = game.CurrentPlayerIndex; view.Ways = (WayData)game.World.Ways.WayData; view.Map = (MapData)game.World.Map.MapData; view.Player = game.World.ActivePlayerObj; - if (game.World.ActivePlayerObj.RefreshMapScrollPosition) + //if (game.World.ActivePlayerObj.RefreshMapScrollPosition) view.CenterTo(view.Map.Entrances[game.World.ActivePlayerObj.Location].Area.Center); - else - view.ScrollPosition = game.World.ActivePlayerObj.MapScrollPosition; + //else + // view.ScrollPosition = game.World.ActivePlayerObj.MapScrollPosition; gui.UpdatePlayer(); game.World.ActivePlayerObj.OnMainMap = true; @@ -261,7 +258,7 @@ public bool OnClickEntrance(int Number, MouseButton Button) if (debugNoTravel) { player.Location = clickedLocation; - player.Character.Position = -Vector2.One; + player.Character.Position = clickedLocation.EntryPoint; player.RefreshScrollPosition = true; } diff --git a/source/Burntime.Remaster/Scenes/MenuScene.cs b/source/Burntime.Remaster/Scenes/MenuScene.cs index 2bd4a9f..1ba8847 100644 --- a/source/Burntime.Remaster/Scenes/MenuScene.cs +++ b/source/Burntime.Remaster/Scenes/MenuScene.cs @@ -31,7 +31,7 @@ public MenuScene(Module App) : base(App) { Background = "sta.pac"; - Music = "15_MUS 15_HSC.ogg"; + Music = "start"; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; copyright = new GuiFont(BurntimeClassic.FontName, new PixelColor(164, 164, 164)) { Borders = TextBorders.Screen }; @@ -101,6 +101,8 @@ public MenuScene(Module App) PlayerOneSwitch.UpCommand += OnPlayerOneUp; PlayerOneSwitch.Command += OnPlayerOneClick; PlayerOneSwitch.Font = new GuiFont(BurntimeClassic.FontName, new PixelColor(184, 184, 184)); + PlayerOneSwitch.TextHorizontalAlign = TextAlignment.Center; + PlayerOneSwitch.TextVerticalAlign = VerticalTextAlignment.Center; Windows += PlayerOneSwitch; PlayerTwoSwitch = new NameWindow(App); PlayerTwoSwitch.Position = new Vector2(204, 92); @@ -110,6 +112,8 @@ public MenuScene(Module App) PlayerTwoSwitch.UpCommand += OnPlayerTwoUp; PlayerTwoSwitch.Command += OnPlayerTwoClick; PlayerTwoSwitch.Font = new GuiFont(BurntimeClassic.FontName, new PixelColor(184, 184, 184)); + PlayerTwoSwitch.TextHorizontalAlign = TextAlignment.Center; + PlayerTwoSwitch.TextVerticalAlign = VerticalTextAlignment.Center; Windows += PlayerTwoSwitch; // color diff --git a/source/Burntime.Remaster/Scenes/OptionsGiveUpPage.cs b/source/Burntime.Remaster/Scenes/OptionsGiveUpPage.cs new file mode 100644 index 0000000..8192a23 --- /dev/null +++ b/source/Burntime.Remaster/Scenes/OptionsGiveUpPage.cs @@ -0,0 +1,53 @@ +using Burntime.Framework; +using Burntime.Framework.GUI; +using Burntime.Platform; + +namespace Burntime.Classic.Scenes; + +internal class OptionsGiveUpPage : Container +{ + readonly OptionFonts _fonts; + + readonly Button _buttonRestart; + + public OptionsGiveUpPage(Module app, OptionFonts fonts) : base(app) + { + _fonts = fonts; + + Windows += _buttonRestart = new Button(app, OnButtonRestart) + { + Font = _fonts.Green, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Text = "@newburn?28", + Position = new Vector2(40, 82), + Size = new Vector2(120, 10), + TextHorizontalAlign = Platform.Graphics.TextAlignment.Center + }; + Windows += new Button(app, () => app.Close()) + { + Font = _fonts.Green, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Text = "@burn?391", + Position = new Vector2(40, 102), + Size = new Vector2(120, 10), + TextHorizontalAlign = Platform.Graphics.TextAlignment.Center + }; + } + + public override void OnUpdate(float elapsed) + { + _buttonRestart.IsEnabled = app.SceneManager.LastScene != "MenuScene"; + + base.OnUpdate(elapsed); + } + + void OnButtonRestart() + { + if (app.SceneManager.LastScene == "MenuScene") return; + + app.StopGame(); + app.SceneManager.SetScene("MenuScene"); + } +} diff --git a/source/Burntime.Remaster/Scenes/OptionsJukeboxPage.cs b/source/Burntime.Remaster/Scenes/OptionsJukeboxPage.cs new file mode 100644 index 0000000..e98ad96 --- /dev/null +++ b/source/Burntime.Remaster/Scenes/OptionsJukeboxPage.cs @@ -0,0 +1,90 @@ +using Burntime.Framework; +using Burntime.Framework.GUI; +using Burntime.Platform; +using Burntime.Remaster; +using System.Collections.Generic; + +namespace Burntime.Classic.Scenes; + +internal class OptionsJukeboxPage : Container +{ + readonly OptionFonts _fonts; + readonly Dictionary _songButtons = new(); + Button _lastPlayingButton; + + public OptionsJukeboxPage(Module app, OptionFonts fonts) : base(app) + { + _fonts = fonts; + } + + public override void OnActivate() + { + base.OnActivate(); + + CreateSongButtons(); + } + + public override void OnUpdate(float elapsed) + { + base.OnUpdate(elapsed); + + _songButtons.TryGetValue(app.Engine.Music.Playing, out Button playingButton); + if (playingButton is not null) + { + playingButton.Font = _fonts.Blue; + playingButton.HoverFont = _fonts.Orange; + } + + if (_lastPlayingButton is not null && _lastPlayingButton != playingButton) + { + _lastPlayingButton.Font = _fonts.Green; + _lastPlayingButton.HoverFont = _fonts.Orange; + } + + _lastPlayingButton = playingButton; + } + + static string Capitalize(string str) + { + var letters = str.ToCharArray(); + letters[0] = char.ToUpper(str[0]); + return new string(letters); + } + + void CreateSongButtons() + { + foreach (var button in _songButtons.Values) + Windows -= button; + _songButtons.Clear(); + + int counter = 0; + + foreach (var song in app.Engine.Music.Songlist) + { + int y = counter % 8; + int x = (counter - counter % 8) / 8; + + x = 38 + x * 44; + y = 58 + y * 10; + + Windows += _songButtons[song] = new Button(app, () => PlaySong(song)) + { + Position = new Vector2(x, y), + Text = Capitalize(song), + Font = _fonts.Green, + HoverFont = _fonts.Orange, + IsTextOnly = true + }; + + counter++; + } + } + + void PlaySong(string song) + { + if (BurntimeClassic.Instance.MusicMode == BurntimeClassic.MusicModes.Off) + BurntimeClassic.Instance.CycleMusicMode(); + + app.Engine.Music.Play(song); + } +} diff --git a/source/Burntime.Remaster/Scenes/OptionsSavesPage.cs b/source/Burntime.Remaster/Scenes/OptionsSavesPage.cs new file mode 100644 index 0000000..0db0465 --- /dev/null +++ b/source/Burntime.Remaster/Scenes/OptionsSavesPage.cs @@ -0,0 +1,186 @@ +using Burntime.Framework; +using Burntime.Framework.GUI; +using Burntime.Platform; +using Burntime.Platform.IO; +using Burntime.Remaster; +using Burntime.Remaster.Logic.Generation; + +namespace Burntime.Classic.Scenes; + +internal struct OptionFonts +{ + public GuiFont Disabled; + + public GuiFont Orange; + public GuiFont Green; + public GuiFont Blue; +} + +internal class OptionsSavesPage : Container +{ + readonly OptionFonts _fonts; + + readonly SavegameInputWindow _input; + readonly Button _load; + readonly Button _save; + readonly Button _delete; + + const int COLUMN_HEIGHT = 5; + readonly Button[] savegames = new Button[COLUMN_HEIGHT * 2]; + + public OptionsSavesPage(Module app, OptionFonts fonts) : base(app) + { + _fonts = fonts; + + Windows += _load = new Button(app) + { + Font = _fonts.Blue, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Text = "@burn?382", + Position = new Vector2(40, 124), + IsTextOnly = true + }; + _load.Command += OnLoad; + Windows += _save = new Button(app) + { + Font = _fonts.Blue, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Text = "@burn?383", + Position = new Vector2(74, 124), + IsTextOnly = true + }; + _save.Command += OnSave; + Windows += _delete = new Button(app) + { + Font = _fonts.Blue, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Text = "@burn?384", + Position = new Vector2(126, 124), + IsTextOnly = true + }; + _delete.Command += OnDelete; + + Windows += _input = new SavegameInputWindow(app) + { + Font = _fonts.Blue, + Position = new Vector2(40, 112), + Size = new Vector2(120, 10), + TextHorizontalAlign = Platform.Graphics.TextAlignment.Center, + TextVerticalAlign = Platform.Graphics.VerticalTextAlignment.Top + }; + + CreateSaveGameButtons(); + } + + public override void OnUpdate(float elapsed) + { + if (_load.IsHover) + _input.Mode = SavegameMode.Load; + else if (_save.IsHover) + _input.Mode = SavegameMode.Save; + else if (_delete.IsHover) + _input.Mode = SavegameMode.Delete; + else + _input.Mode = SavegameMode.None; + + _save.IsEnabled = CanSave; + _load.IsEnabled = CanLoad; + _delete.IsEnabled = CanDelete; + + base.OnUpdate(elapsed); + } + + public void RefreshSaveGames() + { + _input.Name = ""; + + string[] files = FileSystem.GetFileNames("saves/", ".sav"); + + for (int i = 0; i < savegames.Length; i++) + { + if (files.Length > i) + { + var game = new SaveGame("saves/" + files[i]); + + savegames[i].Text = files[i].ToUpper(); + savegames[i].Font = game.Version == BurntimeClassic.SavegameVersion ? _fonts.Green : _fonts.Disabled; + savegames[i].IsTextOnly = true; + + game.Close(); + } + else + savegames[i].Text = ""; + } + } + + void CreateSaveGameButtons() + { + int columnHeight = savegames.Length / 2; + for (int i = 0; i < savegames.Length; i++) + { + int y = i % columnHeight; + int x = (i - i % columnHeight) / columnHeight; + + x = 38 + x * 67; + y = 58 + y * 10; + + savegames[i] = new Button(app) + { + Position = new Vector2(x, y), + Text = "", + Font = _fonts.Green, + HoverFont = _fonts.Orange, + IsTextOnly = true + }; + savegames[i].Command += new CommandHandler(OnSelect, i); + Windows += savegames[i]; + } + } + + void OnSelect(int index) + { + string str = savegames[index].Text; + _input.Name = str[..^4]; + } + + private bool CanSave => (!string.IsNullOrEmpty(_input.Name) && app.Server is not null && app.Server.StateContainer is not null); + void OnSave() + { + if (!CanSave) + return; + + var creation = new GameCreation(app as BurntimeClassic); + creation.SaveGame("saves/" + _input.Name + ".sav"); + + RefreshSaveGames(); + } + + private bool CanLoad => FileSystem.ExistsFile("saves/" + _input.Name + ".sav"); + void OnLoad() + { + if (!CanLoad) + return; + + app.SceneManager.SetScene("WaitScene"); + app.SceneManager.BlockBlendIn(); + + var creation = new GameCreation(app as BurntimeClassic); + if (!creation.LoadGame("saves/" + _input.Name + ".sav")) + app.SceneManager.PreviousScene(); + + app.SceneManager.UnblockBlendIn(); + } + + private bool CanDelete => FileSystem.ExistsFile("saves/" + _input.Name + ".sav"); + void OnDelete() + { + if (!CanDelete) + return; + + FileSystem.RemoveFile("saves/" + _input.Name + ".sav"); + RefreshSaveGames(); + } +} diff --git a/source/Burntime.Remaster/Scenes/OptionsScene.cs b/source/Burntime.Remaster/Scenes/OptionsScene.cs index ee16f81..8707acc 100644 --- a/source/Burntime.Remaster/Scenes/OptionsScene.cs +++ b/source/Burntime.Remaster/Scenes/OptionsScene.cs @@ -3,338 +3,162 @@ using Burntime.Framework.GUI; using Burntime.Remaster.Logic.Generation; using Burntime.Platform.IO; +using Burntime.Classic.Scenes; +using Burntime.Platform.Graphics; -namespace Burntime.Remaster -{ - public class OptionsScene : Scene - { - GuiFont disabled; - GuiFont red; - GuiFont hover; - GuiFont hoverRed; - GuiFont green; - SavegameInputWindow input; - Button load; - Button save; - Button delete; - Button music; - readonly Button _buttonNewGfx; - readonly Button _buttonRestart; - - Button[] savegames = new Button[8]; - - public OptionsScene(Module App) - : base(App) - { - Background = "opti.pac"; - Music = "16_MUS 16_HSC.ogg"; - Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; - - disabled = new GuiFont(BurntimeClassic.FontName, new PixelColor(100, 100, 100)); - red = new GuiFont(BurntimeClassic.FontName, new PixelColor(134, 44, 4)); - hover = new GuiFont(BurntimeClassic.FontName, new PixelColor(109, 117, 170)); - hoverRed = new GuiFont(BurntimeClassic.FontName, new PixelColor(190, 77, 12)); - green = new GuiFont(BurntimeClassic.FontName, new PixelColor(0, 108, 0)); - - Image image = new Image(App); - image.Background = "opt.ani"; - image.Position = new Vector2(0, 4); - Windows += image; - - // menu buttons - Button button = new Button(App); - button.Font = red; - button.HoverFont = hover; - button.Text = "@burn?388"; - button.Position = new Vector2(214, 64); - button.SetTextOnly(); - button.Command += app.SceneManager.PreviousScene; - Windows += button; - button = new Button(App); - if (BurntimeClassic.Instance.DisableMusic) - { - button.Font = disabled; - button.Text = "@burn?389"; - } - else - { - button.Font = red; - button.HoverFont = hover; - button.Text = BurntimeClassic.Instance.MusicPlayback ? "@burn?389" : "@burn?424"; - button.Command += OnButtonMusicSwitch; - } - button.Position = new Vector2(214, 84); - button.SetTextOnly(); - music = button; - Windows += button; +namespace Burntime.Remaster; - Windows += _buttonRestart = new Button(App) - { - Font = red, - HoverFont = hover, - Text = "@burn?390", - Position = new Vector2(214, 105), - IsTextOnly = true - }; - _buttonRestart.Command += OnButtonRestart; - - Windows += _buttonNewGfx = new Button(App) - { - Font = red, - HoverFont = hover, - Text = "@newburn?17", - Position = new Vector2(214, 127), - IsTextOnly = true - }; - _buttonNewGfx.Command += OnButtonNewGfx; - - button = new Button(App); - button.Font = red; - button.HoverFont = hover; - button.Text = "@burn?391"; - button.Position = new Vector2(214, 148); - button.SetTextOnly(); - button.Command += OnButtonExit; - Windows += button; - - // save buttons - load = new Button(App); - load.Font = hover; - load.HoverFont = hoverRed; - load.Text = "@burn?382"; - load.Position = new Vector2(40, 122); - load.SetTextOnly(); - load.Command += OnLoad; - Windows += load; - save = new Button(App); - save.Font = hover; - save.HoverFont = hoverRed; - save.Text = "@burn?383"; - save.Position = new Vector2(74, 122); - save.SetTextOnly(); - save.Command += OnSave; - Windows += save; - delete = new Button(App); - delete.Font = hover; - delete.HoverFont = hoverRed; - delete.Text = "@burn?384"; - delete.Position = new Vector2(126, 122); - delete.SetTextOnly(); - delete.Command += OnDelete; - Windows += delete; - - // savegame name input - input = new SavegameInputWindow(App); - input.Font = hover; - input.Position = new Vector2(40, 108); - input.Size = new Vector2(120, 10); - Windows += input; - - // radio cover - button = new Button(App); - button.Image = "opta.raw?0"; - button.HoverImage = "opta.raw?1"; - button.Position = new Vector2(186, 51); - button.Layer += 2; - Windows += button; - - CreateSaveGameButtons(); - } +public class OptionsScene : Scene +{ + GuiFont disabled; + GuiFont red; + GuiFont hover; + GuiFont hoverRed; + GuiFont green; - public override void OnResizeScreen() - { - base.OnResizeScreen(); + readonly OptionsSavesPage _savesPage; + readonly OptionsSettingsPage _settingsPage; + readonly OptionsGiveUpPage _giveUpPage; + readonly OptionsJukeboxPage _jukeboxPage; - Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; - } + readonly GuiImage _optionsBulb; - protected override void OnActivateScene(object parameter) + Container _activePage; + Container ActivePage + { + set { - RefreshSaveGames(); - input.Name = ""; - _buttonNewGfx.Text = BurntimeClassic.Instance.IsNewGfx ? "@newburn?17" : "@newburn?18"; - - if (app.SceneManager.LastScene == "MenuScene") - { - _buttonRestart.Font = disabled; - _buttonRestart.HoverFont = null; - } - else - { - _buttonRestart.Font = red; - _buttonRestart.HoverFont = hover; - } + if (_activePage is not null) _activePage.IsVisible = false; + if (value is not null) value.IsVisible = true; + _activePage = value; } + } - void RefreshSaveGames() - { - string[] files = Burntime.Platform.IO.FileSystem.GetFileNames("savegame/", ".sav"); - - for (int i = 0; i < 8; i++) - { - if (files.Length > i) - { - SaveGame game = new SaveGame("savegame/" + files[i]); + public OptionsScene(Module App) + : base(App) + { + Background = "opti.pac"; + Music = "radio"; + Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; - savegames[i].Text = files[i].ToUpper(); - savegames[i].Font = game.Version == BurntimeClassic.SavegameVersion ? green : disabled; - savegames[i].SetTextOnly(); + disabled = new GuiFont(BurntimeClassic.FontName, new PixelColor(100, 100, 100)); + red = new GuiFont(BurntimeClassic.FontName, new PixelColor(134, 44, 4)); + hover = new GuiFont(BurntimeClassic.FontName, new PixelColor(109, 117, 170)); + hoverRed = new GuiFont(BurntimeClassic.FontName, new PixelColor(190, 77, 12)); + green = new GuiFont(BurntimeClassic.FontName, new PixelColor(0, 108, 0)); - game.Close(); - } - else - savegames[i].Text = ""; - } - } + _optionsBulb = "gfx/ui/options_bulb.png"; - void CreateSaveGameButtons() + Windows += new Image(App) { - for (int i = 0; i < 8; i++) - { - int y = i % 4; - int x = (i - i % 4) / 4; + Background = "opt.ani", + Position = new Vector2(0, 4) + }; - x = 38 + x * 67; - y = 58 + y * 10; - - savegames[i] = new Button(app); - savegames[i].Position = new Vector2(x, y); - savegames[i].Text = ""; - savegames[i].Font = green; - savegames[i].HoverFont = hoverRed; - savegames[i].SetTextOnly(); - savegames[i].Command += new CommandHandler(OnSelect, i); - Windows += savegames[i]; - } - } - - public override void OnUpdate(float Elapsed) + // menu buttons + Windows += new Button(app, app.SceneManager.PreviousScene) { - if (load.IsHover) - input.Mode = SavegameMode.Load; - else if (save.IsHover) - input.Mode = SavegameMode.Save; - else if (delete.IsHover) - input.Mode = SavegameMode.Delete; - else - input.Mode = SavegameMode.None; - - // can be triggered via F1 or option menu - var classic = BurntimeClassic.Instance; - _buttonNewGfx.Text = classic.IsNewGfx ? "@newburn?17" : "@newburn?18"; - } - - void OnSelect(int index) + Font = red, + HoverFont = hover, + Text = "@burn?388", + Position = new Vector2(214, 64), + IsTextOnly = true + }; + + Windows += new Button(app, () => ActivePage = _savesPage) { - string str = savegames[index].Text; - input.Name = str.Substring(0, str.Length - 4); - } - - void OnSave() + Font = red, + HoverFont = hover, + Text = "@newburn?21", + Position = new Vector2(214, 84), + IsTextOnly = true + }; + + Windows += new Button(app, () => ActivePage = _jukeboxPage) { - if (input.Name == "") - return; - - if (app.Server != null && app.Server.StateContainer != null) - { - GameCreation creation = new GameCreation(app as BurntimeClassic); - creation.SaveGame("savegame/" + input.Name + ".sav"); - } - - input.Name = ""; - RefreshSaveGames(); - } - - void OnLoad() + Font = red, + HoverFont = hover, + DisabledFont = disabled, + IsEnabled = !BurntimeClassic.Instance.DisableMusic, + Text = "@newburn?29", + Position = new Vector2(214, 105), + IsTextOnly = true + }; + + Windows += new Button(app, () => ActivePage = _settingsPage) { - if (Burntime.Platform.IO.FileSystem.ExistsFile("savegame/" + input.Name + ".sav")) - { - app.SceneManager.SetScene("WaitScene"); - app.SceneManager.BlockBlendIn(); - - GameCreation creation = new GameCreation(app as BurntimeClassic); - if (!creation.LoadGame("savegame/" + input.Name + ".sav")) - app.SceneManager.PreviousScene(); - - app.SceneManager.UnblockBlendIn(); - } - - input.Name = ""; - RefreshSaveGames(); - } - - void OnDelete() + Font = red, + HoverFont = hover, + Text = "@newburn?22", + Position = new Vector2(214, 127), + IsTextOnly = true + }; + + Windows += new Button(app, () => ActivePage = _giveUpPage) { - FileSystem.RemoveFile("savegame/" + input.Name + ".sav"); - - input.Name = ""; - RefreshSaveGames(); - } - - void OnButtonMusicSwitch() + Font = red, + HoverFont = hover, + Text = "@newburn?27", + Position = new Vector2(214, 148), + IsTextOnly = true + }; + + // radio cover + Windows += new Button(app) { - if (!BurntimeClassic.Instance.DisableMusic) - { - BurntimeClassic.Instance.MusicPlayback = !BurntimeClassic.Instance.MusicPlayback; - music.Text = BurntimeClassic.Instance.MusicPlayback ? "@burn?389" : "@burn?424"; - music.SetTextOnly(); + Image = "opta.raw?0", + HoverImage = "opta.raw?1", + Position = new Vector2(186, 51) + }; + Windows.Last.Layer += 2; - if (BurntimeClassic.Instance.MusicPlayback) - { - // start music - app.Engine.Music.Enabled = true; - app.Engine.Music.Play(Music); - } - else - { - // stop music - app.Engine.Music.Enabled = false; - app.Engine.Music.Stop(); - } - } - } - - void OnButtonRestart() + var fonts = new OptionFonts() { - if (app.SceneManager.LastScene == "MenuScene") return; + Disabled = disabled, + Green = green, + Blue = hover, + Orange = hoverRed + }; + + Windows += _savesPage = new OptionsSavesPage(app, fonts) { IsVisible = false }; + Windows += _settingsPage = new OptionsSettingsPage(app, fonts) { IsVisible = false }; + Windows += _giveUpPage = new OptionsGiveUpPage(app, fonts) { IsVisible = false }; + Windows += _jukeboxPage = new OptionsJukeboxPage(app, fonts) { IsVisible = false }; + ActivePage = _savesPage; + } - app.StopGame(); - app.SceneManager.SetScene("MenuScene"); - } + public override void OnResizeScreen() + { + base.OnResizeScreen(); + Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; + } + + protected override void OnActivateScene(object parameter) + { + ActivePage = _savesPage; + _savesPage.RefreshSaveGames(); + } - void OnButtonNewGfx() + public override void OnRender(RenderTarget target) + { + var position = new Vector2(192, 59); + if (_activePage == _savesPage) { - var classic = BurntimeClassic.Instance; + position.y += 20; + position.x -= 1; + } + else if (_activePage == _jukeboxPage) + position.y += 20 * 2 + 1; + else if (_activePage == _settingsPage) + position.y += 21 * 3; + else if (_activePage == _giveUpPage) + position.y += 21 * 4; - classic.IsNewGfx = !classic.IsNewGfx; - if (classic.IsNewGfx) - { - FileSystem.AddPackage("newgfx", "game/classic_newgfx"); - if (FileSystem.ExistsFile("newgfx.txt")) - { - classic.ResourceManager.SetResourceReplacement("newgfx.txt"); + target.Layer++; + target.DrawSprite(position, _optionsBulb); + target.Layer--; - // use highres font anyway - if (FileSystem.ExistsFile("highres-font.txt")) - BurntimeClassic.FontName = "highres-font.txt"; - } - else - { - classic.ResourceManager.SetResourceReplacement(null); - } - classic.Engine.ReloadGraphics(); - } - else - { - FileSystem.RemovePackage("newgfx"); - classic.ResourceManager.SetResourceReplacement(null); - classic.Engine.ReloadGraphics(); - } - } - void OnButtonExit() - { - app.Close(); - } + base.OnRender(target); } } diff --git a/source/Burntime.Remaster/Scenes/OptionsSettingsPage.cs b/source/Burntime.Remaster/Scenes/OptionsSettingsPage.cs new file mode 100644 index 0000000..9676007 --- /dev/null +++ b/source/Burntime.Remaster/Scenes/OptionsSettingsPage.cs @@ -0,0 +1,98 @@ +using Burntime.Framework; +using Burntime.Framework.GUI; +using Burntime.Platform; +using Burntime.Remaster; + +namespace Burntime.Classic.Scenes; + +internal class OptionsSettingsPage : Container +{ + readonly OptionFonts _fonts; + + readonly Button _musicToggle; + readonly Button _newgfxToggle; + readonly Button _fullscreenToggle; + readonly Button _languageToggle; + + readonly Button _hintText; + + public OptionsSettingsPage(Module app, OptionFonts fonts) : base(app) + { + _fonts = fonts; + + Windows += _musicToggle = new Button(app, () => BurntimeClassic.Instance.CycleMusicMode()) + { + Font = _fonts.Green, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Position = new Vector2(38, 68), + IsTextOnly = true, + IsEnabled = !BurntimeClassic.Instance.DisableMusic + }; + Windows += _newgfxToggle = new Button(app, () => app.IsNewGfx = !app.IsNewGfx) + { + Font = _fonts.Green, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Position = new Vector2(38, 58), + IsTextOnly = true + }; + Windows += _fullscreenToggle = new Button(app, () => app.Engine.IsFullscreen = !app.Engine.IsFullscreen) + { + Font = _fonts.Green, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Position = new Vector2(38, 78), + IsTextOnly = true + }; + Windows += _languageToggle = new Button(app, () => app.Language = app.Language == "de" ? "en" : "de") + { + Font = _fonts.Green, + HoverFont = _fonts.Orange, + DisabledFont = _fonts.Disabled, + Text = "@newburn?26", + Position = new Vector2(38, 98), + IsTextOnly = true + }; + Windows += _hintText = new Button(app) + { + Font = _fonts.Blue, + Position = new Vector2(40, 122), + Size = new Vector2(120, 10), + TextHorizontalAlign = Platform.Graphics.TextAlignment.Center + }; + } + + public override void OnUpdate(float elapsed) + { + // some options can be triggered via key shortcut + _newgfxToggle.Text = app.IsNewGfx ? "@newburn?17" : "@newburn?18"; + _musicToggle.Text = BurntimeClassic.Instance.MusicMode switch + { + BurntimeClassic.MusicModes.Amiga => "@newburn?30", + BurntimeClassic.MusicModes.Dos => "@newburn?31", + BurntimeClassic.MusicModes.Remaster => "@newburn?32", + _ => "@burn?424", + }; + _fullscreenToggle.Text = app.Engine.IsFullscreen ? "@newburn?19" : "@newburn?20"; + + if (_fullscreenToggle.IsHover) + { + _hintText.Text = "@newburn?23"; + } + else if (_newgfxToggle.IsHover) + { + _hintText.Text = "@newburn?24"; + } + else if (_musicToggle.IsHover) + { + _hintText.Text = "@newburn?25"; + } + else + { + _hintText.Text = ""; + } + + base.OnUpdate(elapsed); + } +} diff --git a/source/Burntime.Remaster/Scenes/PubScene.cs b/source/Burntime.Remaster/Scenes/PubScene.cs index c8f42a0..b941307 100644 --- a/source/Burntime.Remaster/Scenes/PubScene.cs +++ b/source/Burntime.Remaster/Scenes/PubScene.cs @@ -24,7 +24,7 @@ public PubScene(Module app) { BurntimeClassic classic = app as BurntimeClassic; Background = classic.InventoryBackground == 14 ? "bar.pac" : "pub1.pac"; - Music = "19_MUS 19_HSC.ogg"; + Music = "pub"; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; if (classic.InventoryBackground == 14) diff --git a/source/Burntime.Remaster/Scenes/RestaurantScene.cs b/source/Burntime.Remaster/Scenes/RestaurantScene.cs index a4579bf..a9954e6 100644 --- a/source/Burntime.Remaster/Scenes/RestaurantScene.cs +++ b/source/Burntime.Remaster/Scenes/RestaurantScene.cs @@ -23,7 +23,7 @@ class RestaurantScene : Scene public RestaurantScene(Module app) : base(app) { - Music = "01_MUS 01_HSC.ogg"; + Music = "diner"; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; BurntimeClassic classic = app as BurntimeClassic; diff --git a/source/Burntime.Remaster/Scenes/StatisticsScene.cs b/source/Burntime.Remaster/Scenes/StatisticsScene.cs index 5199649..8c84cfa 100644 --- a/source/Burntime.Remaster/Scenes/StatisticsScene.cs +++ b/source/Burntime.Remaster/Scenes/StatisticsScene.cs @@ -38,7 +38,7 @@ public StatisticsScene(Module App) { //Background = "blz.pac"; Size = new Vector2(320, 200); - Music = "17_MUS 17_HSC.ogg"; + Music = "score"; Position = (app.Engine.Resolution.Game - Size) / 2; font = new GuiFont(BurntimeClassic.FontName, new PixelColor(212, 212, 212), new PixelColor(92, 92, 96)); CaptureAllMouseClicks = true; @@ -63,17 +63,19 @@ public override void OnRender(RenderTarget Target) { if (app.IsNewGfx) { - Target.DrawSprite(-Position, _background); - if (app.Engine.Resolution.Game.x > _background.Width) - { -#warning OPTIMIZE avoid duplicating the texture for extra wide background - Target.DrawSprite(-Position + new Vector2(_background.Width, 0), _background2); - } + Vector2 gameSize = app.Engine.Resolution.Game; + gameSize.Max(_background.Size); + + Vector2 offset = (app.Engine.Resolution.Game - _background.Size) / 2; + offset.Min(0); + offset -= Position; + Target.DrawSprite(offset, _background); + Target.Layer++; - Target.DrawSprite(app.Engine.Resolution.Game - _rightBottom.Size - Position + Vector2.One, _rightBottom); - Target.DrawSprite(new Vector2(app.Engine.Resolution.Game.x - _rightTop.Width + 1, 0) - Position, _rightTop); - Target.DrawSprite(-Position, _leftTop); - Target.DrawSprite(new Vector2(0, app.Engine.Resolution.Game.y - _leftBottom.Height + 1) - Position, _leftBottom); + Target.DrawSprite(gameSize - _rightBottom.Size + offset + Vector2.One, _rightBottom); + Target.DrawSprite(new Vector2(gameSize.x - _rightTop.Width + 1, 0) + offset, _rightTop); + Target.DrawSprite(offset, _leftTop); + Target.DrawSprite(new Vector2(0, gameSize.y - _leftBottom.Height + 1) + offset, _leftBottom); } else { diff --git a/source/Burntime.Remaster/Scenes/TraderScene.cs b/source/Burntime.Remaster/Scenes/TraderScene.cs index 2b193de..1fc29e0 100644 --- a/source/Burntime.Remaster/Scenes/TraderScene.cs +++ b/source/Burntime.Remaster/Scenes/TraderScene.cs @@ -1,102 +1,118 @@ -using System; -using System.Collections.Generic; -using System.Text; - +using Burntime.Framework; +using Burntime.Framework.GUI; using Burntime.Platform; using Burntime.Platform.Graphics; -using Burntime.Framework; -using Burntime.Framework.GUI; using Burntime.Remaster.GUI; using Burntime.Remaster.Logic; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Burntime.Remaster.Scenes; -namespace Burntime.Remaster.Scenes +class TraderScene : Scene { - class TraderScene : Scene + InventoryWindow inventory; + InventoryWindow inventoryTrader; + InventorySide side = InventorySide.Left; + Button exitButton; + Button acceptButton; + ExchangeWindow exchangeTop; + ExchangeWindow exchangeBottom; + ItemGridWindow temporarySpace; + + public TraderScene(Module App) + : base(App) { - InventoryWindow inventory; - InventoryWindow inventoryTrader; - InventorySide side = InventorySide.Left; - Button exitButton; - Button acceptButton; - ExchangeWindow exchangeTop; - ExchangeWindow exchangeBottom; - ItemGridWindow temporarySpace; - - public TraderScene(Module App) - : base(App) - { - Background = "hint1.pac"; - Music = "10_MUS 10_HSC.ogg"; - Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; - - inventory = new InventoryWindow(App, InventorySide.Left); - inventory.Position = new Vector2(2, 5); - inventory.LeftClickItemEvent += OnLeftClickItemInventory; - inventory.RightClickItemEvent += OnRightClickItemInventory; - Windows += inventory; - - inventoryTrader = new InventoryWindow(App, InventorySide.Right); - inventoryTrader.Position = new Vector2(154, 5); - inventoryTrader.LeftClickItemEvent += OnLeftClickItemTrader; - inventoryTrader.RightClickItemEvent += OnRightClickItemTrader; - Windows += inventoryTrader; - - exitButton = new Button(App); - exitButton.Position = new Vector2(25, 183); - exitButton.Text = app.ResourceManager.GetString("burn?354"); - exitButton.Font = new GuiFont(BurntimeClassic.FontName, new PixelColor(92, 92, 148)); - exitButton.HoverFont = new GuiFont(BurntimeClassic.FontName, new PixelColor(144, 160, 212)); - exitButton.Command += OnButtonExit; - exitButton.SetTextOnly(); - Windows += exitButton; - - acceptButton = new Button(App); - acceptButton.Position = new Vector2(170, 183); - acceptButton.Text = app.ResourceManager.GetString("burn?353"); - acceptButton.Font = new GuiFont(BurntimeClassic.FontName, new PixelColor(92, 92, 148)); - acceptButton.HoverFont = new GuiFont(BurntimeClassic.FontName, new PixelColor(144, 160, 212)); - acceptButton.Command += OnButtonAccept; - acceptButton.SetTextOnly(); - Windows += acceptButton; - - exchangeTop = new ExchangeWindow(App); - inventoryTrader.Grid.Mask = exchangeTop.Grid; - Windows += exchangeTop; - - exchangeBottom = new ExchangeWindow(App); - inventory.Grid.Mask = exchangeBottom.Grid; - Windows += exchangeBottom; - - temporarySpace = new ItemGridWindow(App); - temporarySpace.Position = new Vector2(156, 0); - temporarySpace.Spacing = new Vector2(0, 1); - temporarySpace.Grid = new Vector2(1, 6); - temporarySpace.LeftClickItemEvent += OnClickTemporarySpace; - temporarySpace.RightClickItemEvent += OnClickTemporarySpace; - Windows += temporarySpace; - } + Background = "hint1.pac"; + Music = "trader"; + + inventory = new InventoryWindow(App, InventorySide.Left); + inventory.Position = new Vector2(2, 5); + inventory.LeftClickItemEvent += OnLeftClickItemInventory; + inventory.RightClickItemEvent += OnRightClickItemInventory; + Windows += inventory; + + inventoryTrader = new InventoryWindow(App, InventorySide.Right); + inventoryTrader.Position = new Vector2(154, 5); + inventoryTrader.LeftClickItemEvent += OnLeftClickItemTrader; + inventoryTrader.RightClickItemEvent += OnRightClickItemTrader; + Windows += inventoryTrader; + + exitButton = new Button(App); + exitButton.Position = new Vector2(25, 183); + exitButton.Text = app.ResourceManager.GetString("burn?354"); + exitButton.Font = new GuiFont(BurntimeClassic.FontName, new PixelColor(92, 92, 148)); + exitButton.HoverFont = new GuiFont(BurntimeClassic.FontName, new PixelColor(144, 160, 212)); + exitButton.Command += OnButtonExit; + exitButton.IsTextOnly = true; + Windows += exitButton; + + acceptButton = new Button(App); + acceptButton.Position = new Vector2(170, 183); + acceptButton.Text = app.ResourceManager.GetString("burn?353"); + acceptButton.Font = new GuiFont(BurntimeClassic.FontName, new PixelColor(92, 92, 148)); + acceptButton.HoverFont = new GuiFont(BurntimeClassic.FontName, new PixelColor(144, 160, 212)); + acceptButton.Command += OnButtonAccept; + acceptButton.IsTextOnly = true; + Windows += acceptButton; + + exchangeTop = new ExchangeWindow(App); + inventoryTrader.Grid.Mask = exchangeTop.Grid; + exchangeTop.LeftClickItemEvent += OnLeftClickItemTrader; + Windows += exchangeTop; + + exchangeBottom = new ExchangeWindow(App); + inventory.Grid.Mask = exchangeBottom.Grid; + exchangeBottom.LeftClickItemEvent += OnLeftClickItemInventory; + Windows += exchangeBottom; + + temporarySpace = new ItemGridWindow(App); + temporarySpace.Position = new Vector2(156, 0); + temporarySpace.Spacing = new Vector2(0, 1); + temporarySpace.Grid = new Vector2(1, 6); + temporarySpace.LeftClickItemEvent += OnClickTemporarySpace; + temporarySpace.RightClickItemEvent += OnClickTemporarySpace; + Windows += temporarySpace; + + PositionElements(); + } - public override void OnResizeScreen() + Vector2 _lastPosition = Vector2.Zero; + void PositionElements(Vector2? mousePosition = null) + { + if (app.Engine.Resolution.Game.x >= 450) { - base.OnResizeScreen(); + Size = new Vector2(470, 200); + Position = (app.Engine.Resolution.Game - Size) / 2; - Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; - } + inventory.Show(); + exitButton.Show(); + acceptButton.Show(); + inventoryTrader.Show(); + exchangeTop.Position = new Vector2(195, 1); + exchangeBottom.Position = new Vector2(195, 101); + temporarySpace.Show(); - public override void OnRender(RenderTarget Target) - { - base.OnRender(Target); + acceptButton.Position = new Vector2(170, 183) + new Vector2(150, 0); + inventoryTrader.Position = new Vector2(154, 5) + new Vector2(150, 0); - BurntimeClassic classic = app as BurntimeClassic; + side = InventorySide.None; } - - public override bool OnMouseMove(Vector2 Position) + else { - InventorySide newside = (Position.x >= (side != InventorySide.Left ? 120 : 200)) ? InventorySide.Right : InventorySide.Left; + Size = new Vector2(320, 200); + Position = (app.Engine.Resolution.Game - Size) / 2; + acceptButton.Position = new Vector2(170, 183); + inventoryTrader.Position = new Vector2(154, 5); + + InventorySide newside = ((mousePosition ?? _lastPosition).x >= (side != InventorySide.Left ? 120 : 200)) ? InventorySide.Right : InventorySide.Left; if (newside != side) { - if (newside == InventorySide.Left) + side = newside; + + if (side == InventorySide.Left) { inventory.Show(); exitButton.Show(); @@ -116,119 +132,140 @@ public override bool OnMouseMove(Vector2 Position) exchangeBottom.Position = new Vector2(2, 101); temporarySpace.Hide(); } - side = newside; } - - return base.OnMouseMove(Position); } - protected override void OnActivateScene(object parameter) - { - BurntimeClassic classic = app as BurntimeClassic; - inventory.SetGroup(classic.SelectedCharacter); - inventoryTrader.SetGroup(classic.Game.World.ActiveTraderObj); - exchangeTop.Title = classic.Game.World.ActiveTraderObj.Name; - exchangeTop.ExchangeResult = ExchangeResult.Ng; - exchangeBottom.Title = classic.Game.World.ActivePlayerObj.Name; - exchangeBottom.ExchangeResult = ExchangeResult.None; + if (mousePosition.HasValue) + _lastPosition = mousePosition.Value; + } - temporarySpace.Clear(); + public override void OnResizeScreen() + { + base.OnResizeScreen(); - side = InventorySide.None; - } + PositionElements(); + } - void OnButtonExit() - { - exchangeTop.Grid.Clear(); - exchangeBottom.Grid.Clear(); + public override void OnRender(RenderTarget Target) + { + base.OnRender(Target); - app.SceneManager.PreviousScene(); - } + BurntimeClassic classic = app as BurntimeClassic; + } - void OnButtonAccept() - { - if (exchangeTop.ExchangeResult == ExchangeResult.Ng) - return; + public override bool OnMouseMove(Vector2 position) + { + PositionElements(position); - BurntimeClassic classic = app as BurntimeClassic; + return base.OnMouseMove(position); + } - // remove items in exchange place from parties - foreach (Character chr in inventory.ActiveCharacter.GetGroup()) - chr.Items.Remove(exchangeBottom.Grid); - classic.Game.World.ActiveTraderObj.Items.Remove(exchangeTop.Grid); + protected override void OnActivateScene(object parameter) + { + BurntimeClassic classic = app as BurntimeClassic; + inventory.SetGroup(classic.SelectedCharacter); + inventoryTrader.SetGroup(classic.Game.World.ActiveTraderObj); + exchangeTop.Title = classic.Game.World.ActiveTraderObj.Name; + exchangeTop.ExchangeResult = ExchangeResult.Ng; + exchangeBottom.Title = classic.Game.World.ActivePlayerObj.Name; + exchangeBottom.ExchangeResult = ExchangeResult.None; - // move items from exchange place to parties - inventory.ActiveCharacter.GetGroup().MoveItems(exchangeTop.Grid); - classic.Game.World.ActiveTraderObj.GetGroup().MoveItems(exchangeBottom.Grid); + temporarySpace.Clear(); - exchangeTop.Grid.Clear(); - exchangeBottom.Grid.Clear(); + side = InventorySide.None; + } - inventory.OnSelectPage(); - inventoryTrader.OnSelectPage(); + void OnButtonExit() + { + exchangeTop.Grid.Clear(); + exchangeBottom.Grid.Clear(); - exchangeTop.ExchangeResult = ExchangeResult.Ng; - } + app.SceneManager.PreviousScene(); + } - void OnLeftClickItemInventory(Framework.States.StateObject State) - { - if (exchangeBottom.Grid.Contains(State as Item)) - exchangeBottom.Grid.Remove(State as Item); - else - exchangeBottom.Grid.Add(State as Item); + void OnButtonAccept() + { + if (exchangeTop.ExchangeResult == ExchangeResult.Ng) + return; - exchangeTop.ExchangeResult = CheckTrade(); - } + BurntimeClassic classic = app as BurntimeClassic; - void OnRightClickItemInventory(Framework.States.StateObject state) - { - if (temporarySpace.MaxCount - temporarySpace.Count <= 0) - return; - - Item item = state as Item; - if (exchangeBottom.Grid.Contains(item)) - return; - inventory.ActiveCharacter.Items.Remove(item); - inventory.OnSelectPage(); - temporarySpace.Add(item); - } + // remove items in exchange place from parties + foreach (Character chr in inventory.ActiveCharacter.GetGroup()) + chr.Items.Remove(exchangeBottom.Grid); + classic.Game.World.ActiveTraderObj.Items.Remove(exchangeTop.Grid); - void OnLeftClickItemTrader(Framework.States.StateObject State) - { - if (exchangeTop.Grid.Contains(State as Item)) - exchangeTop.Grid.Remove(State as Item); - else - exchangeTop.Grid.Add(State as Item); + // move items from exchange place to parties + inventory.ActiveCharacter.GetGroup().MoveItems(exchangeTop.Grid); + classic.Game.World.ActiveTraderObj.GetGroup().MoveItems(exchangeBottom.Grid); - exchangeTop.ExchangeResult = CheckTrade(); - } + exchangeTop.Grid.Clear(); + exchangeBottom.Grid.Clear(); - void OnRightClickItemTrader(Framework.States.StateObject State) - { - } + inventory.OnSelectPage(); + inventoryTrader.OnSelectPage(); - void OnClickTemporarySpace(Framework.States.StateObject state) - { - if (inventory.Grid.MaxCount - inventory.Grid.Count <= 0) - return; + exchangeTop.ExchangeResult = ExchangeResult.Ng; + } - Item item = state as Item; - inventory.ActiveCharacter.Items.Add(item); - inventory.OnSelectPage(); - temporarySpace.Remove(item); - } + void OnLeftClickItemInventory(Framework.States.StateObject State) + { + if (exchangeBottom.Grid.Contains(State as Item)) + exchangeBottom.Grid.Remove(State as Item); + else + exchangeBottom.Grid.Add(State as Item); - ExchangeResult CheckTrade() - { - int player = exchangeBottom.Grid.GetTradeValue(); - int trader = exchangeTop.Grid.GetTradeValue(); - ExchangeResult result = (player >= trader && exchangeBottom.Grid.Count > 0) ? ExchangeResult.Ok : ExchangeResult.Ng; - if (exchangeTop.Grid.Count - exchangeBottom.Grid.Count > inventory.FreeSlots) - result = ExchangeResult.Ng; - if (exchangeBottom.Grid.Count - exchangeTop.Grid.Count > inventoryTrader.FreeSlots) - result = ExchangeResult.Ng; - - return result; - } + exchangeTop.ExchangeResult = CheckTrade(); + } + + void OnRightClickItemInventory(Framework.States.StateObject state) + { + if (temporarySpace.MaxCount - temporarySpace.Count <= 0) + return; + + Item item = state as Item; + if (exchangeBottom.Grid.Contains(item)) + return; + inventory.ActiveCharacter.Items.Remove(item); + inventory.OnSelectPage(); + temporarySpace.Add(item); + } + + void OnLeftClickItemTrader(Framework.States.StateObject State) + { + if (exchangeTop.Grid.Contains(State as Item)) + exchangeTop.Grid.Remove(State as Item); + else + exchangeTop.Grid.Add(State as Item); + + exchangeTop.ExchangeResult = CheckTrade(); + } + + void OnRightClickItemTrader(Framework.States.StateObject State) + { + } + + void OnClickTemporarySpace(Framework.States.StateObject state) + { + if (inventory.Grid.MaxCount - inventory.Grid.Count <= 0) + return; + + Item item = state as Item; + inventory.ActiveCharacter.Items.Add(item); + inventory.OnSelectPage(); + temporarySpace.Remove(item); + } + + ExchangeResult CheckTrade() + { + int player = exchangeBottom.Grid.GetTradeValue(); + int trader = exchangeTop.Grid.GetTradeValue(); + ExchangeResult result = (player >= trader && exchangeBottom.Grid.Count > 0) ? ExchangeResult.Ok : ExchangeResult.Ng; + if (exchangeTop.Grid.Count - exchangeBottom.Grid.Count > inventory.FreeSlots) + result = ExchangeResult.Ng; + if (exchangeBottom.Grid.Count - exchangeTop.Grid.Count > inventoryTrader.FreeSlots) + result = ExchangeResult.Ng; + + return result; } } diff --git a/source/Burntime.Remaster/Scenes/VictoryScene.cs b/source/Burntime.Remaster/Scenes/VictoryScene.cs index d5dbb09..842e7a0 100644 --- a/source/Burntime.Remaster/Scenes/VictoryScene.cs +++ b/source/Burntime.Remaster/Scenes/VictoryScene.cs @@ -18,7 +18,7 @@ public VictoryScene(Module app) : base(app) { Background = "film_01.pac"; - Music = "11_MUS 11_HSC.ogg"; + Music = "victory"; CaptureAllMouseClicks = true; Position = (app.Engine.Resolution.Game - new Vector2(320, 200)) / 2; diff --git a/source/Burntime.sln b/source/Burntime.sln index 04a402a..5bdbda3 100644 --- a/source/Burntime.sln +++ b/source/Burntime.sln @@ -146,8 +146,8 @@ Global {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Debug|ARM.Build.0 = Debug|x86 {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Debug|x64.ActiveCfg = Debug|x86 {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Debug|x64.Build.0 = Debug|x86 - {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Debug|x86.ActiveCfg = Debug|x86 - {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Debug|x86.Build.0 = Debug|x86 + {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Debug|x86.ActiveCfg = Debug|Any CPU + {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Debug|x86.Build.0 = Debug|Any CPU {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Release|Any CPU.ActiveCfg = Release|x86 {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Release|Any CPU.Build.0 = Release|x86 {74955E9B-D49A-4883-A266-F9E6D6A4E42E}.Release|ARM.ActiveCfg = Release|x86 diff --git a/source/PakConverter/PakConverter.csproj b/source/PakConverter/PakConverter.csproj index f1131a9..83136c7 100644 --- a/source/PakConverter/PakConverter.csproj +++ b/source/PakConverter/PakConverter.csproj @@ -1,31 +1,23 @@  - WinExe + Exe net6.0 - False - disable + PakConverter.Program + false + false PakConverter PakConverter - x86 false + false - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - prompt - false - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - prompt - false + + ..\..\bin\tools + true + true + Jakob Harder + Jakob Harder + Burntime PakConverter + enable