diff --git a/.gitignore b/.gitignore index a930fc00..25b7d25b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ .idea/.idea.Funk Engine/.idea/ *.DotSettings.user -export_presets.cfg \ No newline at end of file +export_presets.cfg +*.translation diff --git a/Audio/Song1.ogg b/Audio/Song1.ogg index 93d476e9..13a40f45 100644 Binary files a/Audio/Song1.ogg and b/Audio/Song1.ogg differ diff --git a/Audio/Song1.ogg.import b/Audio/Song1.ogg.import index 8e5bd965..d11e42d0 100644 --- a/Audio/Song1.ogg.import +++ b/Audio/Song1.ogg.import @@ -2,7 +2,7 @@ importer="oggvorbisstr" type="AudioStreamOggVorbis" -uid="uid://iq0xxe5cggs8" +uid="uid://be5ial13ynf3o" path="res://.godot/imported/Song1.ogg-1d785b9ae3fbaa8393048e39af66ed86.oggvorbisstr" [deps] diff --git a/Audio/Song2.ogg b/Audio/Song2.ogg index 063129c0..2301351a 100644 Binary files a/Audio/Song2.ogg and b/Audio/Song2.ogg differ diff --git a/Audio/Song2.ogg.import b/Audio/Song2.ogg.import index cfb5f8ae..650cf1ff 100644 --- a/Audio/Song2.ogg.import +++ b/Audio/Song2.ogg.import @@ -2,7 +2,7 @@ importer="oggvorbisstr" type="AudioStreamOggVorbis" -uid="uid://ckis6k6vuums" +uid="uid://dxp7blovqh1ba" path="res://.godot/imported/Song2.ogg-b95c04f3512de6fa42e0f9c35aba831f.oggvorbisstr" [deps] diff --git a/Audio/Song3.ogg b/Audio/Song3.ogg index 58f8028c..2b45aec4 100644 Binary files a/Audio/Song3.ogg and b/Audio/Song3.ogg differ diff --git a/Audio/Song3.ogg.import b/Audio/Song3.ogg.import index 3d5d0664..01d60072 100644 --- a/Audio/Song3.ogg.import +++ b/Audio/Song3.ogg.import @@ -2,7 +2,7 @@ importer="oggvorbisstr" type="AudioStreamOggVorbis" -uid="uid://ceyw5mjkem2pi" +uid="uid://d4nmixdl8xoic" path="res://.godot/imported/Song3.ogg-d4e6a5f1a550561df18989fb495ba591.oggvorbisstr" [deps] diff --git a/Audio/midi/Song2.mid b/Audio/midi/Song2.mid index c657321a..8464b138 100644 Binary files a/Audio/midi/Song2.mid and b/Audio/midi/Song2.mid differ diff --git a/Classes/MidiMaestro/MidiMaestro.cs b/Classes/MidiMaestro/MidiMaestro.cs index 882d47d9..dfc0b6e1 100644 --- a/Classes/MidiMaestro/MidiMaestro.cs +++ b/Classes/MidiMaestro/MidiMaestro.cs @@ -10,6 +10,9 @@ public partial class MidiMaestro : Resource { private MidiFile _midiFile; + public static TempoMap TempoMap; + public static TimeSignature TimeSignature; + //The four note rows that we care about private midiNoteInfo[] _upNotes; private midiNoteInfo[] _downNotes; @@ -18,8 +21,6 @@ public partial class MidiMaestro : Resource //private MidiFile strippedSong; - private SongData songData; - //The path relative to the Audio folder. Will change later public MidiMaestro(string filePath) { @@ -30,10 +31,12 @@ public MidiMaestro(string filePath) if (!FileAccess.FileExists(filePath)) { - GD.PrintErr("ERROR: Unable to load level Midi file: " + filePath); + GD.PushError("ERROR: Unable to load level Midi file: " + filePath); } _midiFile = MidiFile.Read(filePath); + TempoMap = _midiFile.GetTempoMap(); + TimeSignature = TempoMap.GetTimeSignatureAtTime(new MidiTimeSpan()); //Strip out the notes from the midi file foreach (var track in _midiFile.GetTrackChunks()) @@ -41,7 +44,7 @@ public MidiMaestro(string filePath) string trackName = track.Events.OfType().FirstOrDefault()?.Text; midiNoteInfo[] noteEvents = track .GetNotes() - .Select(note => new midiNoteInfo(note, _midiFile.GetTempoMap())) + .Select(note => new midiNoteInfo(note)) .ToArray(); switch (trackName) @@ -60,19 +63,6 @@ public MidiMaestro(string filePath) break; } } - - //Populate the song data - songData = new SongData - { - //TODO: Allow for changes in this data - Bpm = 120, - //Fudge the numbers a bit if we have a really short song - SongLength = - _midiFile.GetDuration().Seconds < 20 - ? 20 - : _midiFile.GetDuration().Seconds, - NumLoops = 1, - }; } public midiNoteInfo[] GetNotes(ArrowType arrowType) @@ -86,30 +76,29 @@ public midiNoteInfo[] GetNotes(ArrowType arrowType) _ => throw new ArgumentOutOfRangeException(nameof(arrowType), arrowType, null), }; } - - public SongData GetSongData() - { - return songData; - } } //A facade to wrap the midi notes. This is a simple class that wraps a Note object from the DryWetMidi library. public class midiNoteInfo { private readonly Melanchall.DryWetMidi.Interaction.Note _note; - private readonly TempoMap _tempoMap; - public midiNoteInfo(Melanchall.DryWetMidi.Interaction.Note note, TempoMap tempoMap) + public midiNoteInfo(Melanchall.DryWetMidi.Interaction.Note note) { _note = note; - _tempoMap = tempoMap; + } + + public long GetStartTimeBeat() + { + var beatsBar = _note.TimeAs(MidiMaestro.TempoMap); + return beatsBar.Bars * MidiMaestro.TimeSignature.Numerator + beatsBar.Beats; } public long GetStartTimeTicks() => _note.Time; public float GetStartTimeSeconds() => - _note.TimeAs(_tempoMap).Milliseconds / 1000f - + _note.TimeAs(_tempoMap).Seconds; + _note.TimeAs(MidiMaestro.TempoMap).Milliseconds / 1000f + + _note.TimeAs(MidiMaestro.TempoMap).Seconds; public long GetEndTime() => _note.EndTime; diff --git a/Classes/Notes/Note.cs b/Classes/Notes/Note.cs index cc6fed18..87f35cf2 100644 --- a/Classes/Notes/Note.cs +++ b/Classes/Notes/Note.cs @@ -9,15 +9,17 @@ public partial class Note : Resource, IDisplayable { public PuppetTemplate Owner; + public int Id; public string Name { get; set; } private int _baseVal; public float CostModifier { get; private set; } - private Action NoteEffect; //TODO: Where/How to deal with timing. + private Action NoteEffect; public string Tooltip { get; set; } public Texture2D Texture { get; set; } public Note( + int id, string name, string tooltip, Texture2D texture = null, @@ -27,6 +29,7 @@ public Note( float costModifier = 1.0f ) { + Id = id; Name = name; Owner = owner; NoteEffect = @@ -34,7 +37,7 @@ public Note( ?? ( (BD, source, Timing) => { - BD.GetTarget(this).TakeDamage(source._baseVal); + BD.GetTarget(this).TakeDamage((int)Timing * source._baseVal); } ); _baseVal = baseVal; @@ -52,7 +55,21 @@ public Note Clone() { //Eventually could look into something more robust, but for now shallow copy is preferable. //We only would want val and name to be copied by value - Note newNote = new Note(Name, Tooltip, Texture, Owner, _baseVal, NoteEffect, CostModifier); + Note newNote = new Note( + Id, + Name, + Tooltip, + Texture, + Owner, + _baseVal, + NoteEffect, + CostModifier + ); return newNote; } + + public int GetBaseVal() + { + return _baseVal; + } } diff --git a/Classes/Relics/RelicTemplate.cs b/Classes/Relics/RelicTemplate.cs index da8daba4..13dee51c 100644 --- a/Classes/Relics/RelicTemplate.cs +++ b/Classes/Relics/RelicTemplate.cs @@ -5,19 +5,22 @@ public partial class RelicTemplate : Resource, IDisplayable { public RelicEffect[] Effects; + public int Id; public string Name { get; set; } public Texture2D Texture { get; set; } public string Tooltip { get; set; } public RelicTemplate( + int id, string name = "", string tooltip = "", Texture2D texture = null, - RelicEffect[] EffectTags = null + RelicEffect[] effectTags = null ) { - Effects = EffectTags; + Id = id; + Effects = effectTags; Name = name; Tooltip = tooltip; Texture = texture; @@ -25,7 +28,7 @@ public RelicTemplate( public RelicTemplate Clone() { - RelicTemplate newRelic = new RelicTemplate(Name, Tooltip, Texture, Effects); + RelicTemplate newRelic = new RelicTemplate(Id, Name, Tooltip, Texture, Effects); return newRelic; } } diff --git a/Funk Engine.csproj b/Funk Engine.csproj index 0fb7dbfb..e6707425 100644 --- a/Funk Engine.csproj +++ b/Funk Engine.csproj @@ -7,7 +7,7 @@ FunkEngine - + diff --git a/Globals/BGAudioPlayer.tscn b/Globals/BgAudioPlayer/BGAudioPlayer.tscn similarity index 60% rename from Globals/BGAudioPlayer.tscn rename to Globals/BgAudioPlayer/BGAudioPlayer.tscn index 932b5464..bd56bf4f 100644 --- a/Globals/BGAudioPlayer.tscn +++ b/Globals/BgAudioPlayer/BGAudioPlayer.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://csx04jwaoo20u"] -[ext_resource type="Script" path="res://Globals/BgAudioPlayer.cs" id="1_w7def"] +[ext_resource type="Script" path="res://Globals/BgAudioPlayer/BgAudioPlayer.cs" id="1_w7def"] [node name="BgAudioPlayer" type="AudioStreamPlayer"] script = ExtResource("1_w7def") diff --git a/Globals/BgAudioPlayer.cs b/Globals/BgAudioPlayer/BgAudioPlayer.cs similarity index 93% rename from Globals/BgAudioPlayer.cs rename to Globals/BgAudioPlayer/BgAudioPlayer.cs index 95fae32a..35f17e75 100644 --- a/Globals/BgAudioPlayer.cs +++ b/Globals/BgAudioPlayer/BgAudioPlayer.cs @@ -7,6 +7,7 @@ public partial class BgAudioPlayer : AudioStreamPlayer private void PlayMusic(AudioStream music, float volume) { + ProcessMode = ProcessModeEnum.Always; if (Playing && music.Equals(Stream)) { return; diff --git a/Globals/ContrastFilter/ContrastFilter.gdshader b/Globals/ContrastFilter/ContrastFilter.gdshader new file mode 100644 index 00000000..b6f2754d --- /dev/null +++ b/Globals/ContrastFilter/ContrastFilter.gdshader @@ -0,0 +1,10 @@ +shader_type canvas_item; + +uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap; + +void fragment() { + vec4 screen = texture(SCREEN_TEXTURE, SCREEN_UV); + + COLOR.rgb =vec3(0.6 * screen.r + 0.6 * screen.g + 0.2 * screen.b); +} + diff --git a/Globals/ContrastFilter/ContrastFilter.tscn b/Globals/ContrastFilter/ContrastFilter.tscn new file mode 100644 index 00000000..5529050f --- /dev/null +++ b/Globals/ContrastFilter/ContrastFilter.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=3 format=3 uid="uid://b6bblgxtfs020"] + +[ext_resource type="Shader" path="res://Globals/ContrastFilter/ContrastFilter.gdshader" id="1_4bhca"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_i6hl4"] +shader = ExtResource("1_4bhca") + +[node name="CanvasLayer" type="CanvasLayer"] +layer = 128 + +[node name="Filter" type="ColorRect" parent="."] +z_index = 4096 +material = SubResource("ShaderMaterial_i6hl4") +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 diff --git a/Globals/FunkEngineNameSpace.cs b/Globals/FunkEngineNameSpace.cs index 1c31fb5a..fc70a91a 100644 --- a/Globals/FunkEngineNameSpace.cs +++ b/Globals/FunkEngineNameSpace.cs @@ -62,7 +62,7 @@ public enum Stages Boss, Quit, Map, - Controls, + Load, } public class MapGrid @@ -78,6 +78,12 @@ public Room[] GetRooms() public class Room { + public int Idx { get; private set; } + public int[] Children { get; private set; } = Array.Empty(); + public int X { get; private set; } + public int Y { get; private set; } + public Stages Type { get; private set; } + public Room(int idx, int x, int y) { Idx = idx; @@ -96,12 +102,6 @@ public void AddChild(int newIdx) return; Children = Children.Append(newIdx).ToArray(); } - - public int Idx { get; private set; } - public int[] Children { get; private set; } = Array.Empty(); - public int X { get; private set; } - public int Y { get; private set; } - public Stages Type { get; private set; } } public void InitMapGrid(int width, int height, int paths) @@ -166,7 +166,6 @@ private void CreateCommonChildren(int width, int height) continue; if (_map[curPos.X, curPos.Y + 1] == 0) continue; - GD.Print("Added child on same X."); room.AddChild(_map[curPos.X, curPos.Y + 1]); } } diff --git a/Globals/Scribe.cs b/Globals/Scribe.cs index 16c87a9f..cc9c2a14 100644 --- a/Globals/Scribe.cs +++ b/Globals/Scribe.cs @@ -12,6 +12,7 @@ public partial class Scribe : Node public static readonly Note[] NoteDictionary = new[] { new Note( + 0, "EnemyBase", "Basic enemy note, deals damage to player.", null, @@ -19,65 +20,78 @@ public partial class Scribe : Node 1, (director, note, timing) => { - director.Player.TakeDamage(3 - (int)timing); + director.Player.TakeDamage((3 - (int)timing) * note.GetBaseVal()); } ), new Note( + 1, "PlayerBase", - "Basic player note, deals damage to enemy", + "Basic player note, deals damage to enemy.", GD.Load("res://Classes/Notes/assets/single_note.png"), null, 1, (director, note, timing) => { - director.Enemy.TakeDamage((int)timing); + if (timing == Timing.Miss) + return; + director.Enemy.TakeDamage((int)timing * note.GetBaseVal()); } ), new Note( + 2, "PlayerDouble", - "Basic player note, deals double damage to enemy", + "Basic player note, deals double damage to enemy.", GD.Load("res://Classes/Notes/assets/double_note.png"), null, - 1, + 2, (director, note, timing) => { - // can change later, but I want it like this instead of changing base - // in case we have some relic that messes with timing - director.Enemy.TakeDamage(2 * (int)timing); + if (timing == Timing.Miss) + return; + director.Enemy.TakeDamage(note.GetBaseVal() * (int)timing); } ), new Note( + 3, "PlayerHeal", - "Basic player note, heals player", + "Basic player note, heals player.", GD.Load("res://Classes/Notes/assets/heal_note.png"), null, 1, (director, note, timing) => { - director.Player.Heal((int)timing); + if (timing == Timing.Miss) + return; + director.Player.Heal((int)timing * note.GetBaseVal()); } ), new Note( + 4, "PlayerVampire", - "Steals health from enemy", + "Steals health from enemy.", GD.Load("res://Classes/Notes/assets/vampire_note.png"), null, 1, (director, note, timing) => { - director.Player.Heal((int)timing); - director.Enemy.TakeDamage((int)timing); + if (timing == Timing.Miss) + return; + director.Player.Heal((int)timing * note.GetBaseVal()); + director.Enemy.TakeDamage((int)timing * note.GetBaseVal()); } ), new Note( + 5, "PlayerQuarter", - "Basic note at a quarter of the cost", + "Basic note at a quarter of the cost.", GD.Load("res://Classes/Notes/assets/quarter_note.png"), null, 1, (director, note, timing) => { - director.Enemy.TakeDamage((int)timing); + if (timing == Timing.Miss) + return; + director.Enemy.TakeDamage((int)timing + note.GetBaseVal()); }, 0.25f ), @@ -86,6 +100,7 @@ public partial class Scribe : Node public static readonly RelicTemplate[] RelicDictionary = new[] { new RelicTemplate( + 0, "Breakfast", //Reference ha ha, Item to give when relic pool is empty. "Increases max hp.", //TODO: Description can include the relics values? GD.Load("res://Classes/Relics/assets/relic_Breakfast.png"), @@ -103,6 +118,7 @@ public partial class Scribe : Node } ), new RelicTemplate( + 1, "Good Vibes", "Heals the player whenever they place a note.", GD.Load("res://Classes/Relics/assets/relic_GoodVibes.png"), @@ -119,6 +135,7 @@ public partial class Scribe : Node } ), new RelicTemplate( + 2, "Auroboros", "Bigger number, better person. Increases combo multiplier every riff.", GD.Load("res://Classes/Relics/assets/Auroboros.png"), @@ -136,6 +153,7 @@ public partial class Scribe : Node } ), new RelicTemplate( + 3, "Colorboros", "Taste the rainbow. Charges the freestyle bar every riff.", GD.Load("res://Classes/Relics/assets/Colorboros.png"), diff --git a/Globals/StageProducer.cs b/Globals/StageProducer.cs index d8928bc4..7c1a9682 100644 --- a/Globals/StageProducer.cs +++ b/Globals/StageProducer.cs @@ -8,36 +8,93 @@ public partial class StageProducer : Node //Generate a map, starting as a width x height grid, pick a starting spot and do (path) paths from that to the last //row, connecting the path, then connect all at the end to the boss room. public static RandomNumberGenerator GlobalRng = new RandomNumberGenerator(); - private ulong _seed; - private ulong _lastRngState; public static bool IsInitialized; - private Stages _curStage = Stages.Title; //TODO: State Machine kinda deal? + private Stages _curStage = Stages.Title; private Node _curScene; - public static MapGrid.Room CurRoom { get; private set; } - public static Vector2I MapSize { get; private set; } = new Vector2I(7, 6); //For now, make width an odd number + public static int CurRoom { get; private set; } + public static Vector2I MapSize { get; private set; } = new Vector2I(7, 6); //For now, make width an odd number public static MapGrid Map { get; } = new MapGrid(); public static BattleConfig Config; //Hold here to persist between changes - //TODO: Allow for permanent changes and battle temporary stat changes. public static PlayerStats PlayerStats; + public static CanvasLayer ContrastFilter; + + public override void _EnterTree() + { + InitFromCfg(); + } + + private void InitFromCfg() + { + OptionsMenu.ChangeVolume( + SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.Volume).As() + ); + TranslationServer.SetLocale( + SaveSystem.GetConfigValue(SaveSystem.ConfigSettings.LanguageKey).As() + ); + ContrastFilter = GD.Load("res://Globals/ContrastFilter/ContrastFilter.tscn") + .Instantiate(); + ContrastFilter.Visible = SaveSystem + .GetConfigValue(SaveSystem.ConfigSettings.HighContrast) + .AsBool(); + GetTree().Root.CallDeferred("add_child", ContrastFilter); + } + public void StartGame() { - Map.InitMapGrid(MapSize.X, MapSize.Y, 3); GlobalRng.Randomize(); - _seed = GlobalRng.Seed; - _lastRngState = GlobalRng.State; + GenerateMapConsistent(); PlayerStats = new PlayerStats(); - CurRoom = Map.GetRooms()[0]; + CurRoom = Map.GetRooms()[0].Idx; IsInitialized = true; } - public static void ChangeCurRoom(MapGrid.Room room) + public bool LoadGame() + { + SaveSystem.SaveFile sv = SaveSystem.LoadGame(); + if (sv == null) + { + GD.PushWarning("Can't load game, either file 404 or invalid file."); + return false; + } + GlobalRng.Seed = sv.RngSeed; + GenerateMapConsistent(); + GlobalRng.State = sv.RngState; + CurRoom = sv.LastRoomIdx; + + PlayerStats = new PlayerStats(); + PlayerStats.CurNotes = Array.Empty(); + foreach (int noteId in sv.NoteIds) + { + PlayerStats.AddNote(Scribe.NoteDictionary[noteId]); + } + foreach (int relicId in sv.RelicIds) + { + PlayerStats.AddRelic(Scribe.RelicDictionary[relicId]); + } + PlayerStats.CurrentHealth = sv.PlayerHealth; + IsInitialized = true; + return true; + } + + private void GenerateMapConsistent() + { + GlobalRng.State = GlobalRng.Seed << 5 / 2; + Map.InitMapGrid(MapSize.X, MapSize.Y, 3); + } + + public static MapGrid.Room GetCurRoom() + { + return Map.GetRooms()[CurRoom]; + } + + public static void ChangeCurRoom(int room) { CurRoom = room; } @@ -49,6 +106,7 @@ public void TransitionFromRoom(int nextRoomIdx) public void TransitionStage(Stages nextStage, int nextRoomIdx = -1) { + GetTree().Root.RemoveChild(ContrastFilter); switch (nextStage) { case Stages.Title: @@ -63,9 +121,6 @@ public void TransitionStage(Stages nextStage, int nextRoomIdx = -1) Config = MakeConfig(nextStage, nextRoomIdx); GetTree().ChangeSceneToFile("res://scenes/BattleDirector/test_battle_scene.tscn"); break; - case Stages.Controls: - GetTree().ChangeSceneToFile("res://scenes/Remapping/Remap.tscn"); - break; case Stages.Chest: Config = MakeConfig(nextStage, nextRoomIdx); GetTree().ChangeSceneToFile("res://scenes/ChestScene/ChestScene.tscn"); @@ -77,14 +132,21 @@ public void TransitionStage(Stages nextStage, int nextRoomIdx = -1) StartGame(); } break; + case Stages.Load: + if (!LoadGame()) + StartGame(); + GetTree().ChangeSceneToFile("res://scenes/Maps/cartographer.tscn"); + break; case Stages.Quit: GetTree().Quit(); return; default: - GD.Print($"Error Scene Transition is {nextStage}"); + GD.PushError($"Error Scene Transition is {nextStage}"); break; } + //Apply grayscale shader to all scenes + GetTree().Root.AddChild(ContrastFilter); _curStage = nextStage; } diff --git a/Globals/Translations/translations.csv b/Globals/Translations/translations.csv new file mode 100644 index 00000000..36684621 --- /dev/null +++ b/Globals/Translations/translations.csv @@ -0,0 +1,60 @@ +keys,en,cn +TITLE,Midnight Riff,中夜现场 +TITLE_START,New Game,新游戏 +TITLE_CONTINUE,Load Game,加载游戏 +TITLE_QUIT,Quit Game,退出游戏 +TITLE_OPTIONS,Options,选项 +TITLE_CONTROLS,Change Controls,更改操控 +CONTROLS_TITLE_TYPE_WASD,WASD,WASD +CONTROLS_TITLE_TYPE_QWER,QWERT,QWERT +CONTROLS_TITLE_TYPE_ARROW,Arrow,箭头键 +CONTROLS_TITLE_SELECTED,Selected,已选择 +CONTROLS_WASD_BUTTON,WASD,WASD +CONTROLS_CONTROLLER_BUTTON,Controller,控制杆 +CONTROLS_QWER_BUTTON,QWER,QWER +CONTROLS_ARROW_BUTTON,Arrow Keys,箭头键 +CONTROLS_RETURN_BUTTON,Return,返回 +ESCAPE_MENU_RESUME,Resume,继续 +ESCAPE_MENU_QUIT,Quit,退出 +ESCAPE_MENU_TITLE,Quit to Title,返回标题 +CHEST_ROOM_REWARDS,Rewards!,奖励! +CHEST_ROOM_SKIP,Skip,跳过 +CHEST_ROOM_ACCEPT,Accept,接受 +BATTLE_ROOM_BEGIN_BUTTON,"Begin Battle [Enter]","开始战斗 [Enter]" +BATTLE_ROOM_PERFECT,Perfect,精准 +BATTLE_ROOM_GOOD,Good,良好 +BATTLE_ROOM_OKAY,Okay,可以 +BATTLE_ROOM_MISS,Miss,错误 +BATTLE_ROOM_WIN,"You win!","你获胜了!" +BATTLE_ROOM_LOSE,"Game Over!","游戏结束!" +END_SCREEN_RESTART,Restart,重新开始 +BATTLE_ROOM_SKIP_BUTTON,Skip,跳过 +BATTLE_ROOM_ACCEPT_BUTTON,Accept,接受 +NOTE_ENEMYBASE_NAME,EnemyBase,敌人基地 +NOTE_ENEMYBASE_TOOLTIP,"Basic enemy note, deals damage to player.","基础敌人音符,对玩家造成伤害" +NOTE_PLAYERBASE_NAME,PlayerBase,玩家基地 +NOTE_PLAYERBASE_TOOLTIP,"Basic player note, deals damage to enemy.","基础玩家音符,对敌人造成伤害" +NOTE_PLAYERDOUBLE_NAME,PlayerDouble,玩家双击 +NOTE_PLAYERDOUBLE_TOOLTIP,"Basic player note, deals double damage to enemy.","基础玩家音符,对敌人造成双倍伤害" +NOTE_PLAYERHEAL_NAME,PlayerHeal,玩家治愈 +NOTE_PLAYERHEAL_TOOLTIP,"Basic player note, heals player.","基础玩家音符,治愈玩家" +NOTE_PLAYERVAMPIRE_NAME,PlayerVampire,玩家吸血 +NOTE_PLAYERVAMPIRE_TOOLTIP,"Steals health from enemy.","从敌人吸取生命值" +NOTE_PLAYERQUARTER_NAME,PlayerQuarter,玩家一分之一 +NOTE_PLAYERQUARTER_TOOLTIP,"Basic note at a quarter of the cost.","以四分之一的耗费时间量发出基础音符" +RELIC_BREAKFAST_NAME,Breakfast,早餐 +RELIC_BREAKFAST_TOOLTIP,"Increases max hp.",提高最大生命值 +RELIC_GOODVIBES_NAME,Good Vibes,良好消息 +RELIC_GOODVIBES_TOOLTIP,"Heals the player whenever they place a note.","每开始一个音符时治愈玩家" +RELIC_AUROBOROS_NAME,Auroboros,无尾蛇 +RELIC_AUROBOROS_TOOLTIP,"Bigger number, better person. Increases combo multiplier every riff.","进一步增加综合倍数,每次现场提升" +RELIC_COLORBOROS_NAME,Colorboros,彩蛇轮回 +RELIC_COLORBOROS_TOOLTIP,"Taste the rainbow. Charges the freestyle bar every riff.","品尝临岛,每次现场充值自由格条" +INVENTORY_TAB_NOTES,Notes,乐谱 +INVENTORY_TAB_RELICS,Relics,遗物 +OPTIONS_VOLUME_LABEL,Master Volume,最终音量设置 +OPTIONS_CONTRAST_LABEL,High Contrast,高对比度模式 +HOW_TO_PLAY,How to Play,如何游玩 +HOW_TO_PLAY_BLOCK1,"Hit notes to\nbuild combo","点击音符\n以建立连击" +HOW_TO_PLAY_BLOCK2,"Place notes when\ncombo is full","当连击满\n时放置音符" +HOW_TO_PLAY_BLOCK3,"Only placed notes\nhave effects","只有已放置\n的音符才有效" \ No newline at end of file diff --git a/Globals/Translations/translations.csv.import b/Globals/Translations/translations.csv.import new file mode 100644 index 00000000..eeffcdc3 --- /dev/null +++ b/Globals/Translations/translations.csv.import @@ -0,0 +1,17 @@ +[remap] + +importer="csv_translation" +type="Translation" +uid="uid://drjnsd6mqpxqh" + +[deps] + +files=["res://Globals/Translations/translations.en.translation", "res://Globals/Translations/translations.cn.translation"] + +source_file="res://Globals/Translations/translations.csv" +dest_files=["res://Globals/Translations/translations.en.translation", "res://Globals/Translations/translations.cn.translation"] + +[params] + +compress=true +delimiter=0 diff --git a/README.md b/README.md index 1dae4551..c672191d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ Current team members include: #### Attributions: Music: - Title Screen: [Crystal Cave - Cynicmusic](https://opengameart.org/content/crystal-cave-song18) -- Battle Song 1: [gameMusic - Magntron](https://freesound.org/people/Magntron/sounds/335571/) +- Boss Song 1: [gameMusic - Magntron](https://freesound.org/people/Magntron/sounds/335571/) +- Battle Song 1: [Piano loops 181 - josefpres](https://freesound.org/people/josefpres/sounds/789998/) +- Battle Song 2: [Dark loops 220 - josefpres](https://freesound.org/people/josefpres/sounds/620230/) Images: - Input Buttons: [inputKeys - Nicolae (Xelu) Berbece](https://thoseawesomeguys.com/prompts/) diff --git a/SaveData/SaveData.json b/SaveData/SaveData.json deleted file mode 100644 index 1aeaf3cc..00000000 --- a/SaveData/SaveData.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "AccountName": null, - "Notes": { - "PlayerBase": 2, - "PlayerDouble": 1 - }, - "Relics": {}, - "Settings": {} -} \ No newline at end of file diff --git a/SaveData/SaveSystem.cs b/SaveData/SaveSystem.cs index 7cc7af03..d3a65d03 100644 --- a/SaveData/SaveSystem.cs +++ b/SaveData/SaveSystem.cs @@ -1,48 +1,221 @@ +using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.Json; using Godot; - -// TODO: implement saving +using FileAccess = Godot.FileAccess; public static class SaveSystem { - private static string SavePath => "res://SaveData/SaveData.json"; // Update if needed + #region Config + private const string UserConfigPath = "user://Options.cfg"; + private static ConfigFile _curConfigData; + + private const float DefaultVolume = 80f; + private const string DefaultInput = "WASD"; + private const string DefaultLanguage = "en"; + private const bool DefaultHighCon = false; + + public enum ConfigSettings + { + Volume, + InputKey, + LanguageKey, + HighContrast, + } + + //Initializes a new Config and saves it + private static void InitConfig() + { + _curConfigData = new ConfigFile(); + UpdateConfig(ConfigSettings.Volume, DefaultVolume); + UpdateConfig(ConfigSettings.InputKey, DefaultInput); + UpdateConfig(ConfigSettings.LanguageKey, DefaultLanguage); + UpdateConfig(ConfigSettings.HighContrast, DefaultHighCon); + } + + //Saves config + private static void SaveConfig() + { + AssertConfigFile(); + _curConfigData.Save(UserConfigPath); + } - // Loads only the notes section - public static Dictionary LoadNotes() + //Update a config of relevant setting and saves + public static void UpdateConfig(ConfigSettings setting, Variant value) { - var saveData = LoadSaveData(); - if (saveData != null && saveData.Notes != null) + AssertConfigFile(); + switch (setting) { - return saveData.Notes; + case ConfigSettings.Volume: + _curConfigData.SetValue("Options", "Volume", value); + break; + case ConfigSettings.InputKey: + _curConfigData.SetValue("Options", "InputKey", value); + break; + case ConfigSettings.LanguageKey: + _curConfigData.SetValue("Options", "LanguageKey", value); + break; + case ConfigSettings.HighContrast: + _curConfigData.SetValue("Options", "HighContrast", value); + break; + default: + GD.PushError("SaveSystem.UpdateConfig: Invalid config setting passed. " + setting); + break; } - else + SaveConfig(); + } + + //Verifies a config file is currently loaded. + private static void AssertConfigFile() + { + if (_curConfigData == null) { - return new Dictionary(); + LoadConfigData(); } } + //Really naive approach to verifying config integrity, could I have just changed back to JSON? yes. But I'm a real programmer. + //In theory ConfigFiles should be more stable across any version changes. + private static void VerifyConfig() + { + if (!FileAccess.FileExists(UserConfigPath)) + return; + string[] sus = new[] + { + "init", + "object", + "script", + "source", + "extends", + "RefCounted", + "sus", + }; + FileAccess file = FileAccess.Open(UserConfigPath, FileAccess.ModeFlags.Read); + if (!sus.Any(s => file.GetAsText().Contains(s))) + return; + file.Close(); + InitConfig(); + } + // This method loads the entire save data - public static SaveData LoadSaveData() + private static void LoadConfigData() + { + _curConfigData = new ConfigFile(); + VerifyConfig(); + if (_curConfigData.Load(UserConfigPath) == Error.Ok) + return; + GD.PushWarning("Safe. No config could be found, creating a new one."); + InitConfig(); + SaveConfig(); + } + + //Gets config value + public static Variant GetConfigValue(ConfigSettings setting) + { + AssertConfigFile(); + switch (setting) + { + case ConfigSettings.Volume: + return _curConfigData.GetValue("Options", "Volume", DefaultVolume); + case ConfigSettings.InputKey: + return _curConfigData.GetValue("Options", "InputKey", DefaultInput); + case ConfigSettings.LanguageKey: + return _curConfigData.GetValue("Options", "LanguageKey", DefaultLanguage); + case ConfigSettings.HighContrast: + return _curConfigData.GetValue("Options", "HighContrast", DefaultHighCon); + default: + GD.PushError( + "SaveSystem.GetConfigValue: Invalid config setting passed. " + setting + ); + return float.MinValue; + } + } + #endregion + + #region Save + + private const string UserSavePath = "user://MidnighRiff.save"; + + /* + * Values to save: + * Globals: rng seed, rng state, current room (hopefully id) + * Player: Id's of relics, id's of notes, current health + */ + public class SaveFile + { + public ulong RngSeed { get; set; } + public ulong RngState { get; set; } + public int LastRoomIdx { get; set; } + + public int[] NoteIds { get; set; } + public int[] RelicIds { get; set; } + public int PlayerHealth { get; set; } + + public SaveFile( + ulong rngSeed, + ulong rngState, + int lastRoomIdx, + int[] noteIds, + int[] relicIds, + int playerHealth + ) + { + RngSeed = rngSeed; + RngState = rngState; + LastRoomIdx = lastRoomIdx; + NoteIds = noteIds; + RelicIds = relicIds; + PlayerHealth = playerHealth; + } + } + + public static void SaveGame() + { + int[] relicIds = StageProducer.PlayerStats.CurRelics.Select(r => r.Id).ToArray(); + int[] noteIds = StageProducer.PlayerStats.CurNotes.Select(r => r.Id).ToArray(); + SaveFile sv = new SaveFile( + StageProducer.GlobalRng.Seed, + StageProducer.GlobalRng.State, + StageProducer.CurRoom, + noteIds, + relicIds, + StageProducer.PlayerStats.CurrentHealth + ); + string json = JsonSerializer.Serialize(sv); + + FileAccess file = FileAccess.Open(UserSavePath, FileAccess.ModeFlags.Write); + + file.StoreLine(json); + file.Close(); + } + + public static SaveFile LoadGame() { - string path = ProjectSettings.GlobalizePath(SavePath); - if (!File.Exists(path)) + if (!FileAccess.FileExists(UserSavePath)) + return null; + FileAccess file = FileAccess.Open(UserSavePath, FileAccess.ModeFlags.Read); + string json = file.GetAsText(); + + file.Close(); + SaveFile sv; + try { - GD.PrintErr("Can't load save game"); + sv = JsonSerializer.Deserialize(json); + } + catch (JsonException) + { + GD.PushWarning("Cannot deserialize save file, returning null."); return null; } + return sv; + } - string json = File.ReadAllText(path); - SaveData data = JsonSerializer.Deserialize(json); - return data; + public static void ClearSave() + { + DirAccess.RemoveAbsolute(UserSavePath); } -} -public class SaveData -{ - public string AccountName { get; set; } - public Dictionary Notes { get; set; } = new Dictionary(); - public Dictionary Relics { get; set; } = new Dictionary(); - public Dictionary Settings { get; set; } = new Dictionary(); + #endregion } diff --git a/project.godot b/project.godot index d98a7e8c..bad04179 100644 --- a/project.godot +++ b/project.godot @@ -15,77 +15,183 @@ run/main_scene="res://scenes/SceneTransitions/TitleScreen.tscn" config/features=PackedStringArray("4.3", "C#", "Forward Plus") config/icon="res://scenes/BattleDirector/assets/Character1.png" +[audio] + +buses/default_bus_layout="" + [autoload] +StageProducer="*res://Globals/StageProducer.cs" TimeKeeper="*res://Globals/TimeKeeper.cs" Scribe="*res://Globals/Scribe.cs" -StageProducer="*res://Globals/StageProducer.cs" -BgAudioPlayer="*res://Globals/BGAudioPlayer.tscn" +BgAudioPlayer="*res://Globals/BgAudioPlayer/BGAudioPlayer.tscn" [display] window/size/viewport_width=640 window/size/viewport_height=360 -window/stretch/mode="viewport" +window/stretch/mode="canvas_items" window/stretch/scale_mode="integer" [dotnet] project/assembly_name="Funk Engine" -[game] - -input_scheme="ARROWS" - [input] -arrowUp={ +ui_accept={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":88,"key_label":0,"unicode":120,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) ] } -arrowDown={ +ui_cancel={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":99,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +ui_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +ui_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) ] } -arrowLeft={ +ui_down={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":90,"key_label":0,"unicode":122,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +ARROWS_arrowUp={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"location":0,"echo":false,"script":null) +] +} +ARROWS_arrowDown={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":51,"key_label":0,"unicode":51,"location":0,"echo":false,"script":null) +] +} +ARROWS_arrowLeft={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"location":0,"echo":false,"script":null) ] } -arrowRight={ +ARROWS_arrowRight={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":86,"key_label":0,"unicode":118,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":52,"location":0,"echo":false,"script":null) ] } Pause={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":true,"script":null) ] } Inventory={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":73,"key_label":0,"unicode":105,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":4,"pressure":0.0,"pressed":true,"script":null) ] } Secondary={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null) +] +} +CONTROLLER_arrowUp={ +"deadzone": 0.5, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":3,"pressure":0.0,"pressed":true,"script":null) ] } +CONTROLLER_arrowDown={ +"deadzone": 0.5, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) +] +} +CONTROLLER_arrowLeft={ +"deadzone": 0.5, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":2,"pressure":0.0,"pressed":true,"script":null) +] +} +CONTROLLER_arrowRight={ +"deadzone": 0.5, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) +] +} +WASD_arrowUp={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +WASD_arrowDown={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +WASD_arrowLeft={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +WASD_arrowRight={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +QWERT_arrowUp={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +QWERT_arrowDown={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +] +} +QWERT_arrowLeft={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +] +} +QWERT_arrowRight={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null) +] +} + +[internationalization] + +locale/translations=PackedStringArray("res://Globals/Translations/translations.cn.translation", "res://Globals/Translations/translations.en.translation") [rendering] diff --git a/scenes/BattleDirector/NotePlacementBar.tscn b/scenes/BattleDirector/NotePlacementBar.tscn index b0935a83..7404aebc 100644 --- a/scenes/BattleDirector/NotePlacementBar.tscn +++ b/scenes/BattleDirector/NotePlacementBar.tscn @@ -60,8 +60,8 @@ script = ExtResource("1_456es") notePlacementBar = NodePath("ProgressBar") currentComboMultText = NodePath("TextEdit") _particles = NodePath("ProgressBar/Rock") -_currentNote = NodePath("NoteQueueSprite/NextNote") -_nextNote = NodePath("NoteQueueSprite/CurrentNote") +_currentNote = NodePath("NoteQueueSprite/CurrentNote") +_nextNote = NodePath("NoteQueueSprite/NextNote") fullBarParticles = NodePath("ProgressBar/CPUParticles2D") [node name="ProgressBar" type="TextureProgressBar" parent="."] diff --git a/scenes/BattleDirector/assets/BattleFrame1.png b/scenes/BattleDirector/assets/BattleFrame1.png index 223cc97d..2e512481 100644 Binary files a/scenes/BattleDirector/assets/BattleFrame1.png and b/scenes/BattleDirector/assets/BattleFrame1.png differ diff --git a/scenes/BattleDirector/assets/bgupdate.png b/scenes/BattleDirector/assets/bgupdate.png index 68b7ee08..e82dc57c 100644 Binary files a/scenes/BattleDirector/assets/bgupdate.png and b/scenes/BattleDirector/assets/bgupdate.png differ diff --git a/scenes/BattleDirector/notePoofParticles.tscn b/scenes/BattleDirector/notePoofParticles.tscn new file mode 100644 index 00000000..12ee818e --- /dev/null +++ b/scenes/BattleDirector/notePoofParticles.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=5 format=3 uid="uid://k21psn7b5sxf"] + +[ext_resource type="Texture2D" uid="uid://cdf3g3174du4r" path="res://Classes/Notes/assets/heal_note.png" id="1_hjrch"] + +[sub_resource type="Curve" id="Curve_xix4e"] +_data = [Vector2(0, 1), 0.0, 1.4, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="CurveTexture" id="CurveTexture_h7u0t"] +curve = SubResource("Curve_xix4e") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_k0qar"] +lifetime_randomness = 0.25 +particle_flag_disable_z = true +emission_shape = 2 +emission_sphere_radius = 3.0 +spread = 180.0 +initial_velocity_min = 30.77 +initial_velocity_max = 61.55 +gravity = Vector3(0, 0, 0) +tangential_accel_min = -100.0 +tangential_accel_max = 85.12 +scale_curve = SubResource("CurveTexture_h7u0t") + +[node name="NotePoof" type="GPUParticles2D"] +emitting = false +amount = 10 +process_material = SubResource("ParticleProcessMaterial_k0qar") +texture = ExtResource("1_hjrch") +lifetime = 0.75 +one_shot = true +preprocess = 0.1 +explosiveness = 1.0 diff --git a/scenes/BattleDirector/scripts/BattleDirector.cs b/scenes/BattleDirector/scripts/BattleDirector.cs index 48b943e2..89215060 100644 --- a/scenes/BattleDirector/scripts/BattleDirector.cs +++ b/scenes/BattleDirector/scripts/BattleDirector.cs @@ -10,7 +10,7 @@ * @brief Higher priority director to manage battle effects. Can directly access managers, which should signal up to Director WIP */ public partial class BattleDirector : Node2D -{ //TODO: Maybe move some Director functionality to a sub node. +{ #region Declarations public PlayerPuppet Player; @@ -43,7 +43,14 @@ private bool PlayerAddNote(ArrowType type, int beat) { if (!NotePlacementBar.CanPlaceNote()) return false; - if (!CD.AddNoteToLane(type, beat % CM.BeatsPerLoop, NotePlacementBar.PlacedNote(), false)) + if ( + !CD.AddNoteToLane( + type, + beat % CM.BeatsPerLoop, + NotePlacementBar.PlacedNote(this), + false + ) + ) //TODO: Remove passing BD into NPB return false; NotePlaced?.Invoke(this); return true; @@ -63,7 +70,6 @@ public PuppetTemplate GetTarget(Note note) #region Initialization public override void _Ready() { - //TODO: Should come from transition into battle _curSong = StageProducer.Config.CurSong.SongData; Audio.SetStream(GD.Load(StageProducer.Config.CurSong.AudioLocation)); if (_curSong.SongLength <= 0) @@ -103,7 +109,6 @@ public override void _Ready() }; } - //TODO: This will all change private void Begin() { CM.BeginTweens(); @@ -112,7 +117,7 @@ private void Begin() private void EndBattle() { - StageProducer.ChangeCurRoom(StageProducer.Config.BattleRoom); + StageProducer.ChangeCurRoom(StageProducer.Config.BattleRoom.Idx); GetNode("/root/StageProducer").TransitionStage(Stages.Map); } @@ -120,10 +125,13 @@ public override void _Process(double delta) { _focusedButton?.GrabFocus(); TimeKeeper.CurrentTime = Audio.GetPlaybackPosition(); - double realBeat = TimeKeeper.CurrentTime / (60 / (double)TimeKeeper.Bpm) % CM.BeatsPerLoop; + double realBeat = + TimeKeeper.CurrentTime / (60 / (double)TimeKeeper.Bpm) % CM.TrueBeatsPerLoop; CD.CheckMiss(realBeat); if (realBeat < _lastBeat) + { ChartLooped?.Invoke(this); + } _lastBeat = realBeat; } #endregion @@ -137,7 +145,7 @@ public override void _UnhandledInput(InputEvent @event) { if (eventKey.Keycode == Key.Key0) { - DebugKillEnemy(); + //DebugKillEnemy(); } } } @@ -156,7 +164,11 @@ private void OnTimedInput(Note note, ArrowType arrowType, int beat, double beatD if (PlayerAddNote(arrowType, beat)) return; //Miss on empty note. This does not apply to inactive existing notes as a balance decision for now. NotePlacementBar.MissNote(); - CM.ComboText(Timing.Miss.ToString(), arrowType, NotePlacementBar.GetCurrentCombo()); + CM.ComboText( + Tr("BATTLE_ROOM_" + Timing.Miss.ToString().ToUpper()), + arrowType, + NotePlacementBar.GetCurrentCombo() + ); Player.TakeDamage(4); return; } @@ -172,7 +184,11 @@ private void OnTimedInput(Note note, ArrowType arrowType, int beat, double beatD { NotePlacementBar.HitNote(); } - CM.ComboText(timed.ToString(), arrowType, NotePlacementBar.GetCurrentCombo()); + CM.ComboText( + Tr("BATTLE_ROOM_" + timed.ToString().ToUpper()), + arrowType, + NotePlacementBar.GetCurrentCombo() + ); } private Timing CheckTiming(double beatDif) @@ -216,6 +232,7 @@ private void BattleWon() private void BattleLost() { Audio.StreamPaused = true; + SaveSystem.ClearSave(); AddChild(GD.Load("res://scenes/UI/EndScreen.tscn").Instantiate()); GetTree().Paused = true; } @@ -226,7 +243,7 @@ private void ShowRewardSelection(int amount) if (StageProducer.Config.RoomType == Stages.Boss) type = "Relic"; var rewardSelect = RewardSelect.CreateSelection(this, Player.Stats, amount, type); - rewardSelect.GetNode