diff --git a/Assets/Scenes/main.unity b/Assets/Scenes/main.unity index d36b77d..66c64c0 100644 --- a/Assets/Scenes/main.unity +++ b/Assets/Scenes/main.unity @@ -514,6 +514,55 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &749274448 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 749274450} + - component: {fileID: 749274449} + m_Layer: 0 + m_Name: GameManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &749274449 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 749274448} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41ea29e7d7d04eb5be628e56520e9bbd, type: 3} + m_Name: + m_EditorClassIdentifier: + currentRound: 1 + coins: 0 + roundTime: 10 + maxRounds: 20 + player: {fileID: 1261447603} +--- !u!4 &749274450 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 749274448} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &869793958 GameObject: m_ObjectHideFlags: 0 @@ -601,6 +650,85 @@ SpriteRenderer: m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 +--- !u!1 &1020220798 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1020220801} + - component: {fileID: 1020220800} + - component: {fileID: 1020220799} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1020220799 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1020220798} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_MoveRepeatDelay: 0.5 + m_MoveRepeatRate: 0.1 + m_XRTrackingOrigin: {fileID: 0} + m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_DeselectOnBackgroundClick: 1 + m_PointerBehavior: 0 + m_CursorLockBehavior: 0 + m_ScrollDeltaPerTick: 6 +--- !u!114 &1020220800 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1020220798} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &1020220801 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1020220798} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1261447599 GameObject: m_ObjectHideFlags: 0 @@ -726,6 +854,7 @@ MonoBehaviour: armor: 0 level: 1 experience: 0 + baseExperienceToLevelUp: 100 damage: 1 rangedDamage: 1 meleeDamage: 1 @@ -1056,6 +1185,7 @@ MonoBehaviour: armor: 0 level: 1 experience: 0 + baseExperienceToLevelUp: 100 damage: 1 rangedDamage: 1 meleeDamage: 1 @@ -1163,6 +1293,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 73900af7351645fdb19d016d621feb35, type: 3} m_Name: m_EditorClassIdentifier: + expReward: 5 + coinReward: 1 --- !u!114 &2062887722 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1192,6 +1324,231 @@ MonoBehaviour: m_EditorClassIdentifier: character: {fileID: 2062887717} initialHealth: 100 +--- !u!1001 &9153788689307318134 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_Pivot.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_Pivot.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1231569574627435091, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: itemsPerShop + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: inventoryManager + value: + objectReference: {fileID: 1261447609} + - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: possibleItems.Array.size + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: possibleWeapons.Array.size + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: 'possibleItems.Array.data[0]' + value: + objectReference: {fileID: 11400000, guid: 08c8a414828d19951b3a413f631a6d70, type: 2} + - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: 'possibleWeapons.Array.data[0]' + value: + objectReference: {fileID: 11400000, guid: 53811578f5e9423a584055fe920fa137, type: 2} + - target: {fileID: 4567329713039532410, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4567329713039532410, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4567329713039532410, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4567329713039532410, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4567329713039532410, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4985729667885723412, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_Name + value: Shop UI + objectReference: {fileID: 0} + - target: {fileID: 5200376552900448965, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5200376552900448965, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5200376552900448965, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5200376552900448965, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5200376552900448965, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5207083978762949642, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5207083978762949642, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5207083978762949642, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5207083978762949642, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5207083978762949642, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5210823007245714808, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5210823007245714808, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5210823007245714808, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5210823007245714808, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5210823007245714808, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6312224718360856168, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_BlockingMask.m_Bits + value: 247 + objectReference: {fileID: 0} + - target: {fileID: 8381921428845315272, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8381921428845315272, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8381921428845315272, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8381921428845315272, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8381921428845315272, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 @@ -1201,3 +1558,6 @@ SceneRoots: - {fileID: 1261447604} - {fileID: 541374775} - {fileID: 2062887720} + - {fileID: 9153788689307318134} + - {fileID: 1020220801} + - {fileID: 749274450} diff --git a/Assets/Scripts/Data/CharacterAttributes.cs b/Assets/Scripts/Data/CharacterAttributes.cs index 892a9c5..6aaa2ac 100644 --- a/Assets/Scripts/Data/CharacterAttributes.cs +++ b/Assets/Scripts/Data/CharacterAttributes.cs @@ -28,6 +28,7 @@ namespace Data [OdinSerialize] public float armor = 0f; [OdinSerialize] public int level = 1; [OdinSerialize] public int experience = 0; + [OdinSerialize] public int baseExperienceToLevelUp = 100; [OdinSerialize, PropertyTooltip("This is damage multiplier")] public float damage = 1f; @@ -54,6 +55,7 @@ namespace Data public event Action OnExperienceChanged; public event Action OnLevelChanged; + public event Action OnLevelUp; public float Health { @@ -174,7 +176,15 @@ namespace Data experience = value; OnExperienceChanged?.Invoke(experience); - //TODO: Implement level up logic + if (experience >= ExperienceToNextLevel()) + { + Level++; + experience -= ExperienceToNextLevel(); + } + else if (experience < 0) + { + experience = 0; + } } } @@ -186,6 +196,7 @@ namespace Data if (level == value) return; level = value; OnLevelChanged?.Invoke(level); + OnLevelUp?.Invoke(); } } @@ -327,5 +338,10 @@ namespace Data Level = 1; Experience = 0; } + + private int ExperienceToNextLevel() + { + return (int)(baseExperienceToLevelUp * Math.Pow(Level, 2)); + } } } \ No newline at end of file diff --git a/Assets/Scripts/Inventory/StatModifierItem.cs b/Assets/Scripts/Inventory/StatModifierItem.cs index 4693378..69e49ab 100644 --- a/Assets/Scripts/Inventory/StatModifierItem.cs +++ b/Assets/Scripts/Inventory/StatModifierItem.cs @@ -12,8 +12,31 @@ namespace Inventory public string itemName; [TextArea] public string description; public Sprite icon; + public int price; public List cures = new(); public List curses = new(); + + [Button("Build Description")] + private void BuildDescription() + { + var descriptionBuilder = new System.Text.StringBuilder(); + foreach (var modifier in cures) + { + if (descriptionBuilder.Length > 0) descriptionBuilder.Append(", "); + + var desc = $"Cure: {modifier.Description}"; + descriptionBuilder.Append(desc); + } + + foreach (var modifier in curses) + { + if (descriptionBuilder.Length > 0) descriptionBuilder.Append(", "); + var desc = $"Curse: {modifier.Description}"; + descriptionBuilder.Append(desc); + } + + description = descriptionBuilder.ToString(); + } } } \ No newline at end of file diff --git a/Assets/Scripts/Inventory/WeaponItem.cs b/Assets/Scripts/Inventory/WeaponItem.cs index eb95c37..73c23b8 100644 --- a/Assets/Scripts/Inventory/WeaponItem.cs +++ b/Assets/Scripts/Inventory/WeaponItem.cs @@ -10,5 +10,6 @@ namespace Inventory [OdinSerialize, TextArea] public string description; [OdinSerialize] public GameObject prefab; [OdinSerialize] public Sprite icon; + [OdinSerialize] public int price; } } \ No newline at end of file diff --git a/Assets/Scripts/Modifiers/PercentStatModifier.cs b/Assets/Scripts/Modifiers/PercentStatModifier.cs index d143935..37d1fc1 100644 --- a/Assets/Scripts/Modifiers/PercentStatModifier.cs +++ b/Assets/Scripts/Modifiers/PercentStatModifier.cs @@ -11,7 +11,7 @@ namespace Modifiers public Stat stat; public float percent; - public string Description => $"{stat} +{percent * 100}%"; + public string Description => GetDescription(); public void Apply(CharacterAttributes attributes) { @@ -55,5 +55,11 @@ namespace Modifiers _ => throw new System.ArgumentOutOfRangeException() }; } + + private string GetDescription() + { + var sign = percent >= 0 ? "+" : ""; + return $"{stat} {sign}{percent * 100}%"; + } } } \ No newline at end of file diff --git a/Assets/Scripts/Shop.meta b/Assets/Scripts/Shop.meta new file mode 100644 index 0000000..cc5f629 --- /dev/null +++ b/Assets/Scripts/Shop.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b873cfd693194508ae2ef2947a6c42fd +timeCreated: 1752272703 \ No newline at end of file diff --git a/Assets/Scripts/Shop/ShopManager.cs b/Assets/Scripts/Shop/ShopManager.cs new file mode 100644 index 0000000..b831892 --- /dev/null +++ b/Assets/Scripts/Shop/ShopManager.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using Inventory; +using Sirenix.Serialization; +using Systems; +using UnityEngine; +using Random = UnityEngine.Random; + +namespace Shop +{ + public class ShopManager : MonoBehaviour + { + private List currentItemChoices = new(); + private List currentWeaponChoices = new(); + + [SerializeField] private ShopUI shopUI; + [SerializeField] private InventoryManager inventoryManager; + + [SerializeField] private int itemsPerShop = 4; + [OdinSerialize, SerializeField] private List possibleItems = new(); + [OdinSerialize, SerializeField] private List possibleWeapons = new(); + + private void OnEnable() + { + // GameManager.Instance.OnRoundEnd += OpenShop; + GameManager.Instance.OnStoreOpen += OpenShop; + } + + private void OnDisable() + { + // GameManager.Instance.OnRoundEnd -= OpenShop; + GameManager.Instance.OnStoreOpen -= OpenShop; + } + + public void CloseShop() + { + shopUI.Hide(); + Time.timeScale = 1f; + } + + public void BuyItem(StatModifierItem item, int price) + { + if (GameManager.Instance.Coins < price) return; + + GameManager.Instance.SpendCoins(price); + inventoryManager.EquipItem(item); + shopUI.MarkAsPurchased(item); + } + + public void BuyWeapon(WeaponItem weapon, int price) + { + if (GameManager.Instance.Coins < price) return; + + GameManager.Instance.SpendCoins(price); + inventoryManager.EquipWeapon(weapon); + shopUI.MarkAsPurchased(weapon); + } + + public void RerollShop() + { + currentItemChoices = DrawRandomItems(possibleItems, itemsPerShop); + currentWeaponChoices = DrawRandomItems(possibleWeapons, itemsPerShop); + + shopUI.Show(currentItemChoices, currentWeaponChoices, this); + } + + private void OpenShop() + { + OpenShop(GameManager.Instance.CurrentRound); + } + + private void OpenShop(int round) + { + currentItemChoices = DrawRandomItems(possibleItems, itemsPerShop); + currentWeaponChoices = DrawRandomItems(possibleWeapons, itemsPerShop); + + shopUI.Show(currentItemChoices, currentWeaponChoices, this); + Time.timeScale = 0f; + } + + private List DrawRandomItems(List pool, int count) + { + var result = new List(); + var poolCopy = new List(pool); + + for (var i = 0; i < count && poolCopy.Count > 0; i++) + { + var idx = Random.Range(0, poolCopy.Count); + result.Add(poolCopy[idx]); + poolCopy.RemoveAt(idx); + } + + return result; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Shop/ShopManager.cs.meta b/Assets/Scripts/Shop/ShopManager.cs.meta new file mode 100644 index 0000000..fee8a88 --- /dev/null +++ b/Assets/Scripts/Shop/ShopManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3f37c728615643a1abc988bbfd34986c +timeCreated: 1752272671 \ No newline at end of file diff --git a/Assets/Scripts/Shop/ShopSlotUI.cs b/Assets/Scripts/Shop/ShopSlotUI.cs new file mode 100644 index 0000000..181f009 --- /dev/null +++ b/Assets/Scripts/Shop/ShopSlotUI.cs @@ -0,0 +1,56 @@ +using Inventory; +using Systems; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Shop +{ + public class ShopSlotUI : MonoBehaviour + { + private ScriptableObject item; + private ShopManager shopManager; + private int price; + + [SerializeField] private Image icon; + [SerializeField] private TextMeshProUGUI nameText; + [SerializeField] private TextMeshProUGUI descriptionText; + [SerializeField] private TextMeshProUGUI priceText; + [SerializeField] private Button purchaseButton; + + public void Setup(StatModifierItem item, ShopManager manager) + { + this.item = item; + shopManager = manager; + price = item.price; + + icon.sprite = item.icon; + nameText.text = item.name; + descriptionText.text = item.description; + priceText.text = $"Price: {price}"; + + purchaseButton.interactable = GameManager.Instance.Coins >= price; + purchaseButton.onClick.AddListener(() => shopManager.BuyItem(item, price)); + } + + public void Setup(WeaponItem weapon, ShopManager manager) + { + item = weapon; + shopManager = manager; + price = weapon.price; + icon.sprite = weapon.icon; + nameText.text = weapon.weaponName; + descriptionText.text = weapon.description; + priceText.text = $"Price: {price}"; + purchaseButton.interactable = GameManager.Instance.Coins >= price; + purchaseButton.onClick.AddListener(() => shopManager.BuyWeapon(weapon, price)); + } + + public bool MatchesItem(ScriptableObject item) => this.item == item; + + public void MarkAsPurchased() + { + purchaseButton.interactable = false; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Shop/ShopSlotUI.cs.meta b/Assets/Scripts/Shop/ShopSlotUI.cs.meta new file mode 100644 index 0000000..82adffc --- /dev/null +++ b/Assets/Scripts/Shop/ShopSlotUI.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1d76cc9c15ed441b930dbe52e5e9af5f +timeCreated: 1752273703 \ No newline at end of file diff --git a/Assets/Scripts/Shop/ShopUI.cs b/Assets/Scripts/Shop/ShopUI.cs new file mode 100644 index 0000000..19c953f --- /dev/null +++ b/Assets/Scripts/Shop/ShopUI.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Inventory; +using Systems; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Shop +{ + public class ShopUI : MonoBehaviour + { + [SerializeField] private GameObject shopPanel; + [SerializeField] private Transform itemSlotParent; + [SerializeField] private Transform weaponSlotParent; + [SerializeField] private ShopSlotUI slotPrefab; + [SerializeField] private TextMeshProUGUI roundsText; + + private List currentSlots = new(); + + private void OnEnable() + { + GameManager.Instance.OnRoundEnd += UpdateRoundText; + } + + private void OnDisable() + { + GameManager.Instance.OnRoundEnd -= UpdateRoundText; + } + + public void Show(List items, List weapons, ShopManager shopManager) + { + GameManager.Instance.StoreIsClosed = false; + UpdateRoundText(GameManager.Instance.CurrentRound); + + shopPanel.SetActive(true); + ClearSlots(); + + foreach (var item in items) + { + var slot = Instantiate(slotPrefab, itemSlotParent); + slot.Setup(item, shopManager); + currentSlots.Add(slot); + } + + foreach (var weapon in weapons) + { + var slot = Instantiate(slotPrefab, weaponSlotParent); + slot.Setup(weapon, shopManager); + currentSlots.Add(slot); + } + } + + public void Hide() + { + GameManager.Instance.StoreIsClosed = true; + shopPanel.SetActive(false); + ClearSlots(); + } + + public void MarkAsPurchased(ScriptableObject item) + { + foreach (var slot in currentSlots) + { + if (slot.MatchesItem(item)) slot.MarkAsPurchased(); + } + } + + private void ClearSlots() + { + foreach (var slot in currentSlots) Destroy(slot.gameObject); + + currentSlots.Clear(); + } + + private void UpdateRoundText(int round) + { + var nextRound = Mathf.Min(round + 1, GameManager.Instance.MaxRounds); + roundsText.text = $"Round: {nextRound}/{GameManager.Instance.MaxRounds}"; + } + + } +} \ No newline at end of file diff --git a/Assets/Scripts/Shop/ShopUI.cs.meta b/Assets/Scripts/Shop/ShopUI.cs.meta new file mode 100644 index 0000000..0e7387a --- /dev/null +++ b/Assets/Scripts/Shop/ShopUI.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: abe11e705b4f47feb25a4e4845d1e6e9 +timeCreated: 1752272723 \ No newline at end of file diff --git a/Assets/Scripts/Systems/EnemyDeathBehavior.cs b/Assets/Scripts/Systems/EnemyDeathBehavior.cs index eefd192..b230227 100644 --- a/Assets/Scripts/Systems/EnemyDeathBehavior.cs +++ b/Assets/Scripts/Systems/EnemyDeathBehavior.cs @@ -1,15 +1,21 @@ using Interfaces; +using Sirenix.Serialization; using UnityEngine; namespace Systems { public class EnemyDeathBehavior : MonoBehaviour, IDeathBehavior { + [OdinSerialize, SerializeField] private int expReward = 5; + [OdinSerialize, SerializeField] private int coinReward = 1; + public void Die() { + GameManager.Instance.Player.attributes.ModifyExperience(expReward); + GameManager.Instance.AddCoins(coinReward); Destroy(gameObject); + // later let's add particle effects, sound effects, etc. - // and give player experience points } } } \ No newline at end of file diff --git a/Assets/Scripts/Systems/GameManager.cs b/Assets/Scripts/Systems/GameManager.cs new file mode 100644 index 0000000..0524510 --- /dev/null +++ b/Assets/Scripts/Systems/GameManager.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using Data; +using Sirenix.Serialization; +using UnityEngine; + +namespace Systems +{ + public class GameManager : MonoBehaviour + { + private float timer; + + public static GameManager Instance { get; private set; } + + [OdinSerialize, SerializeField] private int currentRound = 1; + [OdinSerialize, SerializeField] private int coins = 0; + [OdinSerialize, SerializeField] private float roundTime = 60f; + [OdinSerialize, SerializeField] private int maxRounds = 20; + + [OdinSerialize, SerializeField] private Character player; + + public Character Player => player; + public int Coins => coins; + public int CurrentRound => currentRound; + public float RoundTime => roundTime; + public int MaxRounds => maxRounds; + public bool StoreIsClosed { get; set; } = true; + + public event Action OnRoundStart; + public event Action OnRoundEnd; + public event Action OnStoreOpen; + + + private void Awake() + { + if (Instance == null) + { + Instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } + + private void Start() + { + StartCoroutine(RoundLoop()); + } + + private IEnumerator RoundLoop() + { + OnStoreOpen?.Invoke(); + yield return new WaitUntil(() => StoreIsClosed); + + for (currentRound = 1; currentRound <= maxRounds; currentRound++) + { + OnRoundStart?.Invoke(currentRound); + timer = roundTime; + + while (timer > 0) + { + timer -= Time.deltaTime; + yield return null; + } + + OnRoundEnd?.Invoke(currentRound); + StoreIsClosed = false; + OnStoreOpen?.Invoke(); + yield return new WaitUntil(() => StoreIsClosed); + } + } + + public void AddCoins(int amount) + { + coins += amount; + } + + public void SpendCoins(int amount) + { + if (coins >= amount) + { + coins -= amount; + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Systems/GameManager.cs.meta b/Assets/Scripts/Systems/GameManager.cs.meta new file mode 100644 index 0000000..1c3342e --- /dev/null +++ b/Assets/Scripts/Systems/GameManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 41ea29e7d7d04eb5be628e56520e9bbd +timeCreated: 1752271597 \ No newline at end of file