Skip to content

Commit db5c9b6

Browse files
LifeHckrQuinn-Hub
andauthored
Added Shop Stage (#206)
* Update: Initial Draft of Shop Changelog -> Shop functionality seems to work fine. Needs update for visuals * Renovate Shop Reworked shop scene and script Made removal slightly portable, would like to eventually make it a separate menu to be reusable. * Add Shop transition * Minor cleanup Practice better null checking Adjust removal panel, decrease size of options section * Clear description after note removal --------- Co-authored-by: Quinn-Hub <56490493+Quinn-Hub@users.noreply.github.com>
1 parent 0a5ee21 commit db5c9b6

14 files changed

Lines changed: 981 additions & 6 deletions

File tree

Globals/StageProducer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ public void PreloadScene(int nextRoomIdx)
141141
});
142142
break;
143143
case Stages.Shop:
144+
_loadTask = Task.Run(() =>
145+
{
146+
_preloadStage = GD.Load<PackedScene>(ShopScene.LoadPath).Instantiate<Node>();
147+
});
148+
break;
144149
case Stages.Event:
145150
case Stages.Chest:
146151
_loadTask = Task.Run(() =>

Globals/Translations/Translations.csv

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ CHEST_ROOM_REWARDS,Reward Selection!,奖励!
3030
CHEST_ROOM_SKIP,Skip,跳过
3131
CHEST_ROOM_ACCEPT,Accept,接受
3232
CHEST_ROOM_REROLL,Rerolls: ,重刷:
33+
SHOP_REMOVAL,Remove A Note,"去掉一个音符"
34+
SHOP_CONFIRM,Confirm Purchase,购买已确认
35+
SHOP_CANCEL,Cancel,取消操作
36+
REMOVAL_COST,Removal Fee,删除费用
3337
BATTLE_ROOM_BEGIN_BUTTON,"Begin Battle [Enter]","开始战斗 [Enter]"
3438
BATTLE_ROOM_PERFECT,Perfect,精准
3539
BATTLE_ROOM_GOOD,Good,良好

Scenes/ChestScene/ChestScene.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ public override void _Process(double delta)
3333

3434
private void GetLoot()
3535
{
36-
GetNode<AudioStreamPlayer>("%Audio").ProcessMode = ProcessModeEnum.Always;
3736
ChestButton.Disabled = true;
3837
RewardSelect.CreateSelection(this, _player.Stats, 3, Stages.Chest).Selected += EndBattle;
3938
}

Scenes/ChestScene/ChestScene.tscn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ ChestButton = NodePath("CenterContainer/VBoxContainer/ChestButton")
2424
PlayerMarker = NodePath("PlayerMarker")
2525

2626
[node name="Audio" type="AudioStreamPlayer" parent="."]
27-
unique_name_in_owner = true
27+
process_mode = 3
2828
stream = ExtResource("2_x78jo")
2929
autoplay = true
3030

Scenes/Puppets/Scripts/PlayerStats.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,9 @@ public void AddNote(Note nSelection)
5555

5656
CurNotes = CurNotes.Append(nSelection).ToArray();
5757
}
58+
59+
public void RemoveNote(Note nSelection)
60+
{
61+
CurNotes = CurNotes.Where(n => n != nSelection).ToArray();
62+
}
5863
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using Godot;
3+
4+
public partial class ShopItem : VBoxContainer
5+
{
6+
public static readonly string LoadPath = "res://Scenes/ShopScene/ShopItem.tscn";
7+
8+
[Export]
9+
public DisplayButton DisplayButton;
10+
11+
[Export]
12+
public Label Cost;
13+
public int Price;
14+
15+
public void Display(int cost, Texture2D texture, string name, bool focusHandling = false)
16+
{
17+
DisplayButton.Display(texture, name, focusHandling);
18+
Price = cost;
19+
Cost.Text = cost.ToString();
20+
}
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://doajq3v7wwje5
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using FunkEngine;
5+
using Godot;
6+
7+
public partial class ShopScene : Control
8+
{
9+
public static readonly string LoadPath = "res://Scenes/ShopScene/ShopScene.tscn";
10+
11+
[Export]
12+
private Label _moneyLabel;
13+
14+
[Export]
15+
private Button _exitButton;
16+
17+
[Export]
18+
private Button _removalButton;
19+
20+
[Export]
21+
private GridContainer _noteGrid;
22+
23+
[Export]
24+
private GridContainer _relicGrid;
25+
26+
[Export]
27+
private CenterContainer _confirmationPopup;
28+
29+
[Export]
30+
private Button _confirmationButton;
31+
32+
[Export]
33+
private Button _denyButton;
34+
35+
[Export]
36+
private Label _descriptionLabel;
37+
38+
[Export]
39+
private Control _removalPanel;
40+
41+
[Export]
42+
private GridContainer _possessionGrid;
43+
44+
[Export]
45+
private Button _removalAcceptButton;
46+
47+
[Export]
48+
private Button _cancelRemoveButton;
49+
50+
[Export]
51+
private Label _removalCostLabel;
52+
53+
private ButtonGroup _bGroup;
54+
55+
private readonly int[] _priceByRarity = [100, 90, 80, 70, 60, 50, 9];
56+
const int NoteCost = 45;
57+
58+
public override void _Ready()
59+
{
60+
_bGroup = new ButtonGroup();
61+
Initialize();
62+
_confirmationButton.Pressed += TryPurchase;
63+
_denyButton.Pressed += CloseConfirmationPopup;
64+
_removalButton.Pressed += OpenRemovalPane;
65+
_removalAcceptButton.Pressed += RemoveNote;
66+
_cancelRemoveButton.Pressed += CloseRemovalPane;
67+
}
68+
69+
private void Initialize()
70+
{
71+
UpdateMoneyLabel();
72+
GenerateShopItems();
73+
PopulatePossessedNotes();
74+
}
75+
76+
public override void _Input(InputEvent @event)
77+
{
78+
if (@event.IsActionPressed("ui_cancel"))
79+
{
80+
if (_confirmationPopup.Visible)
81+
{
82+
CloseConfirmationPopup();
83+
GetViewport().SetInputAsHandled();
84+
}
85+
else if (_removalPanel.Visible)
86+
{
87+
CloseRemovalPane();
88+
GetViewport().SetInputAsHandled();
89+
}
90+
}
91+
}
92+
93+
private void UpdateMoneyLabel()
94+
{
95+
_moneyLabel.Text = StageProducer.PlayerStats.Money.ToString();
96+
}
97+
98+
private const int RelicOptions = 3;
99+
private const int NoteOptions = 5;
100+
101+
private void GenerateShopItems()
102+
{
103+
var relics = Scribe.GetRandomRelics(
104+
RelicOptions,
105+
StageProducer.CurRoom + 10,
106+
StageProducer.PlayerStats.RarityOdds
107+
);
108+
109+
var notes = Scribe.GetRandomRewardNotes(NoteOptions, StageProducer.CurRoom + 10);
110+
111+
foreach (var relic in relics)
112+
{
113+
int price = _priceByRarity[(int)relic.Rarity];
114+
AddShopItem(_relicGrid, relic, price);
115+
}
116+
117+
foreach (var note in notes)
118+
{
119+
int price = NoteCost;
120+
AddShopItem(_noteGrid, note, price);
121+
}
122+
}
123+
124+
private void AddShopItem(GridContainer container, IDisplayable item, int price)
125+
{
126+
if (container == null || item == null)
127+
{
128+
GD.PushError("AddShopItem called with null!");
129+
return;
130+
}
131+
price = Math.Max(price, 0); //Price can't go negative.
132+
ShopItem newItem = GD.Load<PackedScene>(ShopItem.LoadPath).Instantiate<ShopItem>();
133+
newItem.Display(price, item.Texture, item.Name);
134+
newItem.DisplayButton.Pressed += () => SetPurchasable(item, newItem);
135+
newItem.DisplayButton.SetButtonGroup(_bGroup);
136+
newItem.DisplayButton.ToggleMode = true;
137+
newItem.DisplayButton.FocusEntered += () => ChangeDescription(item);
138+
container.AddChild(newItem);
139+
}
140+
141+
private IDisplayable _currentItem;
142+
private ShopItem _currentUItem;
143+
144+
private void SetPurchasable(IDisplayable item, ShopItem uItem)
145+
{
146+
if (item == null || uItem == null)
147+
return;
148+
_currentItem = item;
149+
_currentUItem = uItem;
150+
_confirmationButton.Disabled = StageProducer.PlayerStats.Money < _currentUItem.Price;
151+
OpenConfirmationPopup();
152+
}
153+
154+
private void TryPurchase()
155+
{
156+
if (StageProducer.PlayerStats.Money < _currentUItem.Price)
157+
return;
158+
159+
StageProducer.PlayerStats.Money -= _currentUItem.Price;
160+
switch (_currentItem)
161+
{
162+
case Note note:
163+
StageProducer.PlayerStats.AddNote(note);
164+
AddNoteToPossessions(note);
165+
break;
166+
case RelicTemplate relic:
167+
StageProducer.PlayerStats.AddRelic(relic);
168+
break;
169+
}
170+
171+
CloseConfirmationPopup();
172+
173+
GetViewport().GuiGetFocusOwner().FindNextValidFocus().GrabFocus(); //slightly hacky
174+
_currentUItem.Visible = false;
175+
_currentUItem.QueueFree();
176+
UpdateMoneyLabel();
177+
178+
_currentItem = null;
179+
_currentUItem = null;
180+
}
181+
182+
private Control _lastFocused;
183+
184+
private void OpenConfirmationPopup()
185+
{
186+
_confirmationPopup.Visible = true;
187+
_lastFocused = GetViewport().GuiGetFocusOwner();
188+
_denyButton.GrabFocus();
189+
}
190+
191+
private void CloseConfirmationPopup()
192+
{
193+
_confirmationPopup.Visible = false;
194+
_lastFocused.GrabFocus();
195+
_lastFocused = null;
196+
_bGroup.GetPressedButton().SetPressed(false);
197+
}
198+
199+
private void ChangeDescription(IDisplayable displayable)
200+
{
201+
if (displayable == null)
202+
{
203+
_descriptionLabel.Text = "";
204+
return;
205+
}
206+
string name = displayable.Name.ToUpper();
207+
name = name.Replace(" ", "");
208+
string type = displayable switch
209+
{
210+
Note => "NOTE_",
211+
RelicTemplate => "RELIC_",
212+
_ => "UNKNOWN_",
213+
};
214+
_descriptionLabel.Text = Tr(type + name + "_NAME") + ": " + Tr(type + name + "_TOOLTIP");
215+
}
216+
217+
private const int RemovalCost = 50;
218+
private bool _hasRemoved;
219+
220+
private void OpenRemovalPane()
221+
{
222+
if (_hasRemoved)
223+
return;
224+
_removalCostLabel.Text = RemovalCost.ToString();
225+
_removalButton.Disabled = true;
226+
_exitButton.Disabled = true;
227+
_removalPanel.Visible = true;
228+
_removalAcceptButton.Visible = false;
229+
_removalAcceptButton.Disabled = StageProducer.PlayerStats.Money < RemovalCost;
230+
_bGroup.GetPressedButton()?.SetPressed(false);
231+
_noteGrid.Visible = false;
232+
_relicGrid.Visible = false;
233+
_cancelRemoveButton.GrabFocus();
234+
}
235+
236+
private void CloseRemovalPane()
237+
{
238+
_removalButton.Disabled = _hasRemoved;
239+
_exitButton.Disabled = false;
240+
_removalPanel.Visible = false;
241+
_removalButton.GrabFocus();
242+
_toRemove = null;
243+
_selectedRemoveButton = null;
244+
_noteGrid.Visible = true;
245+
_relicGrid.Visible = true;
246+
ChangeDescription(null);
247+
}
248+
249+
private void PopulatePossessedNotes()
250+
{
251+
foreach (var note in StageProducer.PlayerStats.CurNotes)
252+
{
253+
AddNoteToPossessions(note);
254+
}
255+
}
256+
257+
private void AddNoteToPossessions(Note note)
258+
{
259+
if (note == null)
260+
return;
261+
DisplayButton disButton = GD.Load<PackedScene>(DisplayButton.LoadPath)
262+
.Instantiate<DisplayButton>();
263+
disButton.Display(note.Texture, note.Name);
264+
disButton.ToggleMode = true;
265+
disButton.FocusEntered += () => ChangeDescription(note);
266+
disButton.Pressed += () => RemovalSelection(note, disButton);
267+
disButton.ButtonGroup = _bGroup;
268+
_possessionGrid.AddChild(disButton);
269+
}
270+
271+
private Note _toRemove;
272+
private Button _selectedRemoveButton;
273+
274+
private void RemovalSelection(Note note, Button button)
275+
{
276+
_toRemove = note;
277+
_selectedRemoveButton = button;
278+
_removalAcceptButton.Visible = true;
279+
button.SetPressed(true);
280+
}
281+
282+
private void RemoveNote()
283+
{
284+
if (_toRemove == null || _selectedRemoveButton == null)
285+
return;
286+
StageProducer.PlayerStats.Money -= RemovalCost;
287+
_removalButton.Disabled = true;
288+
_hasRemoved = true;
289+
StageProducer.PlayerStats.RemoveNote(_toRemove);
290+
_selectedRemoveButton.QueueFree();
291+
CloseRemovalPane();
292+
}
293+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://dg0xaieus84ns

0 commit comments

Comments
 (0)