Implementation of saving inventory items (had to resturcture texture loading)

This commit is contained in:
2025-03-04 22:21:18 -08:00
parent d35e81a9a8
commit 8cc6d85265
55 changed files with 797 additions and 727 deletions

View File

@@ -0,0 +1 @@
uid://dd3so0j48jtyc

View File

@@ -14,6 +14,7 @@ config/name="GameJamDungeon"
run/main_scene="uid://d1gjaijijd5ot" run/main_scene="uid://d1gjaijijd5ot"
run/print_header=false run/print_header=false
config/features=PackedStringArray("4.4", "C#", "GL Compatibility") config/features=PackedStringArray("4.4", "C#", "GL Compatibility")
run/max_fps=60
boot_splash/show_image=false boot_splash/show_image=false
[autoload] [autoload]
@@ -192,6 +193,16 @@ Previous={
"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null) "events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null)
] ]
} }
Save={
"deadzone": 0.2,
"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null)
]
}
Load={
"deadzone": 0.2,
"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null)
]
}
[internationalization] [internationalization]

View File

@@ -50,7 +50,7 @@ public partial class InGameAudioLogic
private void OnInventorySorted() => Output(new Output.PlayInventorySortedSound()); private void OnInventorySorted() => Output(new Output.PlayInventorySortedSound());
private void OnEquippedItem(IEquipableItem equipableItem) => Output(new Output.PlayEquipSound()); private void OnEquippedItem(EquipableItem equipableItem) => Output(new Output.PlayEquipSound());
private void OnOverworldEntered() => Output(new Output.PlayOverworldMusic()); private void OnOverworldEntered() => Output(new Output.PlayOverworldMusic());

View File

@@ -135,7 +135,7 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
_enemyModelView.PlayHitAnimation(); _enemyModelView.PlayHitAnimation();
_enemyLogic.Input(new EnemyLogic.Input.Alerted()); _enemyLogic.Input(new EnemyLogic.Input.Alerted());
if (_player.EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.SelfDamage)) if (_player.EquippedWeapon.Value.WeaponTag == WeaponTag.SelfDamage)
_player.Stats.SetCurrentHP(_player.Stats.CurrentHP.Value - 5); _player.Stats.SetCurrentHP(_player.Stats.CurrentHP.Value - 5);
} }
} }

View File

@@ -1,13 +1,23 @@
namespace GameJamDungeon; using System.Text.Json.Serialization;
namespace GameJamDungeon;
public enum WeaponTag public enum WeaponTag
{ {
None,
SelfDamage, SelfDamage,
IgnoreAffinity, IgnoreAffinity,
Knockback, Knockback,
} }
[JsonSerializable(typeof(WeaponTag))]
public partial class WeaponTagEnumContext : JsonSerializerContext;
public enum ItemTag public enum ItemTag
{ {
None,
BreaksOnChange BreaksOnChange
} }
[JsonSerializable(typeof(ItemTag))]
public partial class ItemTagEnumContext : JsonSerializerContext;

View File

@@ -1,4 +1,6 @@
namespace GameJamDungeon; using System.Text.Json.Serialization;
namespace GameJamDungeon;
public enum ElementType public enum ElementType
{ {
@@ -9,3 +11,6 @@ public enum ElementType
Igneous, Igneous,
Ferrum Ferrum
} }
[JsonSerializable(typeof(ElementType))]
public partial class ElementTypeEnumContext : JsonSerializerContext;

View File

@@ -7,11 +7,12 @@ using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder; using Chickensoft.SaveFileBuilder;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Chickensoft.Serialization.Godot; using Chickensoft.Serialization.Godot;
using GameJamDungeon.src.items;
using Godot; using Godot;
using Org.BouncyCastle.Asn1.Pkcs;
using System; using System;
using System.IO.Abstractions; using System.IO.Abstractions;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using static GameJamDungeon.GameLogic.State; using static GameJamDungeon.GameLogic.State;
[Meta(typeof(IAutoNode))] [Meta(typeof(IAutoNode))]
@@ -78,6 +79,8 @@ public partial class Game : Node3D, IGame
public RescuedItemDatabase RescuedItems { get; set; } = default!; public RescuedItemDatabase RescuedItems { get; set; } = default!;
private EffectService _effectService;
public void Setup() public void Setup()
{ {
FileSystem = new FileSystem(); FileSystem = new FileSystem();
@@ -96,6 +99,7 @@ public partial class Game : Node3D, IGame
var resolver = new SerializableTypeResolver(); var resolver = new SerializableTypeResolver();
GodotSerialization.Setup(); GodotSerialization.Setup();
Serializer.AddConverter(new Texture2DConverter());
var upgradeDependencies = new Blackboard(); var upgradeDependencies = new Blackboard();
@@ -104,7 +108,7 @@ public partial class Game : Node3D, IGame
Converters = { Converters = {
new SerializableTypeConverter(upgradeDependencies) new SerializableTypeConverter(upgradeDependencies)
}, },
TypeInfoResolver = resolver, TypeInfoResolver = JsonTypeInfoResolver.Combine(resolver, WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, ThrowableItemTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default),
WriteIndented = true WriteIndented = true
}; };
@@ -113,23 +117,27 @@ public partial class Game : Node3D, IGame
{ {
var gameData = new GameData() var gameData = new GameData()
{ {
PlayerData = new PlayerStats PlayerData = new PlayerData()
{ {
CurrentHP = Player.Stats.CurrentHP.Value, PlayerStats = new PlayerStats()
MaximumHP = Player.Stats.MaximumHP.Value, {
CurrentVT = Player.Stats.CurrentVT.Value, CurrentHP = Player.Stats.CurrentHP.Value,
MaximumVT = Player.Stats.MaximumVT.Value, MaximumHP = Player.Stats.MaximumHP.Value,
CurrentAttack = Player.Stats.CurrentAttack.Value, CurrentVT = Player.Stats.CurrentVT.Value,
BonusAttack = Player.Stats.BonusAttack.Value, MaximumVT = Player.Stats.MaximumVT.Value,
MaxAttack = Player.Stats.MaxAttack.Value, CurrentAttack = Player.Stats.CurrentAttack.Value,
CurrentDefense = Player.Stats.CurrentDefense.Value, BonusAttack = Player.Stats.BonusAttack.Value,
BonusDefense = Player.Stats.BonusDefense.Value, MaxAttack = Player.Stats.MaxAttack.Value,
MaxDefense = Player.Stats.MaxDefense.Value, CurrentDefense = Player.Stats.CurrentDefense.Value,
CurrentExp = Player.Stats.CurrentExp.Value, BonusDefense = Player.Stats.BonusDefense.Value,
CurrentLevel = Player.Stats.CurrentLevel.Value, MaxDefense = Player.Stats.MaxDefense.Value,
ExpToNextLevel = Player.Stats.ExpToNextLevel.Value, CurrentExp = Player.Stats.CurrentExp.Value,
Luck = Player.Stats.Luck.Value CurrentLevel = Player.Stats.CurrentLevel.Value,
}, ExpToNextLevel = Player.Stats.ExpToNextLevel.Value,
Luck = Player.Stats.Luck.Value
},
Inventory = Player.Inventory
}
}; };
return gameData; return gameData;
@@ -222,12 +230,22 @@ public partial class Game : Node3D, IGame
GameEventDepot.RestorativePickedUp += GameEventDepot_RestorativePickedUp; GameEventDepot.RestorativePickedUp += GameEventDepot_RestorativePickedUp;
DoubleEXPTimer.Timeout += DoubleEXPTimer_Timeout; DoubleEXPTimer.Timeout += DoubleEXPTimer_Timeout;
_effectService = new EffectService(this, Player);
}
public void Save()
{
SaveFile.Save();
}
public void Load()
{
SaveFile.Load();
} }
public void ToggleInventory() public void ToggleInventory()
{ {
SaveFile.Load();
if (GameLogic.Value is InventoryOpened) if (GameLogic.Value is InventoryOpened)
GameLogic.Input(new GameLogic.Input.CloseInventory()); GameLogic.Input(new GameLogic.Input.CloseInventory());
else else
@@ -250,7 +268,81 @@ public partial class Game : Node3D, IGame
InGameUI.PlayerInfoUI.DisplayMessage($"{pickedUpItemName} picked up."); InGameUI.PlayerInfoUI.DisplayMessage($"{pickedUpItemName} picked up.");
} }
public void DropItem(IInventoryItem item) public void UseItem(InventoryItem item)
{
if (item is ConsumableItem consumableItem)
{
if (Player.Stats.CurrentHP == Player.Stats.MaximumHP && consumableItem.RaiseHPAmount > 0)
Player.RaiseHP(consumableItem.RaiseHPAmount);
if (Player.Stats.CurrentVT == Player.Stats.MaximumVT && consumableItem.RaiseVTAmount > 0)
Player.RaiseVT(consumableItem.RaiseVTAmount);
if (consumableItem.HealHPAmount > 0 && Player.Stats.CurrentHP != Player.Stats.MaximumHP)
Player.HealHP(consumableItem.HealHPAmount);
if (consumableItem.HealVTAmount > 0 && Player.Stats.CurrentVT != Player.Stats.MaximumVT)
Player.HealVT(consumableItem.HealVTAmount);
}
if (item is EffectItem effectItem)
{
switch (effectItem.UsableItemTag)
{
case UsableItemTag.DoubleEXP:
DoubleEXP(TimeSpan.FromSeconds(30));
break;
case UsableItemTag.TeleportAllEnemiesToRoom:
_effectService.TeleportEnemiesToCurrentRoom();
break;
case UsableItemTag.KillHalfEnemiesInRoom:
_effectService.KillHalfEnemiesInRoom();
break;
case UsableItemTag.TurnAllEnemiesIntoHealingItem:
_effectService.TurnAllEnemiesInRoomIntoHealingItem();
break;
case UsableItemTag.HealsAllInRoomToMaxHP:
_effectService.HealAllEnemiesAndPlayerInRoomToFull();
break;
case UsableItemTag.AbsorbHPFromAllEnemiesInRoom:
_effectService.AbsorbHPFromAllEnemiesInRoom();
break;
case UsableItemTag.DealElementalDamageToAllEnemiesInRoom:
_effectService.DealElementalDamageToAllEnemiesInRoom(ElementType.Hydric);
break;
case UsableItemTag.SwapHPAndVT:
_effectService.SwapHPandVT();
break;
case UsableItemTag.RaiseCurrentWeaponAttack:
_effectService.RaiseCurrentWeaponAttack();
break;
case UsableItemTag.RaiseCurrentDefenseArmor:
_effectService.RaiseCurrentArmorDefense();
break;
case UsableItemTag.RaiseLevel:
_effectService.RaiseLevel();
break;
case UsableItemTag.RandomEffect:
_effectService.RandomEffect(effectItem);
break;
}
}
if (item is ThrowableItem throwableItem)
{
if (throwableItem.HealHPAmount > 0)
Player.HealHP(throwableItem.HealHPAmount);
if (throwableItem.HealVTAmount > 0)
Player.HealVT(throwableItem.HealVTAmount);
if (throwableItem.ThrowableItemTag == ThrowableItemTag.TeleportToRandomLocation)
_effectService.TeleportToRandomRoom(Player);
if (throwableItem.ThrowableItemTag == ThrowableItemTag.CanChangeAffinity)
_effectService.ChangeAffinity(throwableItem);
if (throwableItem.ThrowableItemTag == ThrowableItemTag.WarpToExitIfFound)
_effectService.WarpToExit(Player);
}
}
public void DropItem(InventoryItem item)
{ {
var droppedScene = GD.Load<PackedScene>("res://src/items/dropped/DroppedItem.tscn"); var droppedScene = GD.Load<PackedScene>("res://src/items/dropped/DroppedItem.tscn");
var dropped = droppedScene.Instantiate<DroppedItem>(); var dropped = droppedScene.Instantiate<DroppedItem>();
@@ -259,14 +351,14 @@ public partial class Game : Node3D, IGame
dropped.Drop(); dropped.Drop();
} }
public void ThrowItem(IInventoryItem item) public void ThrowItem(InventoryItem item)
{ {
var thrownScene = GD.Load<PackedScene>("res://src/items/thrown/ThrownItem.tscn"); var thrownScene = GD.Load<PackedScene>("res://src/items/thrown/ThrownItem.tscn");
var thrown = thrownScene.Instantiate<ThrownItem>(); var thrown = thrownScene.Instantiate<ThrownItem>();
thrown.ItemThatIsThrown = item; thrown.ItemThatIsThrown = item;
AddChild(thrown); AddChild(thrown);
thrown.Position += new Vector3(0, 1.5f, 0); thrown.Position += new Vector3(0, 1.5f, 0);
thrown.Throw(); thrown.Throw(_effectService);
} }
public void AnnounceMessageOnInventoryScreen(string message) public void AnnounceMessageOnInventoryScreen(string message)
@@ -323,11 +415,11 @@ public partial class Game : Node3D, IGame
private void FloorClearMenu_TransitionCompleted() private void FloorClearMenu_TransitionCompleted()
{ {
GameRepo.Resume(); GameRepo.Resume();
if (Player.EquippedWeapon.Value.ItemTags.Contains(ItemTag.BreaksOnChange)) if (Player.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange)
Player.Unequip(Player.EquippedWeapon.Value); Player.Unequip(Player.EquippedWeapon.Value);
if (Player.EquippedArmor.Value.ItemTags.Contains(ItemTag.BreaksOnChange)) if (Player.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnChange)
Player.Unequip(Player.EquippedArmor.Value); Player.Unequip(Player.EquippedArmor.Value);
if (Player.EquippedAccessory.Value.ItemTags.Contains(ItemTag.BreaksOnChange)) if (Player.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnChange)
Player.Unequip(Player.EquippedAccessory.Value); Player.Unequip(Player.EquippedAccessory.Value);
} }

View File

@@ -4,7 +4,7 @@
[ext_resource type="Shader" uid="uid://dmjxo4k2rx1an" path="res://src/app/App.gdshader" id="2_6ifxs"] [ext_resource type="Shader" uid="uid://dmjxo4k2rx1an" path="res://src/app/App.gdshader" id="2_6ifxs"]
[ext_resource type="PackedScene" uid="uid://by67pn7fdsg1m" path="res://src/map/Map.tscn" id="3_d8awv"] [ext_resource type="PackedScene" uid="uid://by67pn7fdsg1m" path="res://src/map/Map.tscn" id="3_d8awv"]
[ext_resource type="PackedScene" uid="uid://cfecvvav8kkp6" path="res://src/player/Player.tscn" id="3_kk6ly"] [ext_resource type="PackedScene" uid="uid://cfecvvav8kkp6" path="res://src/player/Player.tscn" id="3_kk6ly"]
[ext_resource type="Resource" uid="uid://bpdbuf0k0exb5" path="res://src/items/weapons/resources/Sword Sword Odette.tres" id="4_6pp6l"] [ext_resource type="Resource" uid="uid://bpdbuf0k0exb5" path="res://src/items/weapons/resources/Swan Sword Odette.tres" id="4_6pp6l"]
[ext_resource type="PackedScene" uid="uid://b1muxus5qdbeu" path="res://src/ui/in_game_ui/InGameUI.tscn" id="5_lxtnp"] [ext_resource type="PackedScene" uid="uid://b1muxus5qdbeu" path="res://src/ui/in_game_ui/InGameUI.tscn" id="5_lxtnp"]
[ext_resource type="PackedScene" uid="uid://b16ejcwanod72" path="res://src/audio/InGameAudio.tscn" id="6_qc71l"] [ext_resource type="PackedScene" uid="uid://b16ejcwanod72" path="res://src/audio/InGameAudio.tscn" id="6_qc71l"]
[ext_resource type="Script" uid="uid://daphxl6vvsbjm" path="res://src/game/DialogueController.cs" id="10_58pbt"] [ext_resource type="Script" uid="uid://daphxl6vvsbjm" path="res://src/game/DialogueController.cs" id="10_58pbt"]

View File

@@ -7,5 +7,5 @@ namespace GameJamDungeon;
public partial record GameData public partial record GameData
{ {
[Save("player_data")] [Save("player_data")]
public required PlayerStats PlayerData { get; init; } public required PlayerData PlayerData { get; init; }
} }

View File

@@ -15,9 +15,11 @@ public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvid
public IDungeonFloor CurrentFloor { get; } public IDungeonFloor CurrentFloor { get; }
public void DropItem(IInventoryItem item); public void UseItem(InventoryItem item);
public void ThrowItem(IInventoryItem item); public void DropItem(InventoryItem item);
public void ThrowItem(InventoryItem item);
public void DoubleEXP(TimeSpan lengthOfEffect); public void DoubleEXP(TimeSpan lengthOfEffect);
@@ -34,4 +36,8 @@ public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvid
public void NextFloorLoaded(); public void NextFloorLoaded();
public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource enemyStatResource); public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource enemyStatResource);
public void Save();
public void Load();
} }

View File

@@ -266,7 +266,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
var currentItem = ItemSlots.ElementAt(_currentIndex).Item; var currentItem = ItemSlots.ElementAt(_currentIndex).Item;
if (currentItem is IEquipableItem equipable) if (currentItem is EquipableItem equipable)
{ {
; ;
UseButton.Text = equipable.IsEquipped ? "Unequip" : "Equip"; UseButton.Text = equipable.IsEquipped ? "Unequip" : "Equip";
@@ -339,14 +339,14 @@ public partial class InventoryMenu : Control, IInventoryMenu
itemSlot.Item = item; itemSlot.Item = item;
ItemsPage.AddChildEx(itemSlot); ItemsPage.AddChildEx(itemSlot);
if (itemSlot.Item is IEquipableItem equipable && equipable.IsEquipped) if (itemSlot.Item is EquipableItem equipable && equipable.IsEquipped)
itemSlot.SetEquippedItemStyle(); itemSlot.SetEquippedItemStyle();
} }
if (ItemSlots.Any()) if (ItemSlots.Any())
{ {
ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle(); ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle();
if (ItemSlots.ElementAt(_currentIndex).Item is IEquipableItem equipable && equipable.IsEquipped) if (ItemSlots.ElementAt(_currentIndex).Item is EquipableItem equipable && equipable.IsEquipped)
ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle(); ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle();
} }
} }
@@ -355,7 +355,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{ {
await ToSignal(GetTree().CreateTimer(0.1f), "timeout"); await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetItemStyle(); itemSlot.SetItemStyle();
if (itemSlot.Item is IEquipableItem equipable && equipable.IsEquipped) if (itemSlot.Item is EquipableItem equipable && equipable.IsEquipped)
itemSlot.SetEquippedItemStyle(); itemSlot.SetEquippedItemStyle();
} }
@@ -370,7 +370,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
private async Task EquipOrUnequipItem() private async Task EquipOrUnequipItem()
{ {
var itemSlot = ItemSlots[_currentIndex]; var itemSlot = ItemSlots[_currentIndex];
if (itemSlot.Item is IEquipableItem equipableItem) if (itemSlot.Item is EquipableItem equipableItem)
{ {
if (equipableItem.IsEquipped) if (equipableItem.IsEquipped)
{ {
@@ -392,21 +392,21 @@ public partial class InventoryMenu : Control, IInventoryMenu
private async void UseButtonPressed() private async void UseButtonPressed()
{ {
var currentItem = ItemSlots[_currentIndex].Item; var currentItem = ItemSlots[_currentIndex].Item;
if (currentItem is IEquipableItem) if (currentItem is EquipableItem)
await EquipOrUnequipItem(); await EquipOrUnequipItem();
else if (currentItem is IUsableItem usableItem) else
{ {
usableItem.Use(); Game.UseItem(currentItem);
DestroyItem(usableItem); DestroyItem(currentItem);
EmitSignal(SignalName.ClosedMenu); EmitSignal(SignalName.ClosedMenu);
} }
RefreshUIAfterUserSelection(); RefreshUIAfterUserSelection();
} }
private async void DestroyItem(IUsableItem usableItem) private async void DestroyItem(InventoryItem item)
{ {
Player.Inventory.Remove(usableItem); Player.Inventory.Remove(item);
if (_currentIndex >= ItemSlots.Length - 1) if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--; _currentIndex--;
if (_currentIndex <= 0) if (_currentIndex <= 0)

View File

@@ -7,7 +7,7 @@ namespace GameJamDungeon;
public interface IItemSlot : IHBoxContainer public interface IItemSlot : IHBoxContainer
{ {
public IInventoryItem Item { get; set; } public InventoryItem Item { get; set; }
public void SetItemStyle(); public void SetItemStyle();
@@ -41,7 +41,7 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
{ {
ItemName.Text = Item.ItemName; ItemName.Text = Item.ItemName;
//EquipBonus.Text = "..."; //EquipBonus.Text = "...";
ItemTexture.Texture = Item.GetTexture(); ItemTexture.Texture = Item.ItemStats.Texture;
Player.EquippedWeapon.Sync += EquippedWeapon_Sync; Player.EquippedWeapon.Sync += EquippedWeapon_Sync;
Player.EquippedArmor.Sync += EquippedArmor_Sync; Player.EquippedArmor.Sync += EquippedArmor_Sync;
Player.EquippedAccessory.Sync += EquippedAccessory_Sync; Player.EquippedAccessory.Sync += EquippedAccessory_Sync;
@@ -90,7 +90,7 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
} }
public void SetSelectedItemStyle() public void SetSelectedItemStyle()
{ {
if (Item is IEquipableItem equipableItem && equipableItem.IsEquipped) if (Item is EquipableItem equipableItem && equipableItem.IsEquipped)
{ {
ItemName.LabelSettings = SelectedEquippedItemFont; ItemName.LabelSettings = SelectedEquippedItemFont;
//EquipBonus.LabelSettings = EquippedItemFont; //EquipBonus.LabelSettings = EquippedItemFont;
@@ -113,5 +113,5 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
//EquipBonus.LabelSettings = EquippedItemFont; //EquipBonus.LabelSettings = EquippedItemFont;
} }
public IInventoryItem Item { get; set; } = default!; public InventoryItem Item { get; set; } = default!;
} }

View File

@@ -4,10 +4,10 @@ namespace GameJamDungeon;
public class RescuedItemDatabase public class RescuedItemDatabase
{ {
public List<IInventoryItem> Items; public List<InventoryItem> Items;
public RescuedItemDatabase() public RescuedItemDatabase()
{ {
Items = new List<IInventoryItem>(); Items = new List<InventoryItem>();
} }
} }

219
src/items/EffectService.cs Normal file
View File

@@ -0,0 +1,219 @@
using Godot;
using System.Linq;
using System;
namespace GameJamDungeon.src.items
{
public class EffectService
{
private readonly IGame _game;
private readonly IPlayer _player;
public EffectService(IGame game, IPlayer player)
{
_game = game;
_player = player;
}
public void TeleportEnemiesToCurrentRoom()
{
var currentFloor = _game.CurrentFloor;
var rooms = currentFloor.Rooms;
var currentRoom = _player.GetCurrentRoom();
if (currentRoom is not MonsterRoom)
return;
var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom monsterRoom)
validRooms.Remove(monsterRoom);
var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom());
foreach (var enemy in enemyList)
{
var spawnPoints = currentMonsterRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyGlobalPosition(randomSpawnPoint.GlobalPosition);
}
}
public void KillHalfEnemiesInRoom()
{
var currentRoom = _player.GetCurrentRoom();
if (currentRoom is not MonsterRoom)
return;
var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().ToList();
var enemiesToKill = enemyList.Count / 2;
for (var i = 0; i < enemiesToKill; i++)
enemyList[i].Die();
}
public void TurnAllEnemiesInRoomIntoHealingItem()
{
var currentRoom = _player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies)
{
enemy.Die();
DropHealingItem(enemy.GetEnemyGlobalPosition());
}
}
public void DropHealingItem(Vector3 vector)
{
var consumableFolder = "res://src/items/consumable";
var restorativeScene = GD.Load<PackedScene>($"{consumableFolder}/ConsumableItem.tscn");
var consumable = restorativeScene.Instantiate<ConsumableItem>();
var resourceFiles = DirAccess.GetFilesAt($"{consumableFolder}/resources");
var rng = new RandomNumberGenerator();
rng.Randomize();
var randomResource = resourceFiles[rng.RandiRange(0, resourceFiles.Length - 1)];
var randomFile = ResourceLoader.Load<ConsumableItemStats>($"{consumableFolder}/resources/{randomResource}");
consumable.ItemStats = randomFile;
_game.AddChild(consumable);
consumable.GlobalPosition = vector;
}
public void HealAllEnemiesAndPlayerInRoomToFull()
{
var currentRoom = _player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies)
enemy.SetCurrentHP(enemy.GetMaximumHP());
_player.Stats.SetCurrentHP(_player.Stats.MaximumHP.Value);
}
public void AbsorbHPFromAllEnemiesInRoom()
{
var currentRoom = _player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
var hpToAbsorb = 0.0;
foreach (var enemy in currentEnemies)
hpToAbsorb += enemy.CurrentHP * 0.05;
_player.Stats.SetCurrentHP(_player.Stats.CurrentHP.Value + (int)hpToAbsorb);
GD.Print("HP to absorb: " + hpToAbsorb);
}
public void DealElementalDamageToAllEnemiesInRoom(ElementType elementType)
{
var currentRoom = _player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies)
enemy.TakeDamage(20, elementType);
}
public void SwapHPandVT()
{
var oldHp = _player.Stats.CurrentHP.Value;
var oldVt = _player.Stats.CurrentVT.Value;
_player.Stats.SetCurrentHP(oldVt);
_player.Stats.SetCurrentVT(oldHp);
}
public void RandomEffect(EffectItem item)
{
var itemEffects = Enum.GetValues<UsableItemTag>().ToList();
itemEffects.Remove(UsableItemTag.RandomEffect);
itemEffects.Remove(UsableItemTag.None);
var randomEffect = new Godot.Collections.Array<UsableItemTag>(itemEffects).PickRandom();
item.SetEffectTag(randomEffect);
_game.UseItem(item);
}
public void RaiseCurrentWeaponAttack()
{
if (_player.EquippedWeapon.Value.ItemName == string.Empty)
return;
var currentWeapon = _player.EquippedWeapon.Value;
currentWeapon.IncreaseWeaponAttack(1);
}
public void RaiseCurrentArmorDefense()
{
if (_player.EquippedArmor.Value.ItemName == string.Empty)
return;
var currentArmor = _player.EquippedArmor.Value;
currentArmor.IncreaseArmorDefense(1);
}
public void RaiseLevel()
{
_player.LevelUp();
}
public void TeleportToRandomRoom(IEnemy enemy)
{
var currentFloor = _game.CurrentFloor;
var rooms = currentFloor.Rooms;
var currentRoom = enemy.GetCurrentRoom();
var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom currentMonsterRoom)
validRooms.Remove(currentMonsterRoom);
if (validRooms.Count == 0)
return;
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms);
var randomRoom = roomsGodotCollection.PickRandom();
var spawnPoints = randomRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyGlobalPosition(randomSpawnPoint.GlobalPosition);
}
public void TeleportToRandomRoom(IPlayer player)
{
var currentFloor = _game.CurrentFloor;
var rooms = currentFloor.Rooms;
var currentRoom = rooms.SingleOrDefault(x => x.IsPlayerInRoom);
var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom currentMonsterRoom)
validRooms.Remove(currentMonsterRoom);
if (validRooms.Count == 0)
return;
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms);
var randomRoom = roomsGodotCollection.PickRandom();
var spawnPoint = randomRoom.PlayerSpawn;
_game.ToggleInventory();
player.TeleportPlayer(spawnPoint.GlobalPosition);
}
public void ChangeAffinity(ThrowableItem throwableItem)
{
var maximumElements = Enum.GetNames(typeof(ElementType)).Length;
throwableItem.SetElementType(throwableItem.ElementType + 1 % maximumElements);
// TODO: Make this an inventory animation to cycle through elements.
throwableItem.SetDescription(
$"Inflicts {throwableItem.ElementType} damage when thrown." +
$"{System.Environment.NewLine}Use item to change Affinity.");
}
public void WarpToExit(IPlayer player)
{
var exitRoom = _game.CurrentFloor.Rooms.OfType<ExitRoom>().Single();
if (exitRoom.PlayerDiscoveredRoom)
player.TeleportPlayer(exitRoom.PlayerSpawn.GlobalPosition);
}
public void WarpToExit(IEnemy enemy)
{
var exitRoom = _game.CurrentFloor.Rooms.OfType<ExitRoom>().Single();
enemy.SetEnemyGlobalPosition(exitRoom.PlayerSpawn.GlobalPosition);
}
}
}

View File

@@ -0,0 +1 @@
uid://cnvyubmj6u7qw

View File

@@ -0,0 +1,13 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
namespace GameJamDungeon;
[Meta]
public abstract partial class EquipableItem : InventoryItem
{
public abstract ItemTag ItemTag { get; }
[Save("equipable_item_is_equipped")]
public bool IsEquipped { get; set; }
}

View File

@@ -0,0 +1 @@
uid://cw8s4mjrde4no

View File

@@ -1,10 +0,0 @@
using System.Collections.Immutable;
namespace GameJamDungeon;
public interface IEquipableItem : IInventoryItem
{
public ImmutableList<ItemTag> ItemTags { get; }
public bool IsEquipped { get; }
}

View File

@@ -1,5 +1,7 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot; using Godot;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -8,11 +10,11 @@ namespace GameJamDungeon;
public interface IInventory : INode public interface IInventory : INode
{ {
public List<IInventoryItem> Items { get; } public List<InventoryItem> Items { get; }
public bool TryAdd(IInventoryItem inventoryItem); public bool TryAdd(InventoryItem inventoryItem);
public void Remove(IInventoryItem inventoryItem); public void Remove(InventoryItem inventoryItem);
public void Sort(); public void Sort();
@@ -21,6 +23,7 @@ public interface IInventory : INode
event Inventory.PickedUpItemEventHandler PickedUpItem; event Inventory.PickedUpItemEventHandler PickedUpItem;
} }
[Meta, Id("inventory")]
public partial class Inventory : Node, IInventory public partial class Inventory : Node, IInventory
{ {
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);
@@ -38,9 +41,10 @@ public partial class Inventory : Node, IInventory
Items = []; Items = [];
} }
public List<IInventoryItem> Items { get; private set; } [Save("inventory_items")]
public List<InventoryItem> Items { get; private set; }
public bool TryAdd(IInventoryItem inventoryItem) public bool TryAdd(InventoryItem inventoryItem)
{ {
if (Items.Count >= _maxInventorySize) if (Items.Count >= _maxInventorySize)
{ {
@@ -53,7 +57,7 @@ public partial class Inventory : Node, IInventory
return true; return true;
} }
public void Remove(IInventoryItem inventoryItem) => Items.Remove(inventoryItem); public void Remove(InventoryItem inventoryItem) => Items.Remove(inventoryItem);
public void Sort() public void Sort()
@@ -61,7 +65,7 @@ public partial class Inventory : Node, IInventory
var equippedWeapon = Items.OfType<Weapon>().Where(x => x.IsEquipped); var equippedWeapon = Items.OfType<Weapon>().Where(x => x.IsEquipped);
var equippedArmor = Items.OfType<Armor>().Where(x => x.IsEquipped); var equippedArmor = Items.OfType<Armor>().Where(x => x.IsEquipped);
var equippedAccessory = Items.OfType<Accessory>().Where(x => x.IsEquipped); var equippedAccessory = Items.OfType<Accessory>().Where(x => x.IsEquipped);
var equippedItems = new List<IInventoryItem>(); var equippedItems = new List<InventoryItem>();
equippedItems.AddRange(equippedWeapon); equippedItems.AddRange(equippedWeapon);
equippedItems.AddRange(equippedArmor); equippedItems.AddRange(equippedArmor);
equippedItems.AddRange(equippedAccessory); equippedItems.AddRange(equippedAccessory);

View File

@@ -1,4 +1,6 @@
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot; using Godot;
using System; using System;
@@ -17,13 +19,22 @@ public interface IInventoryItem : INode3D
double ThrowDamage { get; } double ThrowDamage { get; }
float ThrowSpeed { get; } float ThrowSpeed { get; }
Texture2D GetTexture();
public void SetItemStats(InventoryItemStats inventoryItemStats);
} }
public interface IUsableItem : IInventoryItem [Meta]
public abstract partial class InventoryItem : Node3D, IInventoryItem
{ {
public void Use(); [Save("inventory_item_id")]
public Guid ID => Guid.NewGuid();
public abstract string ItemName { get; }
public abstract string Description { get; }
[Save("inventory_item_spawn_rate")]
public abstract float SpawnRate { get; }
[Save("inventory_item_throw_damage")]
public abstract double ThrowDamage { get; }
[Save("inventory_item_throw_speed")]
public abstract float ThrowSpeed { get; }
[Save("inventory_item_stats")]
public abstract InventoryItemStats ItemStats { get; set; }
} }

View File

@@ -1,16 +1,22 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot; using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
[Meta, Id("inventory_item_stats")]
public partial class InventoryItemStats : Resource public partial class InventoryItemStats : Resource
{ {
[Export] [Export]
public string Name = string.Empty; [Save("inventory_item_name")]
public string Name { get; set; } = string.Empty;
[Export(PropertyHint.MultilineText)] [Export(PropertyHint.MultilineText)]
public string Description = string.Empty; [Save("inventory_item_description")]
public string Description { get; set; } = string.Empty;
[Export] [Save("inventory_item_texture")]
public Texture2D Texture { get; set; } [Export] public Texture2D Texture { get; set; } = default!;
[Export(PropertyHint.Range, "0, 1, 0.01")] [Export(PropertyHint.Range, "0, 1, 0.01")]
public float SpawnRate { get; set; } = 0.5f; public float SpawnRate { get; set; } = 0.5f;
@@ -28,5 +34,5 @@ public partial class InventoryItemStats : Resource
public int ThrowDamage { get; set; } = 5; public int ThrowDamage { get; set; } = 5;
[Export] [Export]
public Godot.Collections.Array<ItemTag> ItemTags { get; set; } = new Godot.Collections.Array<ItemTag>(); public ItemTag ItemTag { get; set; } = ItemTag.None;
} }

View File

@@ -0,0 +1,67 @@
using Godot;
using System.Text.Json.Serialization;
using System.Text.Json;
using System;
/// <summary>Basis JSON converter.</summary>
public class Texture2DConverter : JsonConverter<Texture2D>
{
/// <inheritdoc />
public override bool CanConvert(Type typeToConvert) =>
typeToConvert == typeof(Texture2D);
/// <inheritdoc />
public override Texture2D Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
var texture2D = new Texture2D();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return texture2D;
}
if (reader.TokenType != JsonTokenType.PropertyName)
{
continue;
}
var propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case "resource_path":
var resourcePath = JsonSerializer.Deserialize<string>(ref reader, options);
texture2D = GD.Load<Texture2D>(resourcePath);
break;
default:
break;
}
}
throw new JsonException("Unexpected end when reading Texture2D.");
}
/// <inheritdoc />
public override void Write(
Utf8JsonWriter writer,
Texture2D value,
JsonSerializerOptions options
)
{
var resolver = options.TypeInfoResolver;
var resourcePathType = resolver!.GetTypeInfo(typeof(string), options)!;
writer.WriteStartObject();
writer.WritePropertyName("resource_path");
JsonSerializer.Serialize(writer, value.ResourcePath, resourcePathType);
writer.WriteEndObject();
}
}

View File

@@ -0,0 +1 @@
uid://bdc3xwj2b1oog

View File

@@ -33,7 +33,7 @@ public partial class ItemDatabase : Node
{ {
var armorInfo = GD.Load<ArmorStats>($"res://src/items/armor/resources/{armor}"); var armorInfo = GD.Load<ArmorStats>($"res://src/items/armor/resources/{armor}");
var armorScene = ArmorScene.Instantiate<Armor>(); var armorScene = ArmorScene.Instantiate<Armor>();
armorScene.SetItemStats(armorInfo); armorScene.ItemStats = armorInfo;
database.Add(armorScene); database.Add(armorScene);
} }
@@ -41,7 +41,7 @@ public partial class ItemDatabase : Node
{ {
var weaponInfo = GD.Load<WeaponStats>($"res://src/items/weapons/resources/{weapon}"); var weaponInfo = GD.Load<WeaponStats>($"res://src/items/weapons/resources/{weapon}");
var weaponScene = WeaponScene.Instantiate<Weapon>(); var weaponScene = WeaponScene.Instantiate<Weapon>();
weaponScene.SetItemStats(weaponInfo); weaponScene.ItemStats = weaponInfo;
database.Add(weaponScene); database.Add(weaponScene);
} }
@@ -49,7 +49,7 @@ public partial class ItemDatabase : Node
{ {
var accessoryInfo = GD.Load<AccessoryStats>($"res://src/items/accessory/resources/{accessory}"); var accessoryInfo = GD.Load<AccessoryStats>($"res://src/items/accessory/resources/{accessory}");
var accessoryScene = AccessoryScene.Instantiate<Accessory>(); var accessoryScene = AccessoryScene.Instantiate<Accessory>();
accessoryScene.SetItemStats(accessoryInfo); accessoryScene.ItemStats = accessoryInfo;
database.Add(accessoryScene); database.Add(accessoryScene);
} }
@@ -57,7 +57,7 @@ public partial class ItemDatabase : Node
{ {
var throwableItemInfo = GD.Load<ThrowableItemStats>($"res://src/items/throwable/resources/{throwable}"); var throwableItemInfo = GD.Load<ThrowableItemStats>($"res://src/items/throwable/resources/{throwable}");
var throwableItemScene = ThrowableItemScene.Instantiate<ThrowableItem>(); var throwableItemScene = ThrowableItemScene.Instantiate<ThrowableItem>();
throwableItemScene.SetItemStats(throwableItemInfo); throwableItemScene.ItemStats = throwableItemInfo;
database.Add(throwableItemScene); database.Add(throwableItemScene);
} }
@@ -65,10 +65,10 @@ public partial class ItemDatabase : Node
{ {
var consumableItemInfo = GD.Load<ConsumableItemStats>($"res://src/items/consumable/resources/{consumable}"); var consumableItemInfo = GD.Load<ConsumableItemStats>($"res://src/items/consumable/resources/{consumable}");
var consumableItemScene = ConsumableItemScene.Instantiate<ConsumableItem>(); var consumableItemScene = ConsumableItemScene.Instantiate<ConsumableItem>();
consumableItemScene.SetItemStats(consumableItemInfo); consumableItemScene.ItemStats = consumableItemInfo;
database.Add(consumableItemScene); database.Add(consumableItemScene);
} }
return database.ToArray(); return [.. database];
} }
} }

View File

@@ -1,39 +1,24 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot; using Godot;
using System;
using System.Collections.Immutable;
namespace GameJamDungeon; namespace GameJamDungeon;
[Meta(typeof(IAutoNode))] [Meta, Id("accessory")]
public partial class Accessory : Node3D, IEquipableItem public partial class Accessory : EquipableItem
{ {
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Export] private AccessoryStats _accessoryStats { get; set; } = new AccessoryStats(); [Export] private AccessoryStats _accessoryStats { get; set; } = new AccessoryStats();
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D(); public override string ItemName => _accessoryStats.Name;
[Node] public Area3D Pickup { get; set; } = default!; public override string Description => _accessoryStats.Description;
public Guid ID => Guid.NewGuid(); public override float SpawnRate => _accessoryStats.SpawnRate;
public string ItemName => _accessoryStats.Name; public override double ThrowDamage => _accessoryStats.ThrowDamage;
public string Description => _accessoryStats.Description; public override float ThrowSpeed => _accessoryStats.ThrowSpeed;
public float SpawnRate => _accessoryStats.SpawnRate;
public Texture2D GetTexture() => _accessoryStats.Texture;
public double ThrowDamage => _accessoryStats.ThrowDamage;
public float ThrowSpeed => _accessoryStats.ThrowSpeed;
public int MaxHPUp => _accessoryStats.MaxHPUp; public int MaxHPUp => _accessoryStats.MaxHPUp;
@@ -45,32 +30,9 @@ public partial class Accessory : Node3D, IEquipableItem
public int DEFUp => _accessoryStats.DEFUp; public int DEFUp => _accessoryStats.DEFUp;
public ImmutableList<AccessoryTag> AccessoryTags => [.. _accessoryStats.AccessoryTags]; public AccessoryTag AccessoryTag => _accessoryStats.AccessoryTag;
public ImmutableList<ItemTag> ItemTags => [.. _accessoryStats.ItemTags]; public override ItemTag ItemTag => _accessoryStats.ItemTag;
public bool IsEquipped { get; set; } public override InventoryItemStats ItemStats { get => _accessoryStats; set => _accessoryStats = (AccessoryStats)value; }
public void OnReady()
{
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _accessoryStats.Texture;
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_accessoryStats = (AccessoryStats)inventoryItemStats;
}
public void Throw()
{
Player.Inventory.Remove(this);
}
public void OnEntered(Node3D body)
{
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
} }

View File

@@ -6,13 +6,12 @@
radius = 0.470016 radius = 0.470016
[node name="Accessory" type="Node3D"] [node name="Accessory" type="Node3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1.75, 0)
script = ExtResource("1_ikyk2") script = ExtResource("1_ikyk2")
[node name="Pickup" type="Area3D" parent="."] [node name="Pickup" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
collision_layer = 0 collision_layer = 4
collision_mask = 4 collision_mask = 0
[node name="Sprite" type="Sprite3D" parent="Pickup"] [node name="Sprite" type="Sprite3D" parent="Pickup"]
unique_name_in_owner = true unique_name_in_owner = true

View File

@@ -1,31 +1,44 @@
using Godot; using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
using System.Text.Json.Serialization;
namespace GameJamDungeon; namespace GameJamDungeon;
[GlobalClass] [GlobalClass]
[Meta, Id("accessory_stat_type")]
public partial class AccessoryStats : InventoryItemStats public partial class AccessoryStats : InventoryItemStats
{ {
[Export] [Export]
[Save("accessory_atk_up")]
public int ATKUp { get; set; } = 0; public int ATKUp { get; set; } = 0;
[Export] [Export]
[Save("accessory_def_up")]
public int DEFUp { get; set; } = 0; public int DEFUp { get; set; } = 0;
[Export] [Export]
[Save("accessory_luck_up")]
public double LuckUp { get; set; } = 0; public double LuckUp { get; set; } = 0;
[Export] [Export]
[Save("accessory_max_hp_up")]
public int MaxHPUp { get; set; } = 0; public int MaxHPUp { get; set; } = 0;
[Export] [Export]
[Save("accessory_max_vt_up")]
public int MaxVTUp { get; set; } = 0; public int MaxVTUp { get; set; } = 0;
[Export] [Export]
public Godot.Collections.Array<AccessoryTag> AccessoryTags { get; set; } = new Godot.Collections.Array<AccessoryTag>(); [Save("accessory_tag")]
public AccessoryTag AccessoryTag { get; set; } = AccessoryTag.None;
} }
public enum AccessoryTag public enum AccessoryTag
{ {
None,
HalfVTConsumption, HalfVTConsumption,
StatusEffectImmunity StatusEffectImmunity
} }
[JsonSerializable(typeof(AccessoryTag))]
public partial class AccessoryTagEnumContext : JsonSerializerContext;

View File

@@ -1,17 +1,22 @@
[gd_resource type="Resource" script_class="AccessoryStats" load_steps=3 format=3 uid="uid://c3v6r8s8yruag"] [gd_resource type="Resource" script_class="AccessoryStats" load_steps=3 format=3 uid="uid://c3v6r8s8yruag"]
[ext_resource type="Script" path="res://src/items/accessory/AccessoryStats.cs" id="1_co7sc"] [ext_resource type="Script" uid="uid://b8arlmivk68b" path="res://src/items/accessory/AccessoryStats.cs" id="1_co7sc"]
[ext_resource type="Texture2D" uid="uid://db7i7iy5gagae" path="res://src/items/accessory/textures/MASK 02.PNG" id="1_uwbei"] [ext_resource type="Texture2D" uid="uid://db7i7iy5gagae" path="res://src/items/accessory/textures/MASK 02.PNG" id="1_uwbei"]
[resource] [resource]
script = ExtResource("1_co7sc") script = ExtResource("1_co7sc")
ATKUp = 0 ATKUp = 0
DEFUp = 0 DEFUp = 0
LUCKUp = 0.0 LuckUp = 0.0
MaxHPUp = 0 MaxHPUp = 0
MaxVTUp = 0 MaxVTUp = 0
AccessoryTags = [1] AccessoryTag = 2
Name = "Mask of the Shunned Goddess" Name = "Mask of the Shunned Goddess"
Description = "Status Effect Immunity" Description = "Status Effect Immunity"
Texture = ExtResource("1_uwbei") Texture = ExtResource("1_uwbei")
SpawnRate = 0.1 SpawnRate = 0.1
ThrowSpeed = 12.0
HealHPAmount = 0
HealVTAmount = 0
ThrowDamage = 5
ItemTags = Array[int]([])

View File

@@ -1,17 +1,22 @@
[gd_resource type="Resource" script_class="AccessoryStats" load_steps=3 format=3 uid="uid://ct8iply3dwssv"] [gd_resource type="Resource" script_class="AccessoryStats" load_steps=3 format=3 uid="uid://ct8iply3dwssv"]
[ext_resource type="Texture2D" uid="uid://db7i7iy5gagae" path="res://src/items/accessory/textures/MASK 02.PNG" id="1_t16cd"] [ext_resource type="Texture2D" uid="uid://db7i7iy5gagae" path="res://src/items/accessory/textures/MASK 02.PNG" id="1_t16cd"]
[ext_resource type="Script" path="res://src/items/accessory/AccessoryStats.cs" id="1_vdb56"] [ext_resource type="Script" uid="uid://b8arlmivk68b" path="res://src/items/accessory/AccessoryStats.cs" id="1_vdb56"]
[resource] [resource]
script = ExtResource("1_vdb56") script = ExtResource("1_vdb56")
ATKUp = 0 ATKUp = 0
DEFUp = 0 DEFUp = 0
LUCKUp = 0.0 LuckUp = 0.0
MaxHPUp = 0 MaxHPUp = 0
MaxVTUp = 0 MaxVTUp = 0
AccessoryTags = [0] AccessoryTag = 1
Name = "Mask of the Goddess of Sloth" Name = "Mask of the Goddess of Sloth"
Description = "Halves VT Depletion Rate" Description = "Halves VT Depletion Rate"
Texture = ExtResource("1_t16cd") Texture = ExtResource("1_t16cd")
SpawnRate = 0.1 SpawnRate = 0.1
ThrowSpeed = 12.0
HealHPAmount = 0
HealVTAmount = 0
ThrowDamage = 5
ItemTags = Array[int]([])

View File

@@ -1,72 +1,28 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using System;
using System.Collections.Immutable;
namespace GameJamDungeon; namespace GameJamDungeon;
[Meta(typeof(IAutoNode))] [Meta, Id("armor")]
public partial class Armor : Node3D, IEquipableItem public partial class Armor : EquipableItem
{ {
public override void _Notification(int what) => this.Notify(what); public override InventoryItemStats ItemStats { get => _armorStats; set => _armorStats = (ArmorStats)value; }
[Dependency] public IPlayer Player => this.DependOn<IPlayer>(); private ArmorStats _armorStats { get; set; } = new ArmorStats();
[Export] private ArmorStats _armorStats { get; set; } = new ArmorStats(); public override string ItemName => _armorStats.Name;
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D(); public override string Description => _armorStats.Description;
[Node] private Area3D Pickup { get; set; } = default!; public override float SpawnRate => _armorStats.SpawnRate;
public Guid ID => Guid.NewGuid(); public override double ThrowDamage => _armorStats.ThrowDamage;
public string ItemName => _armorStats.Name; public override float ThrowSpeed => _armorStats.ThrowSpeed;
public string Description => _armorStats.Description;
public float SpawnRate => _armorStats.SpawnRate;
public Texture2D GetTexture() => _armorStats.Texture;
public double ThrowDamage => _armorStats.ThrowDamage;
public float ThrowSpeed => _armorStats.ThrowSpeed;
public int Defense => _armorStats.Defense; public int Defense => _armorStats.Defense;
public void IncreaseArmorDefense(int bonus) => _armorStats.Defense += bonus; public void IncreaseArmorDefense(int bonus) => _armorStats.Defense += bonus;
public override ItemTag ItemTag => _armorStats.ItemTag;
public ImmutableList<ItemTag> ItemTags => [.. _armorStats.ItemTags];
public bool IsEquipped { get; set; }
public void OnReady()
{
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _armorStats.Texture;
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_armorStats = (ArmorStats)inventoryItemStats;
}
public void Throw()
{
Player.Inventory.Remove(this);
}
public void Drop()
{
Player.Inventory.Remove(this);
}
public void OnEntered(Node3D body)
{
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
} }

View File

@@ -6,13 +6,12 @@
size = Vector3(0.778381, 0.929947, 0.731567) size = Vector3(0.778381, 0.929947, 0.731567)
[node name="Armor" type="Node3D"] [node name="Armor" type="Node3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1.75, 0)
script = ExtResource("1_cmjpq") script = ExtResource("1_cmjpq")
[node name="Pickup" type="Area3D" parent="."] [node name="Pickup" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
collision_layer = 0 collision_layer = 4
collision_mask = 4 collision_mask = 0
[node name="Sprite" type="Sprite3D" parent="Pickup"] [node name="Sprite" type="Sprite3D" parent="Pickup"]
unique_name_in_owner = true unique_name_in_owner = true

View File

@@ -1,25 +1,34 @@
using Godot; using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
[GlobalClass] [GlobalClass]
[Meta, Id("armor_stats")]
public partial class ArmorStats : InventoryItemStats public partial class ArmorStats : InventoryItemStats
{ {
[Export] [Export]
[Save("armor_defense")]
public int Defense { get; set; } = 0; public int Defense { get; set; } = 0;
[Export] [Export]
[Save("armor_telluric_resistance")]
public double TelluricResistance { get; set; } = 0; public double TelluricResistance { get; set; } = 0;
[Export] [Export]
[Save("armor_aeolic_resistance")]
public double AeolicResistance { get; set; } = 0; public double AeolicResistance { get; set; } = 0;
[Export] [Export]
[Save("armor_hydric_resistance")]
public double HydricResistance { get; set; } = 0; public double HydricResistance { get; set; } = 0;
[Export] [Export]
[Save("armor_igneous_resistance")]
public double IgneousResistance { get; set; } = 0; public double IgneousResistance { get; set; } = 0;
[Export] [Export]
[Save("armor_ferrum_resistance")]
public double FerrumResistance { get; set; } = 0; public double FerrumResistance { get; set; } = 0;
} }

View File

@@ -1,6 +1,6 @@
[gd_resource type="Resource" script_class="ArmorStats" load_steps=3 format=3 uid="uid://ce2vfa2t3io67"] [gd_resource type="Resource" script_class="ArmorStats" load_steps=3 format=3 uid="uid://ce2vfa2t3io67"]
[ext_resource type="Script" path="res://src/items/armor/ArmorStats.cs" id="1_6r2bl"] [ext_resource type="Script" uid="uid://dqtp6ewvttoyu" path="res://src/items/armor/ArmorStats.cs" id="1_6r2bl"]
[ext_resource type="Texture2D" uid="uid://ckcn67d64mgke" path="res://src/items/armor/textures/atoners adornment.PNG" id="1_588l8"] [ext_resource type="Texture2D" uid="uid://ckcn67d64mgke" path="res://src/items/armor/textures/atoners adornment.PNG" id="1_588l8"]
[resource] [resource]
@@ -15,3 +15,8 @@ Name = "Atoner's Adornments"
Description = "+1 DEF" Description = "+1 DEF"
Texture = ExtResource("1_588l8") Texture = ExtResource("1_588l8")
SpawnRate = 0.25 SpawnRate = 0.25
ThrowSpeed = 12.0
HealHPAmount = 0
HealVTAmount = 0
ThrowDamage = 5
ItemTag = 0

View File

@@ -1,68 +1,31 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using System;
namespace GameJamDungeon; namespace GameJamDungeon;
[Meta(typeof(IAutoNode))] [Meta, Id("consumable_item")]
public partial class ConsumableItem : Node3D, IUsableItem public partial class ConsumableItem : InventoryItem
{ {
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Export] [Export]
private ConsumableItemStats _consumableItemStats { get; set; } private ConsumableItemStats _consumableItemStats { get; set; } = new ConsumableItemStats();
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D(); public override string ItemName => _consumableItemStats.Name;
[Node] private Area3D Pickup { get; set; } = default!; public override string Description => _consumableItemStats.Description;
public Guid ID => Guid.NewGuid(); public override float SpawnRate => _consumableItemStats.SpawnRate;
public string ItemName => _consumableItemStats.Name; public override double ThrowDamage => _consumableItemStats.ThrowDamage;
public string Description => _consumableItemStats.Description; public override float ThrowSpeed => _consumableItemStats.ThrowSpeed;
public float SpawnRate => _consumableItemStats.SpawnRate; public int HealHPAmount => _consumableItemStats.HealHPAmount;
public Texture2D GetTexture() => _consumableItemStats.Texture; public int HealVTAmount => _consumableItemStats.HealVTAmount;
public double ThrowDamage => _consumableItemStats.ThrowDamage; public int RaiseHPAmount => _consumableItemStats.RaiseHPAmount;
public float ThrowSpeed => _consumableItemStats.ThrowSpeed; public int RaiseVTAmount => _consumableItemStats.RaiseVTAmount;
public void Use() public override InventoryItemStats ItemStats { get => _consumableItemStats; set => _consumableItemStats = (ConsumableItemStats)value; }
{
if (Player.Stats.CurrentHP == Player.Stats.MaximumHP && _consumableItemStats.RaiseHPAmount > 0)
Player.RaiseHP(_consumableItemStats.RaiseHPAmount);
if (Player.Stats.CurrentVT == Player.Stats.MaximumVT && _consumableItemStats.RaiseVTAmount > 0)
Player.RaiseVT(_consumableItemStats.RaiseVTAmount);
if (_consumableItemStats.HealHPAmount > 0 && Player.Stats.CurrentHP != Player.Stats.MaximumHP)
Player.HealHP(_consumableItemStats.HealHPAmount);
if (_consumableItemStats.HealVTAmount > 0 && Player.Stats.CurrentVT != Player.Stats.MaximumVT)
Player.HealVT(_consumableItemStats.HealVTAmount);
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_consumableItemStats = (ConsumableItemStats)inventoryItemStats;
}
public void OnReady()
{
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _consumableItemStats.Texture;
}
public void OnEntered(Node3D body)
{
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
} }

View File

@@ -10,8 +10,8 @@ script = ExtResource("1_26bad")
[node name="Pickup" type="Area3D" parent="."] [node name="Pickup" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
collision_layer = 0 collision_layer = 4
collision_mask = 4 collision_mask = 0
[node name="Sprite" type="Sprite3D" parent="Pickup"] [node name="Sprite" type="Sprite3D" parent="Pickup"]
unique_name_in_owner = true unique_name_in_owner = true

View File

@@ -1,13 +1,18 @@
using Godot; using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
[GlobalClass] [GlobalClass]
[Meta, Id("consumable_item_stats")]
public partial class ConsumableItemStats : InventoryItemStats public partial class ConsumableItemStats : InventoryItemStats
{ {
[Export] [Export]
[Save("consumable_item_raise_hp")]
public int RaiseHPAmount { get; set; } = 0; public int RaiseHPAmount { get; set; } = 0;
[Export] [Export]
[Save("consumable_item_raise_vt")]
public int RaiseVTAmount { get; set; } = 0; public int RaiseVTAmount { get; set; } = 0;
} }

View File

@@ -21,13 +21,13 @@ public partial class DroppedItem : RigidBody3D, IDroppedItem
[Node] private Sprite2D Sprite { get; set; } = default!; [Node] private Sprite2D Sprite { get; set; } = default!;
public IInventoryItem Item { get; set; } public InventoryItem Item { get; set; }
public void OnResolved() public void OnResolved()
{ {
ContactMonitor = true; ContactMonitor = true;
BodyEntered += DroppedItem_BodyEntered; BodyEntered += DroppedItem_BodyEntered;
Sprite.Texture = Item.GetTexture(); Sprite.Texture = Item.ItemStats.Texture;
} }
public async void Drop() public async void Drop()

View File

@@ -1,221 +1,27 @@
using Chickensoft.AutoInject; using Chickensoft.Introspection;
using Chickensoft.Introspection;
using Godot; using Godot;
using System;
using System.Linq;
namespace GameJamDungeon; namespace GameJamDungeon;
[Meta(typeof(IAutoNode))] [Meta, Id("effect_item")]
public partial class EffectItem : Node3D, IUsableItem public partial class EffectItem : InventoryItem
{ {
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Export] [Export]
private EffectItemStats _effectItemStats { get; set; } private EffectItemStats _effectItemStats { get; set; } = new EffectItemStats();
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D(); public override string ItemName => _effectItemStats.Name;
[Node] private Area3D Pickup { get; set; } = default!; public override string Description => _effectItemStats.Description;
public Guid ID => Guid.NewGuid(); public override float SpawnRate => _effectItemStats.SpawnRate;
public string ItemName => _effectItemStats.Name; public override double ThrowDamage => _effectItemStats.ThrowDamage;
public string Description => _effectItemStats.Description; public override float ThrowSpeed => _effectItemStats.ThrowSpeed;
public float SpawnRate => _effectItemStats.SpawnRate; public UsableItemTag UsableItemTag => _effectItemStats.UsableItemTag;
public Texture2D GetTexture() => _effectItemStats.Texture; public void SetEffectTag(UsableItemTag effect) => _effectItemStats.UsableItemTag = effect;
public double ThrowDamage => _effectItemStats.ThrowDamage; public override InventoryItemStats ItemStats { get => _effectItemStats; set => _effectItemStats = (EffectItemStats)value; }
public float ThrowSpeed => _effectItemStats.ThrowSpeed;
public void Use()
{
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.DoubleEXP))
Game.DoubleEXP(TimeSpan.FromSeconds(30));
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.TeleportAllEnemiesToRoom))
TeleportEnemiesToCurrentRoom();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.KillHalfEnemiesInRoom))
KillHalfEnemiesInRoom();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.TurnAllEnemiesIntoHealingItem))
TurnAllEnemiesInRoomIntoHealingItem();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.HealsAllInRoomToMaxHP))
HealAllEnemiesAndPlayerInRoomToFull();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.AbsorbHPFromAllEnemiesInRoom))
AbsorbHPFromAllEnemiesInRoom();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.DealElementalDamageToAllEnemiesInRoom))
DealElementalDamageToAllEnemiesInRoom(ElementType.Hydric);
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.SwapHPAndVT))
SwapHPandVT();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.RaiseCurrentWeaponAttack))
RaiseCurrentWeaponAttack();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.RaiseCurrentDefenseArmor))
RaiseCurrentArmorDefense();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.RaiseLevel))
RaiseLevel();
if (_effectItemStats.UsableItemTags.Contains(UsableItemTag.RandomEffect))
RandomEffect();
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_effectItemStats = (EffectItemStats)inventoryItemStats;
}
public void OnReady()
{
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _effectItemStats.Texture;
}
public void OnEntered(Node3D body)
{
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
private void TeleportEnemiesToCurrentRoom()
{
var currentFloor = Game.CurrentFloor;
var rooms = currentFloor.Rooms;
var currentRoom = Player.GetCurrentRoom();
if (currentRoom is not MonsterRoom)
return;
var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom monsterRoom)
validRooms.Remove(monsterRoom);
var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom());
foreach (var enemy in enemyList)
{
var spawnPoints = currentMonsterRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyGlobalPosition(randomSpawnPoint.GlobalPosition);
}
}
private void KillHalfEnemiesInRoom()
{
var currentRoom = Player.GetCurrentRoom();
if (currentRoom is not MonsterRoom)
return;
var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().ToList();
var enemiesToKill = enemyList.Count / 2;
for (var i = 0; i < enemiesToKill; i++)
enemyList[i].Die();
}
private void TurnAllEnemiesInRoomIntoHealingItem()
{
var currentRoom = Player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies)
{
enemy.Die();
DropHealingItem(enemy.GetEnemyGlobalPosition());
}
}
private void DropHealingItem(Vector3 vector)
{
var consumableFolder = "res://src/items/consumable";
var restorativeScene = GD.Load<PackedScene>($"{consumableFolder}/ConsumableItem.tscn");
var consumable = restorativeScene.Instantiate<ConsumableItem>();
var resourceFiles = DirAccess.GetFilesAt($"{consumableFolder}/resources");
var rng = new RandomNumberGenerator();
rng.Randomize();
var randomResource = resourceFiles[rng.RandiRange(0, resourceFiles.Length - 1)];
var randomFile = ResourceLoader.Load<ConsumableItemStats>($"{consumableFolder}/resources/{randomResource}");
consumable.SetItemStats(randomFile);
Game.AddChild(consumable);
consumable.GlobalPosition = vector;
}
private void HealAllEnemiesAndPlayerInRoomToFull()
{
var currentRoom = Player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies)
enemy.SetCurrentHP(enemy.GetMaximumHP());
Player.Stats.SetCurrentHP(Player.Stats.MaximumHP.Value);
}
private void AbsorbHPFromAllEnemiesInRoom()
{
var currentRoom = Player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
var hpToAbsorb = 0.0;
foreach (var enemy in currentEnemies)
hpToAbsorb += enemy.CurrentHP * 0.05;
Player.Stats.SetCurrentHP(Player.Stats.CurrentHP.Value + (int)hpToAbsorb);
GD.Print("HP to absorb: " + hpToAbsorb);
}
private void DealElementalDamageToAllEnemiesInRoom(ElementType elementType)
{
var currentRoom = Player.GetCurrentRoom();
var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies)
enemy.TakeDamage(20, elementType);
}
private void SwapHPandVT()
{
var oldHp = Player.Stats.CurrentHP.Value;
var oldVt = Player.Stats.CurrentVT.Value;
Player.Stats.SetCurrentHP(oldVt);
Player.Stats.SetCurrentVT(oldHp);
}
private void RandomEffect()
{
var itemEffects = Enum.GetValues<UsableItemTag>().ToList();
itemEffects.Remove(UsableItemTag.RandomEffect);
var randomEffect = new Godot.Collections.Array<UsableItemTag>(itemEffects).PickRandom();
_effectItemStats.UsableItemTags.Clear();
_effectItemStats.UsableItemTags.Add(randomEffect);
Use();
}
private void RaiseCurrentWeaponAttack()
{
if (Player.EquippedWeapon.Value.ItemName == string.Empty)
return;
var currentWeapon = Player.EquippedWeapon.Value;
currentWeapon.IncreaseWeaponAttack(1);
}
private void RaiseCurrentArmorDefense()
{
if (Player.EquippedArmor.Value.ItemName == string.Empty)
return;
var currentArmor = Player.EquippedArmor.Value;
currentArmor.IncreaseArmorDefense(1);
}
private void RaiseLevel()
{
Player.LevelUp();
}
} }

View File

@@ -10,8 +10,8 @@ script = ExtResource("1_yw2rj")
[node name="Pickup" type="Area3D" parent="."] [node name="Pickup" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
collision_layer = 0 collision_layer = 4
collision_mask = 4 collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Pickup"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Pickup"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0600509, 0.26725, 0.180481) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0600509, 0.26725, 0.180481)

View File

@@ -1,13 +1,18 @@
using Godot; using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
[GlobalClass] [GlobalClass]
[Meta, Id("effect_item_stats")]
public partial class EffectItemStats : InventoryItemStats public partial class EffectItemStats : InventoryItemStats
{ {
[Export] [Export]
public Godot.Collections.Array<UsableItemTag> UsableItemTags { get; set; } = new Godot.Collections.Array<UsableItemTag>(); [Save("effect_item_tag")]
public UsableItemTag UsableItemTag { get; set; } = UsableItemTag.None;
[Export] [Export]
[Save("effect_item_element")]
public ElementType ElementalDamageType { get; set; } public ElementType ElementalDamageType { get; set; }
} }

View File

@@ -23,7 +23,7 @@ texture = ExtResource("1_1rwq6")
[node name="Pickup" type="Area3D" parent="."] [node name="Pickup" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
collision_layer = 4 collision_layer = 4
collision_mask = 4 collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Pickup"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Pickup"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.348749, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.348749, 0)

View File

@@ -1,146 +1,37 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using System;
using System.Collections.Immutable;
using System.Linq;
namespace GameJamDungeon; namespace GameJamDungeon;
[Meta(typeof(IAutoNode))] [Meta, Id("throwable_item")]
public partial class ThrowableItem : Node3D, IUsableItem public partial class ThrowableItem : InventoryItem
{ {
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
public Guid ID => Guid.NewGuid();
public string ItemName => _throwableItemStats.Name;
public string Description => _throwableItemStats.Description;
public float SpawnRate => _throwableItemStats.SpawnRate;
public Texture2D GetTexture() => _throwableItemStats.Texture;
public double ThrowDamage => _throwableItemStats.ThrowDamage;
public float ThrowSpeed => _throwableItemStats.ThrowSpeed;
public ElementType ElementType => _throwableItemStats.ElementType;
public ImmutableList<ThrowableItemTag> ThrowableItemTags => [.. _throwableItemStats.ThrowableItemTags];
public int Count { get; }
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_throwableItemStats = (ThrowableItemStats)inventoryItemStats;
}
public void OnEntered(Node3D body)
{
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
[Export] [Export]
private ThrowableItemStats _throwableItemStats { get; set; } private ThrowableItemStats _throwableItemStats { get; set; }
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D(); public override string ItemName => _throwableItemStats.Name;
[Node] private Area3D Pickup { get; set; } = default!; public override string Description => _throwableItemStats.Description;
public void OnResolved() public override float SpawnRate => _throwableItemStats.SpawnRate;
{
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _throwableItemStats.Texture;
}
public void Use() public override double ThrowDamage => _throwableItemStats.ThrowDamage;
{
if (_throwableItemStats.HealHPAmount > 0)
Player.HealHP(_throwableItemStats.HealHPAmount);
if (_throwableItemStats.HealVTAmount > 0)
Player.HealVT(_throwableItemStats.HealVTAmount);
if (_throwableItemStats.ThrowableItemTags.Contains(ThrowableItemTag.TeleportToRandomLocation)) public override float ThrowSpeed => _throwableItemStats.ThrowSpeed;
TeleportToRandomRoom(Player);
if (_throwableItemStats.ThrowableItemTags.Contains(ThrowableItemTag.CanChangeAffinity)) public ElementType ElementType => _throwableItemStats.ElementType;
ChangeAffinity();
if (_throwableItemStats.ThrowableItemTags.Contains(ThrowableItemTag.WarpToExitIfFound)) public ThrowableItemTag ThrowableItemTag => _throwableItemStats.ThrowableItemTag;
WarpToExit(Player);
}
public void TeleportToRandomRoom(IEnemy enemy) public int HealHPAmount => _throwableItemStats.HealHPAmount;
{
var currentFloor = Game.CurrentFloor;
var rooms = currentFloor.Rooms;
var currentRoom = enemy.GetCurrentRoom();
var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom currentMonsterRoom)
validRooms.Remove(currentMonsterRoom);
if (validRooms.Count == 0) public int HealVTAmount => _throwableItemStats.HealVTAmount;
return;
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms); public void SetElementType(ElementType elementType) => _throwableItemStats.ElementType = elementType;
var randomRoom = roomsGodotCollection.PickRandom();
var spawnPoints = randomRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyGlobalPosition(randomSpawnPoint.GlobalPosition); public void SetDescription(string description) => _throwableItemStats.Description = description;
}
private void TeleportToRandomRoom(IPlayer player) public int Count { get; }
{
var currentFloor = Game.CurrentFloor;
var rooms = currentFloor.Rooms;
var currentRoom = rooms.SingleOrDefault(x => x.IsPlayerInRoom); public override InventoryItemStats ItemStats { get => _throwableItemStats; set => _throwableItemStats = (ThrowableItemStats)value; }
var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom currentMonsterRoom)
validRooms.Remove(currentMonsterRoom);
if (validRooms.Count == 0)
return;
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms);
var randomRoom = roomsGodotCollection.PickRandom();
var spawnPoint = randomRoom.PlayerSpawn;
Game.ToggleInventory();
player.TeleportPlayer(spawnPoint.GlobalPosition);
}
private void ChangeAffinity()
{
var maximumElements = Enum.GetNames(typeof(ElementType)).Length;
_throwableItemStats.ElementType = _throwableItemStats.ElementType + 1 % maximumElements;
// TODO: Make this an inventory animation to cycle through elements.
_throwableItemStats.Description =
$"Inflicts {_throwableItemStats.ElementType} damage when thrown." +
$"{System.Environment.NewLine}Use item to change Affinity.";
}
private void WarpToExit(IPlayer player)
{
var exitRoom = Game.CurrentFloor.Rooms.OfType<ExitRoom>().Single();
if (exitRoom.PlayerDiscoveredRoom)
player.TeleportPlayer(exitRoom.PlayerSpawn.GlobalPosition);
}
public void WarpToExit(IEnemy enemy)
{
var exitRoom = Game.CurrentFloor.Rooms.OfType<ExitRoom>().Single();
enemy.SetEnemyGlobalPosition(exitRoom.PlayerSpawn.GlobalPosition);
}
} }

View File

@@ -10,8 +10,8 @@ script = ExtResource("1_nac2l")
[node name="Pickup" type="Area3D" parent="."] [node name="Pickup" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
collision_layer = 0 collision_layer = 4
collision_mask = 4 collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Pickup"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Pickup"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0600509, 0.26725, 0.180481) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0600509, 0.26725, 0.180481)

View File

@@ -1,16 +1,22 @@
using Godot; using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
[GlobalClass] [GlobalClass]
[Meta, Id("throwable_item_stats")]
public partial class ThrowableItemStats : InventoryItemStats public partial class ThrowableItemStats : InventoryItemStats
{ {
[Export] [Export]
public Godot.Collections.Array<ThrowableItemTag> ThrowableItemTags { get; set; } = new Godot.Collections.Array<ThrowableItemTag>(); [Save("throwable_item_tag")]
public ThrowableItemTag ThrowableItemTag { get; set; } = ThrowableItemTag.None;
[Export] [Export]
[Save("throwable_item_element")]
public ElementType ElementType { get; set; } = ElementType.None; public ElementType ElementType { get; set; } = ElementType.None;
[Export] [Export]
public Godot.Collections.Array<UsableItemTag> UsableItemTags { get; set; } = new Godot.Collections.Array<UsableItemTag>(); [Save("throwable_item_usable_tag")]
public UsableItemTag UsableItemTag { get; set; } = UsableItemTag.None;
} }

View File

@@ -1,15 +1,22 @@
namespace GameJamDungeon; using System.Text.Json.Serialization;
namespace GameJamDungeon;
public enum ThrowableItemTag public enum ThrowableItemTag
{ {
None,
LowerTargetTo1HP, LowerTargetTo1HP,
CanChangeAffinity, CanChangeAffinity,
TeleportToRandomLocation, TeleportToRandomLocation,
WarpToExitIfFound WarpToExitIfFound
} }
[JsonSerializable(typeof(ThrowableItemTag))]
public partial class ThrowableItemTagEnumContext : JsonSerializerContext;
public enum UsableItemTag public enum UsableItemTag
{ {
None,
DoubleEXP, DoubleEXP,
IdentifyAllItemsCostHP, IdentifyAllItemsCostHP,
BriefImmunity, BriefImmunity,
@@ -26,14 +33,13 @@ public enum UsableItemTag
RandomEffect, RandomEffect,
} }
public enum EnhancingItemTag [JsonSerializable(typeof(UsableItemTag))]
{ public partial class UsableItemTagEnumContext : JsonSerializerContext;
Add1ATK,
Add1DEF,
RaiseLevelBy1,
}
public enum BoxItemTag public enum BoxItemTag
{ {
RandomNewItem, RandomNewItem,
} }
[JsonSerializable(typeof(BoxItemTag))]
public partial class BoxItemTagEnumContext : JsonSerializerContext;

View File

@@ -1,7 +1,7 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using GameJamDungeon.src.items;
using Godot; using Godot;
using System.Linq;
namespace GameJamDungeon; namespace GameJamDungeon;
@@ -14,15 +14,16 @@ public partial class ThrownItem : RigidBody3D
[Dependency] public IGame Game => this.DependOn<IGame>(); [Dependency] public IGame Game => this.DependOn<IGame>();
public IInventoryItem ItemThatIsThrown; public InventoryItem ItemThatIsThrown;
private EffectService _effectService;
[Node] public Sprite2D Sprite { get; set; } = default!; [Node] public Sprite2D Sprite { get; set; } = default!;
public void OnResolved() public void OnResolved()
{ {
BodyEntered += ThrownItem_BodyEntered;
GlobalPosition = Player.CurrentPosition; GlobalPosition = Player.CurrentPosition;
Sprite.Texture = ItemThatIsThrown.GetTexture(); Sprite.Texture = ItemThatIsThrown.ItemStats.Texture;
AddCollisionExceptionWith((Node)Player); AddCollisionExceptionWith((Node)Player);
} }
@@ -33,8 +34,9 @@ public partial class ThrownItem : RigidBody3D
QueueFree(); QueueFree();
} }
public void Throw() public void Throw(EffectService effectService)
{ {
_effectService = effectService;
ApplyCentralImpulse(-Player.CurrentBasis.Z.Normalized() * ItemThatIsThrown.ThrowSpeed); ApplyCentralImpulse(-Player.CurrentBasis.Z.Normalized() * ItemThatIsThrown.ThrowSpeed);
} }
@@ -79,16 +81,16 @@ public partial class ThrownItem : RigidBody3D
{ {
if (ItemThatIsThrown is ThrowableItem throwableItem) if (ItemThatIsThrown is ThrowableItem throwableItem)
{ {
switch (throwableItem.ThrowableItemTags.Single()) switch (throwableItem.ThrowableItemTag)
{ {
case ThrowableItemTag.LowerTargetTo1HP: case ThrowableItemTag.LowerTargetTo1HP:
enemy.TakeDamage(enemy.CurrentHP - 1, ignoreDefense: true, ignoreElementalResistance: true); enemy.TakeDamage(enemy.CurrentHP - 1, ignoreDefense: true, ignoreElementalResistance: true);
break; break;
case ThrowableItemTag.TeleportToRandomLocation: case ThrowableItemTag.TeleportToRandomLocation:
throwableItem.TeleportToRandomRoom(enemy); _effectService.TeleportToRandomRoom(enemy);
break; break;
case ThrowableItemTag.WarpToExitIfFound: case ThrowableItemTag.WarpToExitIfFound:
throwableItem.WarpToExit(enemy); _effectService.WarpToExit(enemy);
break; break;
default: default:
enemy.TakeDamage(throwableItem.ThrowDamage, throwableItem.ElementType); enemy.TakeDamage(throwableItem.ThrowDamage, throwableItem.ElementType);

View File

@@ -1,51 +1,38 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot; using Godot;
using System;
using System.Collections.Immutable;
namespace GameJamDungeon; namespace GameJamDungeon;
[Meta(typeof(IAutoNode))] [Meta, Id("weapon")]
public partial class Weapon : Node3D, IInventoryItem, IEquipableItem public partial class Weapon : EquipableItem
{ {
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Signal] [Signal]
public delegate void EquippedItemEventHandler(Weapon equippedWeapon); public delegate void EquippedItemEventHandler(Weapon equippedWeapon);
[Export] [Export]
private WeaponStats _weaponStats { get; set; } = new WeaponStats(); private WeaponStats _weaponStats { get; set; } = new WeaponStats();
[Node] public Sprite3D Sprite { get; set; } = new Sprite3D(); public override string ItemName => _weaponStats.Name;
[Node] public Area3D Pickup { get; set; } = default!; public override string Description => _weaponStats.Description;
public Texture2D GetTexture() => _weaponStats.Texture; public override float SpawnRate => _weaponStats.SpawnRate;
public Guid ID => Guid.NewGuid();
public string ItemName => _weaponStats.Name;
public string Description => _weaponStats.Description;
public float SpawnRate => _weaponStats.SpawnRate;
public int Damage => _weaponStats.Damage; public int Damage => _weaponStats.Damage;
public double ThrowDamage => _weaponStats.ThrowDamage; public override double ThrowDamage => _weaponStats.ThrowDamage;
public float ThrowSpeed => _weaponStats.ThrowSpeed; public override float ThrowSpeed => _weaponStats.ThrowSpeed;
public double Luck => _weaponStats.Luck; public double Luck => _weaponStats.Luck;
public double AttackSpeed => _weaponStats.AttackSpeed; public double AttackSpeed => _weaponStats.AttackSpeed;
public ImmutableList<WeaponTag> WeaponTags => [.. _weaponStats.WeaponTags]; public WeaponTag WeaponTag => _weaponStats.WeaponTag;
public ImmutableList<ItemTag> ItemTags => [.. _weaponStats.ItemTags]; public override ItemTag ItemTag => _weaponStats.ItemTag;
public ElementType WeaponElement => _weaponStats.WeaponElement; public ElementType WeaponElement => _weaponStats.WeaponElement;
@@ -53,38 +40,5 @@ public partial class Weapon : Node3D, IInventoryItem, IEquipableItem
public void IncreaseWeaponAttack(int bonus) => _weaponStats.Damage += bonus; public void IncreaseWeaponAttack(int bonus) => _weaponStats.Damage += bonus;
public bool IsEquipped { get; set; } public override InventoryItemStats ItemStats { get => _weaponStats; set => _weaponStats = (WeaponStats)value; }
public void OnReady()
{
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _weaponStats.Texture;
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_weaponStats = (WeaponStats)inventoryItemStats;
if (inventoryItemStats.Texture != null)
{
var texture = ResourceLoader.Load(inventoryItemStats.Texture.ResourcePath) as Texture2D;
Sprite.Texture = texture;
}
}
public void Throw()
{
Player.Inventory.Remove(this);
}
public void Drop()
{
Player.Inventory.Remove(this);
}
public void OnEntered(Node3D body)
{
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
} }

View File

@@ -12,7 +12,7 @@ script = ExtResource("1_7pkyf")
[node name="Pickup" type="Area3D" parent="."] [node name="Pickup" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
collision_layer = 4 collision_layer = 4
collision_mask = 4 collision_mask = 0
[node name="Sprite" type="Sprite3D" parent="Pickup"] [node name="Sprite" type="Sprite3D" parent="Pickup"]
unique_name_in_owner = true unique_name_in_owner = true

View File

@@ -1,25 +1,34 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot; using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
[GlobalClass] [GlobalClass]
[Meta, Id("weapon_stat_type")]
public partial class WeaponStats : InventoryItemStats public partial class WeaponStats : InventoryItemStats
{ {
[Export] [Export]
[Save("weapon_damage")]
public int Damage { get; set; } = 0; public int Damage { get; set; } = 0;
[Export] [Export]
[Save("weapon_luck")]
public double Luck { get; set; } = 0.05; public double Luck { get; set; } = 0.05;
[Export] [Export]
[Save("weapon_atk_speed")]
public double AttackSpeed { get; set; } = 1; public double AttackSpeed { get; set; } = 1;
[Export] [Export]
[Save("weapon_element")]
public ElementType WeaponElement { get; set; } = ElementType.None; public ElementType WeaponElement { get; set; } = ElementType.None;
[Export] [Export]
[Save("weapon_elemental_damage_bonus")]
public double ElementalDamageBonus { get; set; } = 1.0; public double ElementalDamageBonus { get; set; } = 1.0;
[Export] [Export]
public Godot.Collections.Array<WeaponTag> WeaponTags { get; set; } = new Godot.Collections.Array<WeaponTag>(); [Save("weapon_tag")]
public WeaponTag WeaponTag { get; set; } = WeaponTag.None;
} }

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=62 format=4 uid="uid://dl6h1djc27ddl"] [gd_scene load_steps=60 format=4 uid="uid://dl6h1djc27ddl"]
[ext_resource type="Script" uid="uid://c1nhqlem1ew3m" path="res://src/map/dungeon/code/Floor0.cs" id="1_db2o3"] [ext_resource type="Script" uid="uid://c1nhqlem1ew3m" path="res://src/map/dungeon/code/Floor0.cs" id="1_db2o3"]
[ext_resource type="Texture2D" uid="uid://b27ksiyfefb33" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_outside_desert.png" id="2_xh2ej"] [ext_resource type="Texture2D" uid="uid://b27ksiyfefb33" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_outside_desert.png" id="2_xh2ej"]
@@ -15,8 +15,6 @@
[ext_resource type="Texture2D" uid="uid://dyufabjcwlago" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_HAND_CYCLE_MOTIF.png" id="13_1i307"] [ext_resource type="Texture2D" uid="uid://dyufabjcwlago" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_HAND_CYCLE_MOTIF.png" id="13_1i307"]
[ext_resource type="Texture2D" uid="uid://4k6vtn4oip5f" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_TILE4.png" id="14_qqc7i"] [ext_resource type="Texture2D" uid="uid://4k6vtn4oip5f" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_TILE4.png" id="14_qqc7i"]
[ext_resource type="Texture2D" uid="uid://cururtxtgylxf" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_COLUMN.jpg" id="15_ojbcg"] [ext_resource type="Texture2D" uid="uid://cururtxtgylxf" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_COLUMN.jpg" id="15_ojbcg"]
[ext_resource type="PackedScene" uid="uid://d0pl1n1jf77jm" path="res://src/items/effect/EffectItem.tscn" id="16_aqomv"]
[ext_resource type="Resource" uid="uid://c6ecr2cquav3" path="res://src/items/effect/resources/EntropicSeal.tres" id="17_db2o3"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3ubi4"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3ubi4"]
shading_mode = 0 shading_mode = 0
@@ -887,7 +885,3 @@ collision_mask = 8
[node name="CollisionShape3D" type="CollisionShape3D" parent="Room/Room"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Room/Room"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 149.05, -4.02862, -1.76389) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 149.05, -4.02862, -1.76389)
shape = SubResource("BoxShape3D_ntxe5") shape = SubResource("BoxShape3D_ntxe5")
[node name="EffectItem" parent="." instance=ExtResource("16_aqomv")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.25212, -2.42406, 0)
_effectItemStats = ExtResource("17_db2o3")

View File

@@ -5,7 +5,7 @@ using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
public interface IPlayer : IKillable, IProvide<ISaveChunk<PlayerStats>> public interface IPlayer : IKillable, IProvide<ISaveChunk<PlayerData>>
{ {
public void Attack(); public void Attack();
@@ -47,7 +47,7 @@ public interface IPlayer : IKillable, IProvide<ISaveChunk<PlayerStats>>
public void ModifyBonusLuck(double amount); public void ModifyBonusLuck(double amount);
public IInventory Inventory { get; } public Inventory Inventory { get; }
public PlayerStatController Stats { get; } public PlayerStatController Stats { get; }
@@ -61,7 +61,7 @@ public interface IPlayer : IKillable, IProvide<ISaveChunk<PlayerStats>>
public IAutoProp<Accessory> EquippedAccessory { get; } public IAutoProp<Accessory> EquippedAccessory { get; }
public void Equip(IEquipableItem equipable); public void Equip(EquipableItem equipable);
public void Unequip(IEquipableItem equipable); public void Unequip(EquipableItem equipable);
} }

View File

@@ -20,7 +20,7 @@ public partial class Player : CharacterBody3D, IPlayer
#endregion #endregion
#region Save #region Save
public ISaveChunk<PlayerStats> PlayerChunk { get; set; } = default!; public ISaveChunk<PlayerData> PlayerChunk { get; set; } = default!;
#endregion #endregion
public double CurrentHP => Stats.CurrentHP.Value; public double CurrentHP => Stats.CurrentHP.Value;
@@ -30,7 +30,7 @@ public partial class Player : CharacterBody3D, IPlayer
public Basis CurrentBasis => Transform.Basis; public Basis CurrentBasis => Transform.Basis;
public PlayerStatController Stats { get; set; } = default!; public PlayerStatController Stats { get; set; } = default!;
public IInventory Inventory { get; private set; } = default!; public Inventory Inventory { get; private set; } = default!;
public IAutoProp<Weapon> EquippedWeapon => _equippedWeapon; public IAutoProp<Weapon> EquippedWeapon => _equippedWeapon;
private AutoProp<Weapon> _equippedWeapon { get; set; } = new AutoProp<Weapon>(new Weapon()); private AutoProp<Weapon> _equippedWeapon { get; set; } = new AutoProp<Weapon>(new Weapon());
@@ -53,7 +53,7 @@ public partial class Player : CharacterBody3D, IPlayer
[Dependency] [Dependency]
public ISaveChunk<GameData> GameChunk => this.DependOn<ISaveChunk<GameData>>(); public ISaveChunk<GameData> GameChunk => this.DependOn<ISaveChunk<GameData>>();
ISaveChunk<PlayerStats> IProvide<ISaveChunk<PlayerStats>>.Value() => PlayerChunk; ISaveChunk<PlayerData> IProvide<ISaveChunk<PlayerData>>.Value() => PlayerChunk;
#endregion #endregion
#region Event Signals #region Event Signals
@@ -82,6 +82,8 @@ public partial class Player : CharacterBody3D, IPlayer
[Node] private IHitbox Hitbox { get; set; } = default!; [Node] private IHitbox Hitbox { get; set; } = default!;
[Node] private Area3D CollisionDetector { get; set; } = default!;
[Node] private Timer HealthTimer { get; set; } = default!; [Node] private Timer HealthTimer { get; set; } = default!;
#endregion #endregion
@@ -144,9 +146,9 @@ public partial class Player : CharacterBody3D, IPlayer
PlayerLogic.Set(Stats); PlayerLogic.Set(Stats);
var defaultWeapon = new Weapon(); var defaultWeapon = new Weapon();
defaultWeapon.SetItemStats(_defaultWeapon); defaultWeapon.ItemStats = _defaultWeapon;
var defaultArmor = new Armor(); var defaultArmor = new Armor();
defaultArmor.SetItemStats(_defaultArmor); defaultArmor.ItemStats = _defaultArmor;
Inventory.TryAdd(defaultWeapon); Inventory.TryAdd(defaultWeapon);
Inventory.TryAdd(defaultArmor); Inventory.TryAdd(defaultArmor);
@@ -164,27 +166,32 @@ public partial class Player : CharacterBody3D, IPlayer
public void OnResolved() public void OnResolved()
{ {
PlayerChunk = new SaveChunk<PlayerStats>( PlayerChunk = new SaveChunk<PlayerData>(
onSave: (chunk) => new PlayerStats onSave: (chunk) => new PlayerData()
{ {
CurrentHP = Stats.CurrentHP.Value, PlayerStats = new PlayerStats()
MaximumHP = Stats.MaximumHP.Value, {
CurrentVT = Stats.CurrentVT.Value, CurrentHP = Stats.CurrentHP.Value,
MaximumVT = Stats.MaximumVT.Value, MaximumHP = Stats.MaximumHP.Value,
CurrentAttack = Stats.CurrentAttack.Value, CurrentVT = Stats.CurrentVT.Value,
BonusAttack = Stats.BonusAttack.Value, MaximumVT = Stats.MaximumVT.Value,
MaxAttack = Stats.MaxAttack.Value, CurrentAttack = Stats.CurrentAttack.Value,
CurrentDefense = Stats.CurrentDefense.Value, BonusAttack = Stats.BonusAttack.Value,
BonusDefense = Stats.BonusDefense.Value, MaxAttack = Stats.MaxAttack.Value,
MaxDefense = Stats.MaxDefense.Value, CurrentDefense = Stats.CurrentDefense.Value,
CurrentExp = Stats.CurrentExp.Value, BonusDefense = Stats.BonusDefense.Value,
CurrentLevel = Stats.CurrentLevel.Value, MaxDefense = Stats.MaxDefense.Value,
ExpToNextLevel = Stats.ExpToNextLevel.Value, CurrentExp = Stats.CurrentExp.Value,
Luck = Stats.Luck.Value CurrentLevel = Stats.CurrentLevel.Value,
ExpToNextLevel = Stats.ExpToNextLevel.Value,
Luck = Stats.Luck.Value
},
Inventory = Inventory
}, },
onLoad: (chunk, data) => onLoad: (chunk, data) =>
{ {
Stats.Init(data); Stats.Init(data.PlayerStats);
Inventory = data.Inventory;
} }
); );
@@ -208,6 +215,17 @@ public partial class Player : CharacterBody3D, IPlayer
this.Provide(); this.Provide();
HealthTimer.Timeout += OnHealthTimerTimeout; HealthTimer.Timeout += OnHealthTimerTimeout;
Hitbox.AreaEntered += Hitbox_AreaEntered; Hitbox.AreaEntered += Hitbox_AreaEntered;
CollisionDetector.BodyEntered += CollisionDetector_BodyEntered;
}
private void CollisionDetector_BodyEntered(Node3D body)
{
if (body is InventoryItem inventoryItem)
{
var isAdded = Inventory.TryAdd(inventoryItem);
if (isAdded)
QueueFree();
}
} }
public void OnReady() public void OnReady()
@@ -368,6 +386,12 @@ public partial class Player : CharacterBody3D, IPlayer
if (@event.IsActionPressed(GameInputs.Attack)) if (@event.IsActionPressed(GameInputs.Attack))
Attack(); Attack();
if (@event.IsActionPressed(GameInputs.Save))
Game.Save();
if (@event.IsActionPressed(GameInputs.Load))
Game.Load();
} }
public void OnPhysicsProcess(double delta) public void OnPhysicsProcess(double delta)
@@ -376,7 +400,7 @@ public partial class Player : CharacterBody3D, IPlayer
PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform)); PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform));
} }
public void Equip(IEquipableItem equipable) public void Equip(EquipableItem equipable)
{ {
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
@@ -400,7 +424,7 @@ public partial class Player : CharacterBody3D, IPlayer
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
} }
public void Unequip(IEquipableItem equipable) public void Unequip(EquipableItem equipable)
{ {
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
@@ -427,7 +451,7 @@ public partial class Player : CharacterBody3D, IPlayer
else else
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
if (equipable.ItemTags.Contains(ItemTag.BreaksOnChange)) if (equipable.ItemTag == ItemTag.BreaksOnChange)
Inventory.Remove(equipable); Inventory.Remove(equipable);
} }
@@ -492,7 +516,7 @@ public partial class Player : CharacterBody3D, IPlayer
if (Stats.CurrentVT.Value > 0) if (Stats.CurrentVT.Value > 0)
{ {
if (EquippedAccessory.Value.AccessoryTags.Contains(AccessoryTag.HalfVTConsumption)) if (EquippedAccessory.Value.AccessoryTag == AccessoryTag.HalfVTConsumption)
{ {
reduceOnTick = !reduceOnTick; reduceOnTick = !reduceOnTick;
} }
@@ -550,7 +574,7 @@ public partial class Player : CharacterBody3D, IPlayer
private void HitEnemy(IEnemy enemy) private void HitEnemy(IEnemy enemy)
{ {
var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value; var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value;
var ignoreElementalResistance = EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.IgnoreAffinity); var ignoreElementalResistance = EquippedWeapon.Value.WeaponTag == WeaponTag.IgnoreAffinity;
var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value); var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value);
var element = EquippedWeapon.Value.WeaponElement; var element = EquippedWeapon.Value.WeaponElement;
@@ -561,7 +585,7 @@ public partial class Player : CharacterBody3D, IPlayer
false, false,
ignoreElementalResistance); ignoreElementalResistance);
if (EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.Knockback)) if (EquippedWeapon.Value.WeaponTag == WeaponTag.Knockback)
enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized()); enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized());
} }
} }

View File

@@ -3,7 +3,7 @@
[ext_resource type="Script" uid="uid://yxmiqy7i0t7r" path="res://src/player/Player.cs" id="1_xcol5"] [ext_resource type="Script" uid="uid://yxmiqy7i0t7r" path="res://src/player/Player.cs" id="1_xcol5"]
[ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="2_lb3qc"] [ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="2_lb3qc"]
[ext_resource type="Script" uid="uid://s6ku2kyc4rbk" path="res://src/player/PlayerStatResource.cs" id="2_xq68d"] [ext_resource type="Script" uid="uid://s6ku2kyc4rbk" path="res://src/player/PlayerStatResource.cs" id="2_xq68d"]
[ext_resource type="Resource" uid="uid://b7xr0l4a8g1gk" path="res://src/items/weapons/resources/SealingRod.tres" id="3_ebyyx"] [ext_resource type="Resource" uid="uid://bpdbuf0k0exb5" path="res://src/items/weapons/resources/Swan Sword Odette.tres" id="3_es4xk"]
[ext_resource type="Resource" uid="uid://ce2vfa2t3io67" path="res://src/items/armor/resources/AtonersAdornments.tres" id="4_bj1ma"] [ext_resource type="Resource" uid="uid://ce2vfa2t3io67" path="res://src/items/armor/resources/AtonersAdornments.tres" id="4_bj1ma"]
[ext_resource type="Texture2D" uid="uid://c6r3dhnkuw22w" path="res://src/vfx/hit_effects/FIRE_STRIKE_1.0.png" id="5_wr6lo"] [ext_resource type="Texture2D" uid="uid://c6r3dhnkuw22w" path="res://src/vfx/hit_effects/FIRE_STRIKE_1.0.png" id="5_wr6lo"]
[ext_resource type="Texture2D" uid="uid://m6xsyhnt67sh" path="res://src/player/dont_look_in_here/tendomaya_body0_tex00.png" id="6_es4xk"] [ext_resource type="Texture2D" uid="uid://m6xsyhnt67sh" path="res://src/player/dont_look_in_here/tendomaya_body0_tex00.png" id="6_es4xk"]
@@ -32,13 +32,13 @@ BonusDefense = 0
MaxDefense = 12 MaxDefense = 12
Luck = 0.05 Luck = 0.05
[sub_resource type="BoxShape3D" id="BoxShape3D_wedu3"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_dw45s"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_dw45s"]
radius = 1.0 radius = 1.0
[sub_resource type="BoxShape3D" id="BoxShape3D_wedu3"]
[sub_resource type="BoxShape3D" id="BoxShape3D_hs4wf"] [sub_resource type="BoxShape3D" id="BoxShape3D_hs4wf"]
size = Vector3(1.94531, 2.43945, 2.35425) size = Vector3(1.94531, 3.38623, 2.35425)
[sub_resource type="Animation" id="Animation_hcjph"] [sub_resource type="Animation" id="Animation_hcjph"]
length = 0.001 length = 0.001
@@ -470,9 +470,13 @@ collision_layer = 806
collision_mask = 775 collision_mask = 775
script = ExtResource("1_xcol5") script = ExtResource("1_xcol5")
PlayerStatResource = SubResource("Resource_btp2w") PlayerStatResource = SubResource("Resource_btp2w")
_defaultWeapon = ExtResource("3_ebyyx") _defaultWeapon = ExtResource("3_es4xk")
_defaultArmor = ExtResource("4_bj1ma") _defaultArmor = ExtResource("4_bj1ma")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.06447, 0)
shape = SubResource("CapsuleShape3D_dw45s")
[node name="Hitbox" type="Area3D" parent="."] [node name="Hitbox" type="Area3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.12691, -1) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.12691, -1)
@@ -484,18 +488,14 @@ script = ExtResource("2_lb3qc")
shape = SubResource("BoxShape3D_wedu3") shape = SubResource("BoxShape3D_wedu3")
disabled = true disabled = true
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionDetector" type="Area3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.06447, 0)
shape = SubResource("CapsuleShape3D_dw45s")
[node name="CollisionDetector" type="Area3D" parent="CollisionShape3D"]
unique_name_in_owner = true unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.937567, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.126903, 0)
collision_layer = 452 collision_layer = 452
collision_mask = 452 collision_mask = 452
[node name="CollisionShape3D" type="CollisionShape3D" parent="CollisionShape3D/CollisionDetector"] [node name="CollisionShape3D" type="CollisionShape3D" parent="CollisionDetector"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0479561, 0.982638, -0.675098) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0479561, 0.509249, -0.675098)
shape = SubResource("BoxShape3D_hs4wf") shape = SubResource("BoxShape3D_hs4wf")
[node name="Camera3D" type="Camera3D" parent="."] [node name="Camera3D" type="Camera3D" parent="."]

14
src/player/PlayerData.cs Normal file
View File

@@ -0,0 +1,14 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
namespace GameJamDungeon;
[Meta, Id("player_data")]
public partial record PlayerData
{
[Save("player_stats")]
public required PlayerStats PlayerStats { get; init; }
[Save("player_inventory")]
public required Inventory Inventory { get; init; }
}