using System.Collections.Generic; using System.ComponentModel; using Godot; using Godot.Bridge; using Godot.Collections; using Godot.NativeInterop; [ScriptPath("res://Scripts/UI/Shop.cs")] public partial class Shop : Control { public enum Menu { Override = -1, Main, Buy, Talk, Diag, Sell, Exit } public ShopCaller.Type type; [Export(PropertyHint.None, "")] private AnimatedSprite2D keeper; [Export(PropertyHint.None, "")] private AnimatedSprite2D bg; [Export(PropertyHint.None, "")] private NinePatchRect mainBox; [Export(PropertyHint.None, "")] private NinePatchRect sideBox; [Export(PropertyHint.None, "")] private NinePatchRect statBox; [Export(PropertyHint.None, "")] private RichTextLabel mainText; [Export(PropertyHint.None, "")] private RichTextLabel sideText; [Export(PropertyHint.None, "")] private RichTextLabel statText; [Export(PropertyHint.None, "")] private RichTextLabel amtText; [Export(PropertyHint.None, "")] private RichTextLabel mainList; [Export(PropertyHint.None, "")] private RichTextLabel moneyText; [Export(PropertyHint.None, "")] private Sprite2D[] party; [Export(PropertyHint.None, "")] private RichTextLabel[] partyStat; [Export(PropertyHint.None, "")] private Sprite2D soul; [Export(PropertyHint.None, "")] private ScrollBar scrollBar; private System.Collections.Generic.Dictionary text = new System.Collections.Generic.Dictionary(); private const float side = -56f; private const float infoShow = -110f; private const float infoHide = -15f; private List currentList; private int[] topics; private int option; private int subOption; private int last; private int listlow; private int lastsub; private string[] talk; private Menu menu; private bool first; private bool delay; private bool exit; private bool buyCancel; private ShopCaller caller; private RichTextLabel currentDiag; private StringName back; private const float fadeSpd = 0.05f; public override void _EnterTree() { base.Modulate = Main.colorClear; } public void SetUp(ShopCaller caller) { this.caller = caller; type = ((caller.dialog == ShopCaller.Type.Auto) ? caller.sprite : caller.dialog); if (caller.bleep == null) { caller.bleep = TextSystem.defaultBleep; } LoadText(); AnimatedSprite2D animatedSprite2D = keeper; int sprite = (int)caller.sprite; animatedSprite2D.Play(sprite + "Default"); back = ((caller.bgType == ShopCaller.Type.Auto) ? caller.sprite.ToString() : caller.bgType.ToString()); if (caller.music != null) { Audio.ChangeMusic(caller.music); } Party.UpdateHUD(); for (int i = 0; i < 3; i++) { party[i].Visible = i < SaveFile.current.activeParty.Count; if (party[i].Visible) { party[i].Texture = DWMenu.instance.partyHUDs[i].icon.Texture; } } for (int j = 0; j < caller.items.Length; j++) { if (caller.items[j].cost < Texts.items[caller.items[j].id].sellPrice) { caller.items[j].cost = Mathf.RoundToInt((float)Texts.items[caller.items[j].id].sellPrice * 1.5f); } } UpdateText(); UpdateMoney(); } private void LoadText() { this.text.Clear(); List list = new List(FileAccess.Open(Texts.textFolder + "/Shops.txt", FileAccess.ModeFlags.Read).GetAsText(skipCr: true).Split('\n')); list.AddRange(FileAccess.Open(Texts.textFolder + "/Shops/" + type.ToString() + ".txt", FileAccess.ModeFlags.Read).GetAsText(skipCr: true).Split('\n')); for (int i = 0; i < list.Count; i++) { if (list[i].Length <= 3 || list[i][0] != '@') { continue; } string text = ""; for (int j = i + 1; j < list.Count && list[j].Length > 3; j++) { if (list[j][0] != '>') { text = text + list[j].Replace(" ", "\t") + "\n"; } } this.text.Add(list[i].Remove(0, 1), text); } } private void SellDontHave() { AnimatedSprite2D animatedSprite2D = keeper; int sprite = (int)caller.sprite; animatedSprite2D.Play(sprite + "Sad"); UpdateShopText("SellDontHave"); Audio.PlaySound(Audio.commonSounds[1]); } private void CheckSell() { if (option == 4) { option = 1; UpdateText(Menu.Main); return; } switch (option) { case 0: if (SaveFile.current.dwItems.Count == 0) { SellDontHave(); break; } subOption = 1 + option; UpdateText(); option = 0; break; case 1: if (SaveFile.current.dwWeapon.Count == 0) { SellDontHave(); break; } subOption = 1 + option; UpdateText(); option = 0; break; case 2: if (SaveFile.current.dwArmor.Count == 0) { SellDontHave(); break; } subOption = 1 + option; UpdateText(); option = 0; break; case 3: if (SaveFile.current.storage.Count == 0) { SellDontHave(); break; } subOption = 1 + option; UpdateText(); option = 0; break; } } public override void _Process(double delta) { bg.Play(back); if (!exit) { base.Modulate = base.Modulate.Lerp(Main.colorWhite, 0.05f); } else { base.Modulate = base.Modulate.Lerp(Main.colorClear, 0.05f); if (base.Modulate.A < 0.1f) { QueueFree(); } } keeper.Position = new Vector2(Mathf.Lerp(keeper.Position.X, statBox.Visible ? (-56f) : 0f, 0.1f), 0f); AdvanceText(); if (!first) { return; } switch (menu) { case Menu.Main: soul.Position = new Vector2(62f, 18 + option * 20); if (Input.IsActionJustPressed(Main.keys[0])) { if (option > 0) { option--; } Audio.PlaySound(Audio.commonSounds[0]); } else if (Input.IsActionJustPressed(Main.keys[1])) { if (option < 3) { option++; } Audio.PlaySound(Audio.commonSounds[0]); } else if (Input.IsActionJustPressed(Main.keys[4])) { Audio.PlaySound(Audio.commonSounds[2]); switch (option) { case 0: option = 0; UpdateText(Menu.Buy); break; case 1: option = 0; UpdateText(Menu.Sell); break; case 2: UpdateText(Menu.Talk); break; case 3: UpdateText(Menu.Exit); break; } } break; case Menu.Diag: if (!Input.IsActionJustPressed(Main.keys[4])) { break; } if (mainText.VisibleRatio < 1f) { mainText.VisibleRatio = 1f; break; } subOption++; if (subOption >= talk.Length) { EndDiagFlag(); int num = option; UpdateText(Menu.Talk); option = num; } else { NextDiag(); } break; case Menu.Talk: soul.Position = new Vector2(-146f, 18 + option * 20); if (Input.IsActionJustPressed(Main.keys[0])) { if (option > 0) { option--; } Audio.PlaySound(Audio.commonSounds[0]); } else if (Input.IsActionJustPressed(Main.keys[1])) { if (option < topics.Length) { option++; } Audio.PlaySound(Audio.commonSounds[0]); } else if (Input.IsActionJustPressed(Main.keys[4])) { Audio.PlaySound(Audio.commonSounds[2]); if (option == topics.Length) { option = 2; UpdateText(Menu.Main); } else { talk = text["TalkTopic" + (topics[option] + 1)].Split("\nBREAK\n"); UpdateText(Menu.Diag); } } else if (Input.IsActionJustPressed(Main.keys[5])) { Audio.PlaySound(Audio.commonSounds[0]); option = 2; UpdateText(Menu.Main); } break; case Menu.Sell: statBox.Position = new Vector2(statBox.Position.X, Mathf.Lerp(statBox.Position.Y, (subOption == 0 || option >= currentList.Count) ? (-15f) : (-110f), 0.1f)); if (subOption == 0) { soul.Position = new Vector2(-146f, 18 + option * 20); } else if (subOption == -1) { soul.Position = new Vector2(62f, 58 + option * 20); } else if (option <= listlow && option >= listlow - 4) { soul.Position = new Vector2(-146f, 18 + 20 * (option - listlow + 4)); } if (Input.IsActionJustPressed(Main.keys[0])) { if (option > 0) { option--; } Audio.PlaySound(Audio.commonSounds[0]); if (subOption > 0) { if (listlow - 4 > option) { listlow--; } UpdateSellList(); } } else if (Input.IsActionJustPressed(Main.keys[1])) { if (option < ((subOption == -1) ? 1 : ((subOption == 0) ? 4 : (talk.Length - 1)))) { option++; } Audio.PlaySound(Audio.commonSounds[0]); if (subOption > 0) { if (listlow < option) { listlow++; } UpdateSellList(); } } else if (Input.IsActionJustPressed(Main.keys[4])) { Audio.PlaySound(Audio.commonSounds[2]); if (subOption == -1) { if (option == 0) { SaveFile.AddMoney(Texts.items[currentList[last]].sellPrice); currentList.RemoveAt(last); Audio.PlaySound("snd_locker_ch1.wav"); subOption = lastsub; option = last; UpdateText(); UpdateMoney(); UpdateShopText("SellConfirmed"); UpdateSellList(); } else { subOption = lastsub; option = last; UpdateText(); } } else if (subOption == 0) { CheckSell(); } else if (option >= currentList.Count) { Audio.PlaySound(Audio.commonSounds[1]); } else { lastsub = subOption; last = option; option = 0; UpdateShopText("SellConfirm"); sideText.Text = sideText.Text.Replace("@", Texts.items[currentList[last]].sellPrice.ToString()); sideText.VisibleRatio = 99f; subOption = -1; } } else if (Input.IsActionJustPressed(Main.keys[5])) { Audio.PlaySound(Audio.commonSounds[0]); if (subOption == 0) { option = 1; UpdateText(Menu.Main); } else { option = subOption - 1; subOption = 0; UpdateText(); } } break; case Menu.Buy: statBox.Position = new Vector2(statBox.Position.X, Mathf.Lerp(statBox.Position.Y, (option == caller.items.Length) ? (-15f) : (-110f), 0.1f)); if (subOption == 0) { if (option <= listlow && option >= listlow - 4) { soul.Position = new Vector2(-146f, 18 + 20 * (option - listlow + 4)); } } else { soul.Position = new Vector2(62f, 58 + option * 20); } if (Input.IsActionJustPressed(Main.keys[0])) { Audio.PlaySound(Audio.commonSounds[0]); if (subOption == 0) { if (option > 0) { option--; if (listlow - 4 > option) { listlow--; } } UpdateSellList(); } else if (option == 1) { option = 0; } } else if (Input.IsActionJustPressed(Main.keys[1])) { Audio.PlaySound(Audio.commonSounds[0]); if (subOption == 0) { if (option < caller.items.Length) { option++; if (listlow < option) { listlow++; } } UpdateSellList(); } else if (option == 0) { option = 1; } } else if (Input.IsActionJustPressed(Main.keys[4])) { Audio.PlaySound(Audio.commonSounds[2]); if (subOption == 0) { if (option == caller.items.Length) { option = 0; UpdateText(Menu.Main); break; } subOption = 1; last = option; UpdateShopText("BuyConfirm"); sideText.Text = sideText.Text.Replace("@", caller.items[last].cost.ToString()); sideText.VisibleRatio = 99f; option = 0; } else if (option == 0) { if (SaveFile.current.dollars < caller.items[last].cost) { AnimatedSprite2D animatedSprite2D = keeper; int sprite = (int)caller.sprite; animatedSprite2D.Play(sprite + "Sad"); option = last; UpdateText(null, 1); UpdateShopText("BuyMoney"); } else if (Items.CheckFull(Texts.items[caller.items[last].id].type)) { AnimatedSprite2D animatedSprite2D2 = keeper; int sprite = (int)caller.sprite; animatedSprite2D2.Play(sprite + "Sad"); option = last; UpdateText(null, 1); UpdateShopText("BuyFull"); } else { SaveFile.current.dollars -= caller.items[last].cost; switch (Texts.items[caller.items[last].id].type) { case Items.Type.Normal: SaveFile.current.dwItems.Add(caller.items[last].id); break; case Items.Type.Equip: SaveFile.current.dwArmor.Add(caller.items[last].id); break; case Items.Type.Weapon: SaveFile.current.dwWeapon.Add(caller.items[last].id); break; } option = last; UpdateText(null, 1); AnimatedSprite2D animatedSprite2D3 = keeper; int sprite = (int)caller.sprite; animatedSprite2D3.Play(sprite + "Happy"); UpdateShopText("BuyConfirmed"); Audio.PlaySound("snd_locker_ch1.wav"); UpdateMoney(); } subOption = 0; } else { AnimatedSprite2D animatedSprite2D4 = keeper; int sprite = (int)caller.sprite; animatedSprite2D4.Play(sprite + "Default"); option = last; UpdateText(null, 1); subOption = 0; UpdateShopText("BuyCancel"); } } else if (Input.IsActionJustPressed(Main.keys[5])) { Audio.PlaySound(Audio.commonSounds[0]); if (subOption == 0) { option = 0; UpdateText(Menu.Main); statText.Visible = false; break; } AnimatedSprite2D animatedSprite2D5 = keeper; int sprite = (int)caller.sprite; animatedSprite2D5.Play(sprite + "Default"); option = last; UpdateText(); subOption = 0; UpdateShopText("BuyCancel"); } break; case Menu.Exit: if (Input.IsActionJustPressed(Main.keys[4])) { if (mainText.VisibleRatio < 1f) { mainText.VisibleRatio = 1f; break; } exit = true; menu = Menu.Override; } break; } } private void SetText(in string text) { currentDiag.Text = this.text[text]; currentDiag.VisibleRatio = 0f; } private void AdvanceText() { if (currentDiag == null) { return; } if (currentDiag.VisibleRatio < 1f) { keeper.Play(); if (!TextSystem.bleepPlayer.Playing) { TextSystem.bleepPlayer.Stream = caller.bleep; TextSystem.bleepPlayer.Play(); } if (!delay) { currentDiag.VisibleCharacters++; } delay = !delay; } else { keeper.Stop(); if (TextSystem.bleepPlayer.Playing) { TextSystem.bleepPlayer.Stop(); } } } private void UpdateSelectedItem(Items.IDs id, int exit = 4) { if (option >= exit || id == Items.IDs.None) { statText.Text = ""; return; } switch (Texts.items[id].type) { case Items.Type.Normal: statText.Text = Texts.common[41]; amtText.Text = Texts.common[44].Replace("@", (12 - SaveFile.current.dwItems.Count).ToString()); break; case Items.Type.Weapon: statText.Text = Texts.common[42]; amtText.Text = Texts.common[44].Replace("@", (48 - SaveFile.current.dwWeapon.Count).ToString()); break; case Items.Type.Equip: statText.Text = Texts.common[43]; amtText.Text = Texts.common[44].Replace("@", (48 - SaveFile.current.dwArmor.Count).ToString()); break; } RichTextLabel richTextLabel = statText; richTextLabel.Text = richTextLabel.Text + "\n" + (Texts.items[id].shopDesc ?? Texts.items[id].shortDesc); if (menu != Menu.Buy) { return; } for (int i = 0; i < SaveFile.current.activeParty.Count; i++) { if (Texts.items[id].type == Items.Type.Normal) { party[i].Visible = false; continue; } party[i].Visible = true; if (!Texts.items[id].partyOnly.HasValue) { party[i].SelfModulate = Main.colorWhite; if (Texts.items[id].type == Items.Type.Equip) { string text = ""; for (int j = 0; j < 2; j++) { text = text + "[img]res://Sprites/Menus/Menu Sprites/Armor" + (j + 1) + ".tres[/img] "; int num = ((Party.party[SaveFile.current.activeParty[i]].EQUIP[j + 1] != -1) ? Texts.items[(Items.IDs)Party.party[SaveFile.current.activeParty[i]].EQUIP[j + 1]].def : 0); int def = Texts.items[id].def; text = ((def < num) ? (text + "[color=red]" + (def - num) + "[/color]\n") : (text + "[color=cyan]+" + (def - num) + "[/color]\n")); } partyStat[i].Text = text; } } else if (Texts.items[id].partyOnly.Value != (Party.IDs)SaveFile.current.activeParty[i]) { party[i].SelfModulate = Main.colorDark; partyStat[i].Text = ""; } else { party[i].SelfModulate = Main.colorWhite; if (Texts.items[id].type == Items.Type.Weapon) { int atk = Texts.items[(Items.IDs)Party.party[SaveFile.current.activeParty[i]].EQUIP[0]].atk; int atk2 = Texts.items[id].atk; string text2 = "[img=13]res://Sprites/Menus/Menu Sprites/StatAtk.tres[/img] "; text2 = ((atk2 < atk) ? (text2 + "[color=red]" + (atk2 - atk) + "[/color]\n") : (text2 + "[color=cyan]+" + (atk2 - atk) + "[/color]\n")); partyStat[i].Text = text2; } } } } private void UpdateShopText(in string tex) { currentDiag = sideText; sideText.Text = text[tex]; sideText.VisibleRatio = 0f; } private void UpdateMoney() { moneyText.Text = " D$ " + SaveFile.current.dollars; } private void UpdateSellList() { DWMenu.UpdateEquipListCurrent(ref listlow, mainText, ref talk, 5); scrollBar.current = option; if (option >= currentList.Count) { statText.Text = ""; } else { UpdateSelectedItem(currentList[option], 9999); } } private void UpdateText(Menu? to = null, int mod = 0) { if (to.HasValue) { menu = to.Value; } if (TextSystem.bleepPlayer.Playing) { TextSystem.bleepPlayer.Stop(); } switch (menu) { case Menu.Main: { currentDiag = mainText; sideText.Text = "\t" + Texts.common[37] + "\n\t" + Texts.common[38] + "\n\t" + Texts.common[39] + "\n\t" + Texts.common[40]; if (first) { SetText("Intro2"); } else { SetText("Intro"); } sideText.VisibleRatio = 99f; first = true; sideBox.Visible = true; statBox.Visible = false; amtText.Visible = false; scrollBar.Visible = false; subOption = 0; mainList.Visible = false; buyCancel = false; moneyText.Visible = true; soul.Visible = true; AnimatedSprite2D animatedSprite2D3 = keeper; int sprite = (int)caller.sprite; animatedSprite2D3.Play(sprite + "Default"); break; } case Menu.Sell: { if (subOption == 0) { statBox.Position = new Vector2(statBox.Position.X, -15f); statBox.Visible = true; statText.Text = ""; currentDiag = null; option = 0; UpdateShopText("SellMenu"); mainText.Text = "\t" + Texts.common[47] + "\n\t" + Texts.common[48] + "\n\t" + Texts.common[49] + "\n\t" + Texts.common[50] + "\n\t" + Texts.common[45]; mainText.VisibleRatio = 99f; scrollBar.Visible = false; listlow = 4; for (int i = 0; i < 3; i++) { party[i].Visible = false; } break; } AnimatedSprite2D animatedSprite2D2 = keeper; int sprite = (int)caller.sprite; animatedSprite2D2.Play(sprite + "Default"); switch (subOption) { case 1: currentList = SaveFile.current.dwItems; break; case 2: currentList = SaveFile.current.dwWeapon; break; case 3: currentList = SaveFile.current.dwArmor; break; case 4: currentList = SaveFile.current.storage; break; } UpdateShopText("SellChoose"); statText.Visible = true; scrollBar.current = 0; scrollBar.max = ((subOption == 1) ? 12 : 48); scrollBar.Visible = true; scrollBar.Update(); talk = new string[scrollBar.max]; for (int j = 0; j < scrollBar.max; j++) { if (j < currentList.Count) { talk[j] = "\t" + (Texts.items[currentList[j]].shopName ?? Texts.items[currentList[j]].name) + " (D$ " + Texts.items[currentList[j]].sellPrice + ")"; } else { talk[j] = "\t[color=gray] - - - - - - - - - - - - -"; } } UpdateSellList(); break; } case Menu.Buy: if (subOption == 0) { statBox.Position = new Vector2(statBox.Position.X, -15f); } currentDiag = null; if (buyCancel) { UpdateShopText("BuyCancel"); } else { UpdateShopText("BuyMenu"); } mainText.Text = ""; if (mod == 0) { mainList.Visible = false; amtText.Visible = true; statBox.Visible = true; statText.Visible = true; mainText.VisibleRatio = 99f; sideText.VisibleRatio = 99f; scrollBar.current = 0; listlow = Mathf.Min(caller.items.Length, 4); scrollBar.max = caller.items.Length + 1; scrollBar.Visible = caller.items.Length > 4; if (scrollBar.Visible) { scrollBar.Update(); } currentList = new List(); talk = new string[caller.items.Length + 1]; for (int k = 0; k < talk.Length; k++) { if (k < talk.Length - 1) { talk[k] = "\t" + (Texts.items[caller.items[k].id].shopName ?? Texts.items[caller.items[k].id].name); if (SaveFile.current.flags.Contains(caller.items[k].soldFlag)) { talk[k] += Texts.common[46]; } else { ref string reference = ref talk[k]; reference = reference + " (D$ " + caller.items[k].cost + ")"; } currentList.Add(caller.items[k].id); } else { talk[k] = "\t" + Texts.common[45]; currentList.Add(Items.IDs.None); } } } UpdateSelectedItem(caller.items[option].id); UpdateSellList(); break; case Menu.Talk: { moneyText.Visible = false; AnimatedSprite2D animatedSprite2D = keeper; int sprite = (int)caller.sprite; animatedSprite2D.Play(sprite + "Default"); currentDiag = sideText; SetText(buyCancel ? "TalkMenu2" : "TalkMenu"); GetTopics(); soul.Visible = true; option = 0; mainText.VisibleRatio = 99f; sideBox.Visible = true; break; } case Menu.Diag: buyCancel = true; subOption = 0; currentDiag = mainText; soul.Visible = false; sideBox.Visible = false; NextDiag(); break; case Menu.Exit: sideBox.Visible = false; soul.Visible = false; SetText("Exit"); break; } } private void NextDiag() { if (talk[subOption].Contains("anim:")) { string[] array = talk[subOption].Split(':'); AnimatedSprite2D animatedSprite2D = keeper; int sprite = (int)caller.sprite; animatedSprite2D.Play(sprite + array[1]); talk[subOption] = array[2].Remove(0, 1); } mainText.Text = talk[subOption]; mainText.VisibleRatio = 0f; } private void GetTopics() { mainText.Text = ""; topics = new int[4]; for (int i = 0; i < topics.Length; i++) { int num = i + 1; Array talkReplacers = caller.talkReplacers; if (talkReplacers != null && talkReplacers.Count > 0) { for (int j = 0; j < caller.talkReplacers.Count; j++) { if (caller.talkReplacers[j][1] - 1 == i && SaveFile.HasFlag((SaveFile.Flags)caller.talkReplacers[j][0])) { num = caller.talkReplacers[j][2]; break; } } } RichTextLabel richTextLabel = mainText; richTextLabel.Text = richTextLabel.Text + "\t" + text["Topic" + num]; topics[i] = num - 1; } RichTextLabel richTextLabel2 = mainText; richTextLabel2.Text = richTextLabel2.Text + "\t" + Texts.common[45]; } private void EndDiagFlag() { Array talkSetFlag = caller.talkSetFlag; if (talkSetFlag == null || talkSetFlag.Count <= 0) { return; } for (int i = 0; i < caller.talkSetFlag.Count; i++) { if (caller.talkSetFlag[i][0] == topics[option] + 1) { SaveFile.AddFlag((SaveFile.Flags)caller.talkSetFlag[i][1]); break; } } } }