From 653cc451d2e439bb924346594882207df35a74e1 Mon Sep 17 00:00:00 2001 From: Jose Henrique Date: Sat, 8 Feb 2025 22:46:19 -0300 Subject: [PATCH] Refactor skill allocation to upgrade; implement skill requirements and resource checks; remove experience from Pet model; update README for skill tree progress --- Controllers/SkillController.cs | 4 +- Data/ApplicationDbContext.cs | 36 +- Data/SkillsData.cs | 441 ++++++++++-------- ... 20250209011029_InitialCreate.Designer.cs} | 187 +++++++- ...ate.cs => 20250209011029_InitialCreate.cs} | 77 ++- .../ApplicationDbContextModelSnapshot.cs | 185 +++++++- Models/Pet.cs | 3 - Models/Skill.cs | 17 +- README.md | 24 +- Repositories/PetSkillRepository.cs | 9 + Services/PetService.cs | 1 - Services/PetSkillService.cs | 32 +- Services/SkillService.cs | 62 +-- 13 files changed, 723 insertions(+), 355 deletions(-) rename Migrations/{20250204213845_InitialCreate.Designer.cs => 20250209011029_InitialCreate.Designer.cs} (78%) rename Migrations/{20250204213845_InitialCreate.cs => 20250209011029_InitialCreate.cs} (83%) diff --git a/Controllers/SkillController.cs b/Controllers/SkillController.cs index 41d4d47..cc0b5ef 100644 --- a/Controllers/SkillController.cs +++ b/Controllers/SkillController.cs @@ -44,11 +44,11 @@ namespace PetCompanion.Controllers } [HttpPost("{petId}/allocate/{skillId}")] - public IActionResult AllocateSkillPoint(string petId, int skillId) + public IActionResult UpgradeSkill(string petId, int skillId) { try { - var result = _petSkillService.AllocateSkillPoint(petId, userId.ToString(), skillId); + var result = _petSkillService.UpgradeSkill(petId, userId.ToString(), skillId); return Ok(result); } catch (Exception ex) diff --git a/Data/ApplicationDbContext.cs b/Data/ApplicationDbContext.cs index 4bbf35b..d0527c0 100644 --- a/Data/ApplicationDbContext.cs +++ b/Data/ApplicationDbContext.cs @@ -51,6 +51,11 @@ namespace PetCompanion.Data .WithMany(s => s.Effects) .HasForeignKey(se => se.SkillId); + modelBuilder.Entity() + .HasOne(se => se.Skill) + .WithMany(s => s.SkillRequirements) + .HasForeignKey(se => se.SkillId); + modelBuilder.Entity() .HasOne(e => e.Pet) .WithMany(p => p.EquippedItemsList) @@ -61,31 +66,14 @@ namespace PetCompanion.Data .WithMany() .HasForeignKey(e => e.GameItemId); - // Seed initial skills - var skills = SkillsData.GetInitialSkills(); - var skillWithoutEffects = SkillsData.GetInitialSkills().Select(s => new Skill - { - Id = s.Id, - Name = s.Name, - Type = s.Type, - Description = s.Description, - PointsCost = s.PointsCost, - Icon = s.Icon, - SkillsIdRequired = s.SkillsIdRequired - }).ToList(); + // Seed initial data + var skills = SkillsData.GetInitialSkillsWithoutRelations(); + var requirements = SkillsData.GetInitialSkillRequirements(); + var effects = SkillsData.GetInitialSkillEffects(); - foreach (var skill in skillWithoutEffects) - { - modelBuilder.Entity().HasData(skill); - } - - foreach (var skill in skills) - { - foreach (var effect in skill.Effects) - { - modelBuilder.Entity().HasData(effect); - } - } + modelBuilder.Entity().HasData(skills); + modelBuilder.Entity().HasData(requirements); + modelBuilder.Entity().HasData(effects); } } } diff --git a/Data/SkillsData.cs b/Data/SkillsData.cs index a6b22a2..ea67017 100644 --- a/Data/SkillsData.cs +++ b/Data/SkillsData.cs @@ -4,7 +4,7 @@ namespace PetCompanion.Data { public static class SkillsData { - public static IEnumerable GetInitialSkills() + public static IEnumerable GetInitialSkillsWithoutRelations() { return new List { @@ -14,36 +14,8 @@ namespace PetCompanion.Data Name = "Vitality Mastery", Description = "Increases maximum health of your pet, making it more resilient.", Type = SkillType.GROUND, - PointsCost = 1, Icon = "❤", - SkillsIdRequired = null, - Effects = new List - { - new SkillEffect - { - Id = 1, - SkillId = 1, - Tier = SkillTier.I, - Effect = "MaxHealth", - Value = 25 - }, - new SkillEffect - { - Id = 2, - SkillId = 1, - Tier = SkillTier.II, - Effect = "MaxHealth", - Value = 50 - }, - new SkillEffect - { - Id = 3, - SkillId = 1, - Tier = SkillTier.III, - Effect = "MaxHealth", - Value = 100 - } - } + SkillsIdRequired = null }, new Skill { @@ -51,36 +23,8 @@ namespace PetCompanion.Data Name = "Mind Enhancement", Description = "Increases maximum intelligence of your pet, improving its learning capabilities.", Type = SkillType.GROUND, - PointsCost = 1, Icon = "🧠", - SkillsIdRequired = null, - Effects = new List - { - new SkillEffect - { - Id = 4, - SkillId = 2, - Tier = SkillTier.I, - Effect = "MaxIntelligence", - Value = 5 - }, - new SkillEffect - { - Id = 5, - SkillId = 2, - Tier = SkillTier.II, - Effect = "MaxIntelligence", - Value = 10 - }, - new SkillEffect - { - Id = 6, - SkillId = 2, - Tier = SkillTier.III, - Effect = "MaxIntelligence", - Value = 20 - } - } + SkillsIdRequired = null }, new Skill { @@ -88,36 +32,8 @@ namespace PetCompanion.Data Name = "Strength Training", Description = "Increases maximum strength of your pet, making it more powerful.", Type = SkillType.GROUND, - PointsCost = 1, Icon = "💪", - SkillsIdRequired = null, - Effects = new List - { - new SkillEffect - { - Id = 7, - SkillId = 3, - Tier = SkillTier.I, - Effect = "MaxStrength", - Value = 5 - }, - new SkillEffect - { - Id = 8, - SkillId = 3, - Tier = SkillTier.II, - Effect = "MaxStrength", - Value = 10 - }, - new SkillEffect - { - Id = 9, - SkillId = 3, - Tier = SkillTier.III, - Effect = "MaxStrength", - Value = 20 - } - } + SkillsIdRequired = null }, new Skill { @@ -125,36 +41,8 @@ namespace PetCompanion.Data Name = "Charisma Boost", Description = "Increases maximum charisma of your pet, making it more charming.", Type = SkillType.GROUND, - PointsCost = 1, Icon = "🎭", - SkillsIdRequired = null, - Effects = new List - { - new SkillEffect - { - Id = 10, - SkillId = 4, - Tier = SkillTier.I, - Effect = "MaxCharisma", - Value = 5 - }, - new SkillEffect - { - Id = 11, - SkillId = 4, - Tier = SkillTier.II, - Effect = "MaxCharisma", - Value = 10 - }, - new SkillEffect - { - Id = 12, - SkillId = 4, - Tier = SkillTier.III, - Effect = "MaxCharisma", - Value = 20 - } - } + SkillsIdRequired = null }, new Skill { @@ -162,36 +50,8 @@ namespace PetCompanion.Data Name = "Luck of the Draw", Description = "Increases luck of your pet, making it more fortunate to find rare items.", Type = SkillType.GROUND, - PointsCost = 1, Icon = "🍀", - SkillsIdRequired = new List { 4 }, - Effects = new List - { - new SkillEffect - { - Id = 13, - SkillId = 5, - Tier = SkillTier.I, - Effect = "Luck", - Value = 1 - }, - new SkillEffect - { - Id = 14, - SkillId = 5, - Tier = SkillTier.II, - Effect = "Luck", - Value = 2 - }, - new SkillEffect - { - Id = 15, - SkillId = 5, - Tier = SkillTier.III, - Effect = "Luck", - Value = 3 - } - } + SkillsIdRequired = new List { 4 } }, new Skill { @@ -199,36 +59,8 @@ namespace PetCompanion.Data Name = "Agility Training", Description = "Increases agility of your pet, making it faster in combat.", Type = SkillType.GROUND, - PointsCost = 1, Icon = "🏃", - SkillsIdRequired = new List { 3 }, - Effects = new List - { - new SkillEffect - { - Id = 16, - SkillId = 6, - Tier = SkillTier.I, - Effect = "Agility", - Value = 1 - }, - new SkillEffect - { - Id = 17, - SkillId = 6, - Tier = SkillTier.II, - Effect = "Agility", - Value = 2 - }, - new SkillEffect - { - Id = 18, - SkillId = 6, - Tier = SkillTier.III, - Effect = "Agility", - Value = 3 - } - } + SkillsIdRequired = new List { 3 } }, new Skill { @@ -236,36 +68,237 @@ namespace PetCompanion.Data Name = "Perception Boost", Description = "Increases perception of your pet, making it more aware of its surroundings.", Type = SkillType.GROUND, - PointsCost = 1, Icon = "👀", - SkillsIdRequired = new List { 2 }, - Effects = new List - { - new SkillEffect - { - Id = 19, - SkillId = 7, - Tier = SkillTier.I, - Effect = "Perception", - Value = 1 - }, - new SkillEffect - { - Id = 20, - SkillId = 7, - Tier = SkillTier.II, - Effect = "Perception", - Value = 2 - }, - new SkillEffect - { - Id = 21, - SkillId = 7, - Tier = SkillTier.III, - Effect = "Perception", - Value = 3 - } - } + SkillsIdRequired = new List { 2 } + }, + new Skill + { + Id = 8, + Name = "Resourcefulness", + Description = "Increases the amount of resources gathered by your pet.", + Type = SkillType.GRAND, + Icon = "📦", + SkillsIdRequired = new List { 5, 6, 7 } + } + }; + } + + public static IEnumerable GetInitialSkillRequirements() + { + return new List + { + new SkillRequirement { Id = 1, SkillId = 1, Resource = "Wisdom", Cost = 100 }, + new SkillRequirement { Id = 2, SkillId = 1, Resource = "Food", Cost = 150 }, + new SkillRequirement { Id = 3, SkillId = 2, Resource = "Wisdom", Cost = 150 }, + new SkillRequirement { Id = 4, SkillId = 3, Resource = "Gold", Cost = 100 }, + new SkillRequirement { Id = 5, SkillId = 3, Resource = "Food", Cost = 150 }, + new SkillRequirement { Id = 6, SkillId = 4, Resource = "Wisdom", Cost = 200 }, + new SkillRequirement { Id = 7, SkillId = 5, Resource = "Gold", Cost = 200 }, + new SkillRequirement { Id = 8, SkillId = 6, Resource = "Gold", Cost = 200 }, + new SkillRequirement { Id = 9, SkillId = 7, Resource = "Gold", Cost = 100 }, + new SkillRequirement { Id = 10, SkillId = 7, Resource = "Junk", Cost = 100 }, + new SkillRequirement { Id = 11, SkillId = 8, Resource = "Gold", Cost = 500 }, + new SkillRequirement { Id = 12, SkillId = 8, Resource = "Junk", Cost = 500 }, + new SkillRequirement { Id = 13, SkillId = 8, Resource = "Wisdom", Cost = 300 }, + new SkillRequirement { Id = 14, SkillId = 8, Resource = "Food", Cost = 300 } + }; + } + + public static IEnumerable GetInitialSkillEffects() + { + return new List + { + new SkillEffect + { + Id = 1, + SkillId = 1, + Tier = SkillTier.I, + Effect = "MaxHealth", + Value = 25 + }, + new SkillEffect + { + Id = 2, + SkillId = 1, + Tier = SkillTier.II, + Effect = "MaxHealth", + Value = 50 + }, + new SkillEffect + { + Id = 3, + SkillId = 1, + Tier = SkillTier.III, + Effect = "MaxHealth", + Value = 100 + }, + new SkillEffect + { + Id = 4, + SkillId = 2, + Tier = SkillTier.I, + Effect = "MaxIntelligence", + Value = 5 + }, + new SkillEffect + { + Id = 5, + SkillId = 2, + Tier = SkillTier.II, + Effect = "MaxIntelligence", + Value = 10 + }, + new SkillEffect + { + Id = 6, + SkillId = 2, + Tier = SkillTier.III, + Effect = "MaxIntelligence", + Value = 20 + }, + new SkillEffect + { + Id = 7, + SkillId = 3, + Tier = SkillTier.I, + Effect = "MaxStrength", + Value = 5 + }, + new SkillEffect + { + Id = 8, + SkillId = 3, + Tier = SkillTier.II, + Effect = "MaxStrength", + Value = 10 + }, + new SkillEffect + { + Id = 9, + SkillId = 3, + Tier = SkillTier.III, + Effect = "MaxStrength", + Value = 20 + }, + new SkillEffect + { + Id = 10, + SkillId = 4, + Tier = SkillTier.I, + Effect = "MaxCharisma", + Value = 5 + }, + new SkillEffect + { + Id = 11, + SkillId = 4, + Tier = SkillTier.II, + Effect = "MaxCharisma", + Value = 10 + }, + new SkillEffect + { + Id = 12, + SkillId = 4, + Tier = SkillTier.III, + Effect = "MaxCharisma", + Value = 20 + }, + new SkillEffect + { + Id = 13, + SkillId = 5, + Tier = SkillTier.I, + Effect = "Luck", + Value = 1 + }, + new SkillEffect + { + Id = 14, + SkillId = 5, + Tier = SkillTier.II, + Effect = "Luck", + Value = 2 + }, + new SkillEffect + { + Id = 15, + SkillId = 5, + Tier = SkillTier.III, + Effect = "Luck", + Value = 3 + }, + new SkillEffect + { + Id = 16, + SkillId = 6, + Tier = SkillTier.I, + Effect = "Agility", + Value = 1 + }, + new SkillEffect + { + Id = 17, + SkillId = 6, + Tier = SkillTier.II, + Effect = "Agility", + Value = 2 + }, + new SkillEffect + { + Id = 18, + SkillId = 6, + Tier = SkillTier.III, + Effect = "Agility", + Value = 3 + }, + new SkillEffect + { + Id = 19, + SkillId = 7, + Tier = SkillTier.I, + Effect = "Perception", + Value = 1 + }, + new SkillEffect + { + Id = 20, + SkillId = 7, + Tier = SkillTier.II, + Effect = "Perception", + Value = 2 + }, + new SkillEffect + { + Id = 21, + SkillId = 7, + Tier = SkillTier.III, + Effect = "Perception", + Value = 3 + }, + new SkillEffect + { + Id = 22, + SkillId = 8, + Tier = SkillTier.I, + Effect = "Resourcefulness", + Value = 1 + }, + new SkillEffect + { + Id = 23, + SkillId = 8, + Tier = SkillTier.II, + Effect = "Resourcefulness", + Value = 2 + }, + new SkillEffect + { + Id = 24, + SkillId = 8, + Tier = SkillTier.III, + Effect = "Resourcefulness", + Value = 3 } }; } diff --git a/Migrations/20250204213845_InitialCreate.Designer.cs b/Migrations/20250209011029_InitialCreate.Designer.cs similarity index 78% rename from Migrations/20250204213845_InitialCreate.Designer.cs rename to Migrations/20250209011029_InitialCreate.Designer.cs index 68229e4..21efb46 100644 --- a/Migrations/20250204213845_InitialCreate.Designer.cs +++ b/Migrations/20250209011029_InitialCreate.Designer.cs @@ -11,7 +11,7 @@ using PetCompanion.Data; namespace PetCompanion.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20250204213845_InitialCreate")] + [Migration("20250209011029_InitialCreate")] partial class InitialCreate { /// @@ -108,9 +108,6 @@ namespace PetCompanion.Migrations b.Property("Class") .HasColumnType("INTEGER"); - b.Property("Experience") - .HasColumnType("INTEGER"); - b.Property("GatherActionSince") .HasColumnType("TEXT"); @@ -136,9 +133,6 @@ namespace PetCompanion.Migrations b.Property("PetGatherAction") .HasColumnType("INTEGER"); - b.Property("SkillPoints") - .HasColumnType("INTEGER"); - b.Property("UserId") .IsRequired() .HasColumnType("TEXT"); @@ -250,9 +244,6 @@ namespace PetCompanion.Migrations .IsRequired() .HasColumnType("TEXT"); - b.Property("PointsCost") - .HasColumnType("INTEGER"); - b.PrimitiveCollection("SkillsIdRequired") .HasColumnType("TEXT"); @@ -270,7 +261,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum health of your pet, making it more resilient.", Icon = "❤", Name = "Vitality Mastery", - PointsCost = 1, Type = 0 }, new @@ -279,7 +269,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum intelligence of your pet, improving its learning capabilities.", Icon = "🧠", Name = "Mind Enhancement", - PointsCost = 1, Type = 0 }, new @@ -288,7 +277,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum strength of your pet, making it more powerful.", Icon = "💪", Name = "Strength Training", - PointsCost = 1, Type = 0 }, new @@ -297,7 +285,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum charisma of your pet, making it more charming.", Icon = "🎭", Name = "Charisma Boost", - PointsCost = 1, Type = 0 }, new @@ -306,7 +293,6 @@ namespace PetCompanion.Migrations Description = "Increases luck of your pet, making it more fortunate to find rare items.", Icon = "🍀", Name = "Luck of the Draw", - PointsCost = 1, SkillsIdRequired = "[4]", Type = 0 }, @@ -316,7 +302,6 @@ namespace PetCompanion.Migrations Description = "Increases agility of your pet, making it faster in combat.", Icon = "🏃", Name = "Agility Training", - PointsCost = 1, SkillsIdRequired = "[3]", Type = 0 }, @@ -326,9 +311,17 @@ namespace PetCompanion.Migrations Description = "Increases perception of your pet, making it more aware of its surroundings.", Icon = "👀", Name = "Perception Boost", - PointsCost = 1, SkillsIdRequired = "[2]", Type = 0 + }, + new + { + Id = 8, + Description = "Increases the amount of resources gathered by your pet.", + Icon = "📦", + Name = "Resourcefulness", + SkillsIdRequired = "[5,6,7]", + Type = 1 }); }); @@ -525,6 +518,153 @@ namespace PetCompanion.Migrations SkillId = 7, Tier = 3, Value = 3m + }, + new + { + Id = 22, + Effect = "Resourcefulness", + SkillId = 8, + Tier = 1, + Value = 1m + }, + new + { + Id = 23, + Effect = "Resourcefulness", + SkillId = 8, + Tier = 2, + Value = 2m + }, + new + { + Id = 24, + Effect = "Resourcefulness", + SkillId = 8, + Tier = 3, + Value = 3m + }); + }); + + modelBuilder.Entity("PetCompanion.Models.SkillRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("INTEGER"); + + b.Property("Resource") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SkillId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SkillId"); + + b.ToTable("SkillRequirement"); + + b.HasData( + new + { + Id = 1, + Cost = 100, + Resource = "Wisdom", + SkillId = 1 + }, + new + { + Id = 2, + Cost = 150, + Resource = "Food", + SkillId = 1 + }, + new + { + Id = 3, + Cost = 150, + Resource = "Wisdom", + SkillId = 2 + }, + new + { + Id = 4, + Cost = 100, + Resource = "Gold", + SkillId = 3 + }, + new + { + Id = 5, + Cost = 150, + Resource = "Food", + SkillId = 3 + }, + new + { + Id = 6, + Cost = 200, + Resource = "Wisdom", + SkillId = 4 + }, + new + { + Id = 7, + Cost = 200, + Resource = "Gold", + SkillId = 5 + }, + new + { + Id = 8, + Cost = 200, + Resource = "Gold", + SkillId = 6 + }, + new + { + Id = 9, + Cost = 100, + Resource = "Gold", + SkillId = 7 + }, + new + { + Id = 10, + Cost = 100, + Resource = "Junk", + SkillId = 7 + }, + new + { + Id = 11, + Cost = 500, + Resource = "Gold", + SkillId = 8 + }, + new + { + Id = 12, + Cost = 500, + Resource = "Junk", + SkillId = 8 + }, + new + { + Id = 13, + Cost = 300, + Resource = "Wisdom", + SkillId = 8 + }, + new + { + Id = 14, + Cost = 300, + Resource = "Food", + SkillId = 8 }); }); @@ -606,6 +746,17 @@ namespace PetCompanion.Migrations b.Navigation("Skill"); }); + modelBuilder.Entity("PetCompanion.Models.SkillRequirement", b => + { + b.HasOne("PetCompanion.Models.Skill", "Skill") + .WithMany("SkillRequirements") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + }); + modelBuilder.Entity("PetCompanion.Models.Pet", b => { b.Navigation("EquippedItemsList"); @@ -627,6 +778,8 @@ namespace PetCompanion.Migrations b.Navigation("Effects"); b.Navigation("PetSkills"); + + b.Navigation("SkillRequirements"); }); #pragma warning restore 612, 618 } diff --git a/Migrations/20250204213845_InitialCreate.cs b/Migrations/20250209011029_InitialCreate.cs similarity index 83% rename from Migrations/20250204213845_InitialCreate.cs rename to Migrations/20250209011029_InitialCreate.cs index 257e4b8..f392471 100644 --- a/Migrations/20250204213845_InitialCreate.cs +++ b/Migrations/20250209011029_InitialCreate.cs @@ -40,7 +40,6 @@ namespace PetCompanion.Migrations Name = table.Column(type: "TEXT", nullable: false), Class = table.Column(type: "INTEGER", nullable: false), Level = table.Column(type: "INTEGER", nullable: false), - Experience = table.Column(type: "INTEGER", nullable: false), Health = table.Column(type: "INTEGER", nullable: false), MaxHealth = table.Column(type: "INTEGER", nullable: false), UserId = table.Column(type: "TEXT", nullable: false), @@ -48,8 +47,7 @@ namespace PetCompanion.Migrations PetGatherAction = table.Column(type: "INTEGER", nullable: false), GatherActionSince = table.Column(type: "TEXT", nullable: false), PetBasicAction = table.Column(type: "INTEGER", nullable: false), - BasicActionCooldown = table.Column(type: "TEXT", nullable: false), - SkillPoints = table.Column(type: "INTEGER", nullable: false) + BasicActionCooldown = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -65,7 +63,6 @@ namespace PetCompanion.Migrations Name = table.Column(type: "TEXT", nullable: false), Description = table.Column(type: "TEXT", nullable: false), Type = table.Column(type: "INTEGER", nullable: false), - PointsCost = table.Column(type: "INTEGER", nullable: false), Icon = table.Column(type: "TEXT", nullable: false), SkillsIdRequired = table.Column(type: "TEXT", nullable: true) }, @@ -216,18 +213,40 @@ namespace PetCompanion.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "SkillRequirement", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SkillId = table.Column(type: "INTEGER", nullable: false), + Resource = table.Column(type: "TEXT", nullable: false), + Cost = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SkillRequirement", x => x.Id); + table.ForeignKey( + name: "FK_SkillRequirement_Skills_SkillId", + column: x => x.SkillId, + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.InsertData( table: "Skills", - columns: new[] { "Id", "Description", "Icon", "Name", "PointsCost", "SkillsIdRequired", "Type" }, + columns: new[] { "Id", "Description", "Icon", "Name", "SkillsIdRequired", "Type" }, values: new object[,] { - { 1, "Increases maximum health of your pet, making it more resilient.", "❤", "Vitality Mastery", 1, null, 0 }, - { 2, "Increases maximum intelligence of your pet, improving its learning capabilities.", "🧠", "Mind Enhancement", 1, null, 0 }, - { 3, "Increases maximum strength of your pet, making it more powerful.", "💪", "Strength Training", 1, null, 0 }, - { 4, "Increases maximum charisma of your pet, making it more charming.", "🎭", "Charisma Boost", 1, null, 0 }, - { 5, "Increases luck of your pet, making it more fortunate to find rare items.", "🍀", "Luck of the Draw", 1, "[4]", 0 }, - { 6, "Increases agility of your pet, making it faster in combat.", "🏃", "Agility Training", 1, "[3]", 0 }, - { 7, "Increases perception of your pet, making it more aware of its surroundings.", "👀", "Perception Boost", 1, "[2]", 0 } + { 1, "Increases maximum health of your pet, making it more resilient.", "❤", "Vitality Mastery", null, 0 }, + { 2, "Increases maximum intelligence of your pet, improving its learning capabilities.", "🧠", "Mind Enhancement", null, 0 }, + { 3, "Increases maximum strength of your pet, making it more powerful.", "💪", "Strength Training", null, 0 }, + { 4, "Increases maximum charisma of your pet, making it more charming.", "🎭", "Charisma Boost", null, 0 }, + { 5, "Increases luck of your pet, making it more fortunate to find rare items.", "🍀", "Luck of the Draw", "[4]", 0 }, + { 6, "Increases agility of your pet, making it faster in combat.", "🏃", "Agility Training", "[3]", 0 }, + { 7, "Increases perception of your pet, making it more aware of its surroundings.", "👀", "Perception Boost", "[2]", 0 }, + { 8, "Increases the amount of resources gathered by your pet.", "📦", "Resourcefulness", "[5,6,7]", 1 } }); migrationBuilder.InsertData( @@ -255,7 +274,31 @@ namespace PetCompanion.Migrations { 18, "Agility", 6, 3, 3m }, { 19, "Perception", 7, 1, 1m }, { 20, "Perception", 7, 2, 2m }, - { 21, "Perception", 7, 3, 3m } + { 21, "Perception", 7, 3, 3m }, + { 22, "Resourcefulness", 8, 1, 1m }, + { 23, "Resourcefulness", 8, 2, 2m }, + { 24, "Resourcefulness", 8, 3, 3m } + }); + + migrationBuilder.InsertData( + table: "SkillRequirement", + columns: new[] { "Id", "Cost", "Resource", "SkillId" }, + values: new object[,] + { + { 1, 100, "Wisdom", 1 }, + { 2, 150, "Food", 1 }, + { 3, 150, "Wisdom", 2 }, + { 4, 100, "Gold", 3 }, + { 5, 150, "Food", 3 }, + { 6, 200, "Wisdom", 4 }, + { 7, 200, "Gold", 5 }, + { 8, 200, "Gold", 6 }, + { 9, 100, "Gold", 7 }, + { 10, 100, "Junk", 7 }, + { 11, 500, "Gold", 8 }, + { 12, 500, "Junk", 8 }, + { 13, 300, "Wisdom", 8 }, + { 14, 300, "Food", 8 } }); migrationBuilder.CreateIndex( @@ -282,6 +325,11 @@ namespace PetCompanion.Migrations name: "IX_SkillEffects_SkillId", table: "SkillEffects", column: "SkillId"); + + migrationBuilder.CreateIndex( + name: "IX_SkillRequirement_SkillId", + table: "SkillRequirement", + column: "SkillId"); } /// @@ -305,6 +353,9 @@ namespace PetCompanion.Migrations migrationBuilder.DropTable( name: "SkillEffects"); + migrationBuilder.DropTable( + name: "SkillRequirement"); + migrationBuilder.DropTable( name: "GameItems"); diff --git a/Migrations/ApplicationDbContextModelSnapshot.cs b/Migrations/ApplicationDbContextModelSnapshot.cs index e3f61f5..c08dfe6 100644 --- a/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Migrations/ApplicationDbContextModelSnapshot.cs @@ -105,9 +105,6 @@ namespace PetCompanion.Migrations b.Property("Class") .HasColumnType("INTEGER"); - b.Property("Experience") - .HasColumnType("INTEGER"); - b.Property("GatherActionSince") .HasColumnType("TEXT"); @@ -133,9 +130,6 @@ namespace PetCompanion.Migrations b.Property("PetGatherAction") .HasColumnType("INTEGER"); - b.Property("SkillPoints") - .HasColumnType("INTEGER"); - b.Property("UserId") .IsRequired() .HasColumnType("TEXT"); @@ -247,9 +241,6 @@ namespace PetCompanion.Migrations .IsRequired() .HasColumnType("TEXT"); - b.Property("PointsCost") - .HasColumnType("INTEGER"); - b.PrimitiveCollection("SkillsIdRequired") .HasColumnType("TEXT"); @@ -267,7 +258,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum health of your pet, making it more resilient.", Icon = "❤", Name = "Vitality Mastery", - PointsCost = 1, Type = 0 }, new @@ -276,7 +266,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum intelligence of your pet, improving its learning capabilities.", Icon = "🧠", Name = "Mind Enhancement", - PointsCost = 1, Type = 0 }, new @@ -285,7 +274,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum strength of your pet, making it more powerful.", Icon = "💪", Name = "Strength Training", - PointsCost = 1, Type = 0 }, new @@ -294,7 +282,6 @@ namespace PetCompanion.Migrations Description = "Increases maximum charisma of your pet, making it more charming.", Icon = "🎭", Name = "Charisma Boost", - PointsCost = 1, Type = 0 }, new @@ -303,7 +290,6 @@ namespace PetCompanion.Migrations Description = "Increases luck of your pet, making it more fortunate to find rare items.", Icon = "🍀", Name = "Luck of the Draw", - PointsCost = 1, SkillsIdRequired = "[4]", Type = 0 }, @@ -313,7 +299,6 @@ namespace PetCompanion.Migrations Description = "Increases agility of your pet, making it faster in combat.", Icon = "🏃", Name = "Agility Training", - PointsCost = 1, SkillsIdRequired = "[3]", Type = 0 }, @@ -323,9 +308,17 @@ namespace PetCompanion.Migrations Description = "Increases perception of your pet, making it more aware of its surroundings.", Icon = "👀", Name = "Perception Boost", - PointsCost = 1, SkillsIdRequired = "[2]", Type = 0 + }, + new + { + Id = 8, + Description = "Increases the amount of resources gathered by your pet.", + Icon = "📦", + Name = "Resourcefulness", + SkillsIdRequired = "[5,6,7]", + Type = 1 }); }); @@ -522,6 +515,153 @@ namespace PetCompanion.Migrations SkillId = 7, Tier = 3, Value = 3m + }, + new + { + Id = 22, + Effect = "Resourcefulness", + SkillId = 8, + Tier = 1, + Value = 1m + }, + new + { + Id = 23, + Effect = "Resourcefulness", + SkillId = 8, + Tier = 2, + Value = 2m + }, + new + { + Id = 24, + Effect = "Resourcefulness", + SkillId = 8, + Tier = 3, + Value = 3m + }); + }); + + modelBuilder.Entity("PetCompanion.Models.SkillRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("INTEGER"); + + b.Property("Resource") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SkillId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SkillId"); + + b.ToTable("SkillRequirement"); + + b.HasData( + new + { + Id = 1, + Cost = 100, + Resource = "Wisdom", + SkillId = 1 + }, + new + { + Id = 2, + Cost = 150, + Resource = "Food", + SkillId = 1 + }, + new + { + Id = 3, + Cost = 150, + Resource = "Wisdom", + SkillId = 2 + }, + new + { + Id = 4, + Cost = 100, + Resource = "Gold", + SkillId = 3 + }, + new + { + Id = 5, + Cost = 150, + Resource = "Food", + SkillId = 3 + }, + new + { + Id = 6, + Cost = 200, + Resource = "Wisdom", + SkillId = 4 + }, + new + { + Id = 7, + Cost = 200, + Resource = "Gold", + SkillId = 5 + }, + new + { + Id = 8, + Cost = 200, + Resource = "Gold", + SkillId = 6 + }, + new + { + Id = 9, + Cost = 100, + Resource = "Gold", + SkillId = 7 + }, + new + { + Id = 10, + Cost = 100, + Resource = "Junk", + SkillId = 7 + }, + new + { + Id = 11, + Cost = 500, + Resource = "Gold", + SkillId = 8 + }, + new + { + Id = 12, + Cost = 500, + Resource = "Junk", + SkillId = 8 + }, + new + { + Id = 13, + Cost = 300, + Resource = "Wisdom", + SkillId = 8 + }, + new + { + Id = 14, + Cost = 300, + Resource = "Food", + SkillId = 8 }); }); @@ -603,6 +743,17 @@ namespace PetCompanion.Migrations b.Navigation("Skill"); }); + modelBuilder.Entity("PetCompanion.Models.SkillRequirement", b => + { + b.HasOne("PetCompanion.Models.Skill", "Skill") + .WithMany("SkillRequirements") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + }); + modelBuilder.Entity("PetCompanion.Models.Pet", b => { b.Navigation("EquippedItemsList"); @@ -624,6 +775,8 @@ namespace PetCompanion.Migrations b.Navigation("Effects"); b.Navigation("PetSkills"); + + b.Navigation("SkillRequirements"); }); #pragma warning restore 612, 618 } diff --git a/Models/Pet.cs b/Models/Pet.cs index 1b66e32..e1e2417 100644 --- a/Models/Pet.cs +++ b/Models/Pet.cs @@ -12,7 +12,6 @@ namespace PetCompanion.Models public PetStats Stats { get; set; } public Resources Resources { get; set; } public int Level { get; set; } - public int Experience { get; set; } public int Health { get; set; } public int MaxHealth { get; set; } public string UserId { get; set; } @@ -24,8 +23,6 @@ namespace PetCompanion.Models public PetBasicAction PetBasicAction { get; set; } public DateTime BasicActionCooldown { get; set; } - public int SkillPoints { get; set; } = 2; - public virtual ICollection Skills { get; set; } = new List(); public virtual Inventory Inventory { get; set; } public virtual ICollection EquippedItemsList { get; set; } = new List(); diff --git a/Models/Skill.cs b/Models/Skill.cs index f044927..8d1cf64 100644 --- a/Models/Skill.cs +++ b/Models/Skill.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; namespace PetCompanion.Models @@ -23,16 +24,30 @@ namespace PetCompanion.Models public string Name { get; set; } public string Description { get; set; } public SkillType Type { get; set; } - public int PointsCost { get; set; } public string Icon { get; set; } public List? SkillsIdRequired { get; set; } + public virtual ICollection SkillRequirements { get; set; } public virtual ICollection Effects { get; set; } [JsonIgnore] public virtual ICollection PetSkills { get; set; } } + public class SkillRequirement + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Key] + public int Id { get; set; } + public int SkillId { get; set; } + public string Resource { get; set; } + public int Cost { get; set; } + + [JsonIgnore] + public virtual Skill Skill { get; set; } + } + public class SkillEffect { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Key] public int Id { get; set; } public int SkillId { get; set; } diff --git a/README.md b/README.md index 54f8c82..97acc28 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,38 @@ # Pet Companion -## Todo: -- [ ] Inventory system -- [ ] Skill tree system +## Todo (v1): +- [P] Inventory system +- [P] Skill tree system - [ ] Explore system - [ ] Battle system -- [ ] Quest system - [ ] User login/register system - [ ] User profile/settings system + +### V1.5: +- [ ] + +### V2: +- [ ] Quest system +- [ ] Market system (internal) - [ ] Front-End: 3D pet model - [ ] Front-End: 3D pet animations - [ ] Front-End: 3D pet interactions - [ ] Front-End: 3D pet environment (depending on the class) +### V3: +- [ ] Front-End: improved skill tree design +- [ ] Front-End: improved inventory design + ## Inventory system -- [ ] Inventory UI +- [x] Inventory UI - [x] Inventory database - [x] Inventory item database (items, consumables, equipment) - [x] Inventory item interactions (use, equip, unequip, drop) - [/] Inventory item stacking -- [ ] Inventory item sorting (UI) +- [/] Inventory item sorting (UI) ## Skill tree system -- [ ] Skill tree UI +- [x] Skill tree UI - [x] Skill tree database - [/] Skill tree interactions (learn, unlearn, upgrade) - [/] Skill tree requirements (level, skill points, other skills) diff --git a/Repositories/PetSkillRepository.cs b/Repositories/PetSkillRepository.cs index bbb8cde..d667745 100644 --- a/Repositories/PetSkillRepository.cs +++ b/Repositories/PetSkillRepository.cs @@ -13,10 +13,19 @@ namespace PetCompanion.Repositories _context = context; } + public IEnumerable GetAvailableSkills() + { + return _context.Skills + .Include(s => s.Effects) + .Include(s => s.SkillRequirements) + .ToList(); + } + public Skill GetSkill(int id) { return _context.Skills .Include(s => s.Effects) + .Include(s => s.SkillRequirements) .FirstOrDefault(s => s.Id == id); } diff --git a/Services/PetService.cs b/Services/PetService.cs index fd21699..3fd70e8 100644 --- a/Services/PetService.cs +++ b/Services/PetService.cs @@ -42,7 +42,6 @@ namespace PetCompanion.Services Health = 100, MaxHealth = 100, Level = 1, - Experience = 0, Stats = PetStats.BuildFromClass(petRequest.Class), Resources = new Resources(), GatherActionSince = DateTime.UtcNow, diff --git a/Services/PetSkillService.cs b/Services/PetSkillService.cs index 48c2d78..b5ecb7c 100644 --- a/Services/PetSkillService.cs +++ b/Services/PetSkillService.cs @@ -23,16 +23,29 @@ namespace PetCompanion.Services return _petSkillRepository.GetPetSkills(petId); } - public PetSkill AllocateSkillPoint(string petId, string userId, int skillId) + public PetSkill UpgradeSkill(string petId, string userId, int skillId) { var pet = _petRepository.GetPetById(petId, userId); if (pet == null) throw new Exception("Pet not found"); - if (pet.SkillPoints <= 0) - throw new Exception("No skill points available"); - var skill = _petSkillRepository.GetSkill(skillId); + + if (skill == null) + throw new Exception("Skill not found"); + + foreach (var req in skill.SkillRequirements) + { + if (req.Resource.ToLower() == "wisdom" && pet.Resources.Wisdom < req.Cost) + throw new Exception("Insufficient resources"); + if (req.Resource.ToLower() == "food" && pet.Resources.Food < req.Cost) + throw new Exception("Insufficient resources"); + if (req.Resource.ToLower() == "gold" && pet.Resources.Gold < req.Cost) + throw new Exception("Insufficient resources"); + if (req.Resource.ToLower() == "junk" && pet.Resources.Junk < req.Cost) + throw new Exception("Insufficient resources"); + } + var skills = _petSkillRepository.GetPetSkills(petId); var existingSkill = skills.FirstOrDefault(s => s.SkillId == skillId); @@ -46,7 +59,7 @@ namespace PetCompanion.Services } else { - if (!skill.SkillsIdRequired.TrueForAll(ni => pet.Skills.Any(s => s.SkillId == ni))) + if (!skill.SkillsIdRequired?.TrueForAll(ni => pet.Skills.Any(s => s.SkillId == ni)) ?? false) { throw new Exception("Missing required skill"); } @@ -60,7 +73,14 @@ namespace PetCompanion.Services _petSkillRepository.SavePetSkill(existingSkill); } - pet.SkillPoints--; + foreach (var req in skill.SkillRequirements) + { + if (req.Resource.ToLower() == "wisdom") pet.Resources.Wisdom -= req.Cost; + if (req.Resource.ToLower() == "food") pet.Resources.Food -= req.Cost; + if (req.Resource.ToLower() == "gold") pet.Resources.Gold -= req.Cost; + if (req.Resource.ToLower() == "junk") pet.Resources.Junk -= req.Cost; + } + _petRepository.UpdatePet(pet); return existingSkill; diff --git a/Services/SkillService.cs b/Services/SkillService.cs index 3b2b309..81d4049 100644 --- a/Services/SkillService.cs +++ b/Services/SkillService.cs @@ -1,84 +1,24 @@ using PetCompanion.Models; -using PetCompanion.Data; -using Microsoft.EntityFrameworkCore; using PetCompanion.Repositories; namespace PetCompanion.Services { public class SkillService { - private readonly ApplicationDbContext _context; private readonly PetRepository _petRepository; private readonly PetSkillRepository _petSkillRepository; public SkillService( - ApplicationDbContext context, PetRepository petRepository, PetSkillRepository petSkillRepository) { - _context = context; _petRepository = petRepository; _petSkillRepository = petSkillRepository; } - public PetSkill AllocateSkillPoint(string petId, string userId, int skillId) - { - var pet = _context.Pets - .Include(p => p.Skills) - .FirstOrDefault(p => p.Id == petId && p.UserId == userId); - - if (pet == null) - throw new Exception("Pet not found"); - - if (pet.SkillPoints <= 0) - throw new Exception("No skill points available"); - - var skill = _context.Skills - .Include(s => s.Effects) - .FirstOrDefault(s => s.Id == skillId); - - if (skill == null) - throw new Exception("Skill not found"); - - var existingSkill = pet.Skills.FirstOrDefault(ps => ps.SkillId == skillId); - if (existingSkill != null) - { - if (existingSkill.CurrentTier == SkillTier.III) - throw new Exception("Skill already at maximum tier"); - - existingSkill.CurrentTier++; - var effect = skill.Effects.FirstOrDefault(e => e.Tier == existingSkill.CurrentTier); - if (effect != null) - effect.PetSkillUpgrade(ref pet); - - _petSkillRepository.SavePetSkill(existingSkill); - } - else - { - var newSkill = new PetSkill - { - PetId = petId, - SkillId = skillId, - CurrentTier = SkillTier.I - }; - _petSkillRepository.SavePetSkill(newSkill); - - var effect = skill.Effects.FirstOrDefault(e => e.Tier == SkillTier.I); - if (effect != null) - effect.PetSkillUpgrade(ref pet); - } - - pet.SkillPoints--; - _context.SaveChanges(); - - return _petSkillRepository.GetPetSkills(petId).First(ps => ps.SkillId == skillId); - } - public IEnumerable GetAvailableSkills() { - return _context.Skills - .Include(s => s.Effects) - .ToList(); + return _petSkillRepository.GetAvailableSkills(); } } }