Compare commits

...

7 Commits

@ -11,7 +11,7 @@ namespace CMSGame
{
protected Dictionary<Type, GameSettings> CurrentSettings = new();
protected Dictionary<Type, GameSettings> OriginalSettings = new();
protected Dictionary<Type, GameSettings> PreviousSettings = new();
protected Dictionary<Type, string> SettingsPaths = new();
@ -24,12 +24,9 @@ namespace CMSGame
public GameSettingsNode()
{
RegisterAllSettings();
}
public override void _Ready()
{
MakeDirectories();
LoadAllSettings();
ApplyVideoSettings();
}
protected void RegisterAllSettings()
@ -58,26 +55,26 @@ namespace CMSGame
{
foreach (var settingsType in SettingsPaths.Keys)
{
if (!OriginalSettings.ContainsKey(settingsType) || OriginalSettings[settingsType] != CurrentSettings[settingsType])
if (!PreviousSettings.ContainsKey(settingsType) || PreviousSettings[settingsType] != CurrentSettings[settingsType])
{
SaveSettings(settingsType);
}
}
}
private static void MakeDirectories()
protected static void MakeDirectories()
{
DirAccess.MakeDirRecursiveAbsolute("user://Settings/");
}
private void LoadSettings(Type settingsType)
protected void LoadSettings(Type settingsType)
{
string settingsText = ReadFileAsString(SettingsPaths[settingsType]);
var settings = JsonConvert.DeserializeObject(settingsText, settingsType);
if (settings != null)
{
OriginalSettings[settingsType] = (GameSettings)settings;
CurrentSettings[settingsType] = (GameSettings)settings;
PreviousSettings[settingsType] = (GameSettings)settings;
CurrentSettings[settingsType] = (GameSettings)settings with { };
}
}
@ -86,7 +83,7 @@ namespace CMSGame
return (TSettings)CurrentSettings[typeof(TSettings)];
}
private static string ReadFileAsString(string path)
protected static string ReadFileAsString(string path)
{
if (FileAccess.FileExists(path))
{
@ -96,11 +93,16 @@ namespace CMSGame
return "null";
}
private void SaveSettings(Type settingsType)
protected void SaveSettings(Type settingsType)
{
string settingsText = JsonConvert.SerializeObject(CurrentSettings[settingsType]);
using var file = FileAccess.Open(SettingsPaths[settingsType], FileAccess.ModeFlags.Write);
file.StoreString(settingsText);
}
protected void ApplyVideoSettings()
{
DisplayServerHelper.ApplyResolutionSettings(VideoSettings.UseFullScreen);
}
}
}

@ -0,0 +1,7 @@
# 更新日志
## [未发布]
## [1.0.0] - 2023-06-03
- 第一个测试场景

@ -1,6 +1,6 @@
namespace CMSGame
{
public partial class AudioSettingsMenu : VBoxContainer
public partial class AudioSettingsMenu : Control
{
public GameSettingsNode? Settings;

@ -1,10 +1,9 @@
namespace CMSGame
{
public partial class BattleSettingsMenu : VBoxContainer
{
public override void _Ready()
{
}
}
public partial class BattleSettingsMenu : Control
{
public override void _Ready()
{
}
}
}

@ -1,12 +1,12 @@
namespace CMSGame
{
public partial class DeveloperOptionsMenu : VBoxContainer
public partial class DeveloperOptionsMenu : Control
{
public override void _Ready()
{
}
public void On_OpenUserDataDirButton_Pressed()
public static void On_OpenUserDataDirButton_Pressed()
{
OS.ShellOpen(new GodotPath("user://"));
}

@ -2,11 +2,28 @@ namespace CMSGame
{
public partial class SettingsMenu : TabContainer
{
private static bool s_inDevelopment => OS.HasFeature("debug") || OS.HasFeature("editor");
public GameSettingsNode? Settings;
public Control? BattleSettingsMenu;
public Control? VideoSettingsMenu;
public Control? AudioSettingsMenu;
public Control? DeveloperOptionsMenu;
public override void _Ready()
{
this.GetAutoloadNode(ref Settings, nameof(GameSettingsNode));
this.GetUniqueNode(ref BattleSettingsMenu, nameof(BattleSettingsMenu));
this.GetUniqueNode(ref VideoSettingsMenu, nameof(VideoSettingsMenu));
this.GetUniqueNode(ref AudioSettingsMenu, nameof(AudioSettingsMenu));
this.GetUniqueNode(ref DeveloperOptionsMenu, nameof(DeveloperOptionsMenu));
DeveloperOptionsMenu!.Visible = s_inDevelopment;
}
}
}

@ -14,18 +14,46 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_av57b")
[node name="BattleSettingsMenu" type="VBoxContainer" parent="."]
[node name="BattleSettingsMenu" type="Control" parent="."]
unique_name_in_owner = true
layout_mode = 2
script = ExtResource("2_xtjo5")
[node name="VideoSettingsMenu" type="VBoxContainer" parent="."]
[node name="GridContainer" type="GridContainer" parent="BattleSettingsMenu"]
layout_mode = 0
offset_right = 40.0
offset_bottom = 40.0
columns = 2
[node name="Label" type="Label" parent="BattleSettingsMenu/GridContainer"]
layout_mode = 2
text = "跳过非玩家回合"
[node name="SkipNonPlayerTurnCheckButton" type="CheckButton" parent="BattleSettingsMenu/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="VideoSettingsMenu" type="Control" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
script = ExtResource("3_68iki")
[node name="AudioSettingsMenu" type="VBoxContainer" parent="."]
[node name="GridContainer" type="GridContainer" parent="VideoSettingsMenu"]
layout_mode = 0
offset_right = 40.0
offset_bottom = 40.0
columns = 2
[node name="Label" type="Label" parent="VideoSettingsMenu/GridContainer"]
layout_mode = 2
text = "全屏"
[node name="FullScreenCheckButton" type="CheckButton" parent="VideoSettingsMenu/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="AudioSettingsMenu" type="Control" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
@ -69,13 +97,25 @@ max_value = 1.0
step = 0.01
value = 0.8
[node name="DeveloperOptionsMenu" type="VBoxContainer" parent="."]
[node name="DeveloperOptionsMenu" type="Control" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
script = ExtResource("2_dgytf")
[node name="OpenUserDataDirButton" type="Button" parent="DeveloperOptionsMenu"]
[node name="VBoxContainer" type="VBoxContainer" parent="DeveloperOptionsMenu"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -68.0
offset_right = 68.0
offset_bottom = 31.0
grow_horizontal = 2
size_flags_horizontal = 4
[node name="OpenUserDataDirButton" type="Button" parent="DeveloperOptionsMenu/VBoxContainer"]
layout_mode = 2
text = "打开用户数据目录"
[connection signal="pressed" from="DeveloperOptionsMenu/OpenUserDataDirButton" to="DeveloperOptionsMenu" method="On_OpenUserDataDirButton_Pressed"]
[connection signal="pressed" from="DeveloperOptionsMenu/VBoxContainer/OpenUserDataDirButton" to="DeveloperOptionsMenu" method="On_OpenUserDataDirButton_Pressed"]

@ -1,10 +1,26 @@
namespace CMSGame
{
public partial class VideoSettingsMenu : VBoxContainer
{
public override void _Ready()
{
}
}
}
public partial class VideoSettingsMenu : Control
{
public VideoSettings? Settings;
public CheckButton? FullScreenCheckButton;
public override void _Ready()
{
Settings = this.GetAutoloadNode<GameSettingsNode>(nameof(GameSettingsNode)).VideoSettings;
this.GetUniqueNode(ref FullScreenCheckButton, nameof(FullScreenCheckButton));
FullScreenCheckButton!.Toggled += FullScreenCheckButton_Toggled;
FullScreenCheckButton.SetPressedNoSignal(Settings.UseFullScreen);
}
private void FullScreenCheckButton_Toggled(bool buttonPressed)
{
Settings!.UseFullScreen = buttonPressed;
DisplayServerHelper.ApplyResolutionSettings(buttonPressed);
}
}
}

@ -1,5 +1,4 @@
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.Linq;
global using Godot;

@ -0,0 +1,17 @@
namespace CMSGame
{
internal static class DisplayServerHelper
{
public static void ApplyResolutionSettings(bool useFullScreen)
{
if (useFullScreen)
{
DisplayServer.WindowSetMode(DisplayServer.WindowMode.Fullscreen);
}
else
{
DisplayServer.WindowSetMode(DisplayServer.WindowMode.Windowed);
}
}
}
}

@ -0,0 +1,95 @@
using System.Text;
namespace CMSGame
{
internal record class Changelog
{
public DateOnly? Date { set; get; }
public string Title { set; get; }
public string Description { set; get; }
public Changelog(DateOnly? date, string title, string description)
{
Date = date;
Title = title;
Description = description;
}
}
internal sealed class ChangelogList : List<Changelog>
{
public ChangelogList()
{
try
{
ParseChangelogFile();
}
catch (Exception ex)
{
GD.PushError(ex);
}
}
private void ParseChangelogFile()
{
var file = FileAccess.Open(new GodotPath("res://CHANGELOG.md"), FileAccess.ModeFlags.Read);
var fileContent = file.GetAsText();
if (fileContent == null) return;
Changelog? changelog = null;
StringBuilder contentBuilder = new();
var commitChangelog = () =>
{
if (changelog != null)
{
changelog.Description = contentBuilder.ToString();
contentBuilder.Clear();
this.Add(changelog);
changelog = null;
}
};
foreach (var line in fileContent.Split("\n"))
{
if (line.Trim() == "")
{
continue;
}
if (line.StartsWith("#"))
{
if (line.StartsWith("# ")) // 一号标题
{
continue;
}
if (line.StartsWith("## ")) // 二号标题,格式为 [未发布] 或 [1.0.0] - 2023-06-03
{
string title = line.TrimStart(new char[] { '#', ' ' });
string[] parts = title.Split('-', 2).Select(str => str.Trim()).ToArray();
string versionString = parts[0];
string dateString = "";
if (parts.Length >= 2)
{
dateString = parts[1];
}
commitChangelog();
changelog = new Changelog(dateString != string.Empty ? DateOnly.Parse(dateString) : null, versionString, "");
continue;
}
}
contentBuilder.AppendLine(line.Trim());
}
commitChangelog();
}
}
}

@ -8,18 +8,20 @@ namespace CMSGame
{ }
public record class VideoSettings : GameSettings
{ }
{
public bool UseFullScreen { set; get; } = false;
}
public record class AudioSettings : GameSettings
{
/// <summary>
/// 音乐音量
/// </summary>
public double MusicVolume = 80;
public double MusicVolume { set; get; } = 80;
/// <summary>
/// 音效音量
/// </summary>
public double SoundEffectVolume = 80;
public double SoundEffectVolume { set; get; } = 80;
}
}

@ -10,4 +10,5 @@ Godot 4.0 with Mono (.NET 7.0)
## 许可
代码采用 MIT 协议许可;素材使用 CC-BY-4.0 协议授权。
这是一个使用 Godot 引擎制作的开源游戏,项目中原创的代码采用 MIT 协议许可;原创素材使用 CC-BY-4.0 协议授权。
项目中还使用了来自其他开源项目的代码和素材。这些资产的许可文件已在项目的源码树中列出。

@ -0,0 +1,39 @@
using System.Text;
namespace CMSGame
{
public partial class ChangelogContainer : VBoxContainer
{
private readonly ChangelogList _changelogList = new();
public RichTextLabel? ChangelogLabel;
public override void _Ready()
{
this.GetUniqueNode(ref ChangelogLabel, nameof(ChangelogLabel));
SetLabelText();
}
public void SetLabelText()
{
var changelogText = _changelogList.Select(log =>
{
StringBuilder textBuilder = new();
textBuilder.Append($"[b]{log.Title}[/b]");
if (log.Date != null)
{
textBuilder.Append($" [i]{log.Date}[/i]");
}
textBuilder.Append('\n');
textBuilder.Append(log.Description);
return textBuilder.ToString();
}).ToArray().Join("\n");
ChangelogLabel!.Text = changelogText;
}
}
}

@ -1,6 +1,6 @@
namespace CMSGame
{
public partial class MainScene : Control
public partial class LandingScene : Control
{
public void On_SettingsPopupButton_Pressed()
{

@ -1,8 +1,9 @@
[gd_scene load_steps=4 format=3 uid="uid://c3ovsoq6o7y3t"]
[gd_scene load_steps=5 format=3 uid="uid://c3ovsoq6o7y3t"]
[ext_resource type="Script" path="res://Scenes/MainScene.cs" id="1_kso8c"]
[ext_resource type="Theme" uid="uid://cn55cr5w4yy3n" path="res://Themes/UI.tres" id="1_y72s3"]
[ext_resource type="PackedScene" uid="uid://cslqihnfw0me2" path="res://Components/Settings/SettingsMenuPopup.tscn" id="3_1d7q7"]
[ext_resource type="Script" path="res://Scenes/LandingScene/LandingScene.cs" id="1_m2rl1"]
[ext_resource type="Theme" uid="uid://cn55cr5w4yy3n" path="res://Themes/UI.tres" id="2_js62n"]
[ext_resource type="PackedScene" uid="uid://cslqihnfw0me2" path="res://Components/Settings/SettingsMenuPopup.tscn" id="3_6b4hi"]
[ext_resource type="Script" path="res://Scenes/LandingScene/ChangelogContainer.cs" id="4_7m05h"]
[node name="MainScene" type="Control"]
layout_mode = 3
@ -11,7 +12,7 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_kso8c")
script = ExtResource("1_m2rl1")
[node name="BackgroundImage" type="Label" parent="."]
layout_mode = 1
@ -34,7 +35,7 @@ offset_top = -65.5
offset_right = 287.0
offset_bottom = 65.5
grow_vertical = 2
theme = ExtResource("1_y72s3")
theme = ExtResource("2_js62n")
theme_override_constants/separation = 16
[node name="StartButton" type="Button" parent="StartMenu"]
@ -49,12 +50,46 @@ text = "设置"
layout_mode = 2
text = "退出"
[node name="SettingsMenuPopup" parent="." instance=ExtResource("3_1d7q7")]
[node name="SettingsMenuPopup" parent="." instance=ExtResource("3_6b4hi")]
unique_name_in_owner = true
title = "游戏设置"
visible = false
borderless = false
[node name="ChangelogContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 6
anchor_left = 1.0
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = -450.0
offset_top = -338.0
offset_right = -60.0
offset_bottom = 374.0
grow_horizontal = 0
grow_vertical = 2
script = ExtResource("4_7m05h")
[node name="Label" type="Label" parent="ChangelogContainer"]
layout_mode = 2
theme = ExtResource("2_js62n")
text = "更新日志"
horizontal_alignment = 1
[node name="ScrollContainer" type="ScrollContainer" parent="ChangelogContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="ChangelogLabel" type="RichTextLabel" parent="ChangelogContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
bbcode_enabled = true
text = "[b]标题[/b]
- 更新内容"
[connection signal="pressed" from="StartMenu/StartButton" to="." method="On_BattleDemo1Button_Pressed"]
[connection signal="pressed" from="StartMenu/SettingsButton" to="." method="On_SettingsPopupButton_Pressed"]
[connection signal="pressed" from="StartMenu/QuitButton" to="." method="On_QuitButton_Pressed"]

@ -8,7 +8,7 @@ custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="Temp/Exports/CMSGame.exe"
export_path="Temp/Exports/windows-64/CMSGame.exe"
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
@ -45,7 +45,7 @@ application/company_name="CMSGame Developers"
application/product_name="CMSGame"
application/file_description="The Country of Mountains and Seas"
application/copyright="CMSGame Developers"
application/trademarks="CMSGame"
application/trademarks=""
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"

@ -16,7 +16,8 @@ config/name_localized={
"zh_Hans": "山与海的国",
"zh_Hant": "山與海的囯"
}
run/main_scene="res://Scenes/MainScene.tscn"
run/main_scene="res://Scenes/LandingScene/LandingScene.tscn"
config/project_settings_override="user://Engine.cfg"
config/features=PackedStringArray("4.0", "C#", "Forward Plus")
config/icon="res://icon.svg"

Loading…
Cancel
Save