diff --git a/Controllers/SkillController.cs b/Controllers/SkillController.cs new file mode 100644 index 0000000..8781a72 --- /dev/null +++ b/Controllers/SkillController.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Mvc; +using PetCompanion.Services; + +namespace PetCompanion.Controllers +{ + [ApiController] + [Route("api/v1/[controller]")] + public class SkillController : ControllerBase + { + private readonly SkillService _skillService; + private readonly ILogger _logger; + private Guid userId = Guid.Parse("f5f4b3b3-3b7b-4b7b-8b7b-7b7b7b7b7b7b"); + + public SkillController( + ILogger logger, + SkillService skillService) + { + _logger = logger; + _skillService = skillService; + } + + [HttpGet] + public async Task GetAvailableSkills() + { + var skills = await _skillService.GetAvailableSkills(); + return Ok(skills); + } + + [HttpPost("{petId}/allocate/{skillId}")] + public async Task AllocateSkillPoint(string petId, int skillId) + { + try + { + var result = await _skillService.AllocateSkillPoint(petId, userId.ToString(), skillId); + return Ok(result); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } + } +} diff --git a/Data/ApplicationDbContext.cs b/Data/ApplicationDbContext.cs index 0e9a2c0..4c42c4a 100644 --- a/Data/ApplicationDbContext.cs +++ b/Data/ApplicationDbContext.cs @@ -12,6 +12,9 @@ namespace PetCompanion.Data public DbSet Pets { get; set; } public DbSet PetStats { get; set; } public DbSet Resources { get; set; } + public DbSet Skills { get; set; } + public DbSet SkillEffects { get; set; } + public DbSet PetSkills { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -29,6 +32,32 @@ namespace PetCompanion.Data .HasConversion( v => v != DateTime.MinValue ? v.ToUniversalTime() : v, v => v != DateTime.MinValue ? DateTime.SpecifyKind(v, DateTimeKind.Utc) : v); + + modelBuilder.Entity() + .HasOne(ps => ps.Pet) + .WithMany(p => p.Skills) + .HasForeignKey(ps => ps.PetId); + + modelBuilder.Entity() + .HasOne(ps => ps.Skill) + .WithMany(s => s.PetSkills) + .HasForeignKey(ps => ps.SkillId); + + modelBuilder.Entity() + .HasOne(se => se.Skill) + .WithMany(s => s.Effects) + .HasForeignKey(se => se.SkillId); + + // Seed initial skills + var skills = SkillsData.GetInitialSkills(); + foreach (var skill in skills) + { + modelBuilder.Entity().HasData(skill); + foreach (var effect in skill.Effects) + { + modelBuilder.Entity().HasData(effect); + } + } } } } diff --git a/Data/SkillsData.cs b/Data/SkillsData.cs new file mode 100644 index 0000000..a6b22a2 --- /dev/null +++ b/Data/SkillsData.cs @@ -0,0 +1,273 @@ +using PetCompanion.Models; + +namespace PetCompanion.Data +{ + public static class SkillsData + { + public static IEnumerable GetInitialSkills() + { + return new List + { + new Skill + { + Id = 1, + 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 + } + } + }, + new Skill + { + Id = 2, + 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 + } + } + }, + new Skill + { + Id = 3, + 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 + } + } + }, + new Skill + { + Id = 4, + 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 + } + } + }, + new Skill + { + Id = 5, + 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 + } + } + }, + new Skill + { + Id = 6, + 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 + } + } + }, + new Skill + { + Id = 7, + 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 + } + } + } + }; + } + } +} diff --git a/Items.csv b/Items.csv index ed4b6cf..33f4160 100644 --- a/Items.csv +++ b/Items.csv @@ -1,20 +1,21 @@ -Item Name, Type, Rarity, Description -Basic Kibble,Consumable,Common,Adds +20 food resources -Superfood Smoothie,Consumable,Uncommon,Adds +30 food resources; Restores 5 Intelligence -Energy Drink,Consumable,Rare,Reduces Cooldown by 5 min -Golden Apple,Consumable,Legendary,Adds +20 Intelligence (Permanent); Adds +100 food resources -Healing Potion,Consumable,Uncommon,Adds +20 to Health; Adds +20 food resources -Charisma Cookie,Consumable,Rare,Adds +2 Charisma (Permanent) -XP Booster,Consumable,Rare,Award +10 XP -Sleeping Draught,Consumable,Common,Reduces Cooldown for resting by 10 min -Mystery Meat,Consumable,Uncommon,Randomly ±2 to one stat (Permanent) -Elixir of Vitality,Consumable,Legendary,Fully restores all stats and health; Adds +1 Level -Leather Hat,Equipment,Common,Helmet: +5 Max Health -Wizard Hat,Equipment,Rare,Helmet: +15 Max Intelligence -Knight's Armor,Equipment,Rare,Chest: +15 Max Strength -Golden Boots,Equipment,Uncommon,Legging: +10 Max Charisma -Laser Pointer,Equipment,Common,Weapon: +5 Max Strength -Celestial Crown,Equipment,Legendary,Helmet: +20 max to all stats -Dragon Scale Shield,Equipment,Legendary,Weapon: +50 Max Health -Feathers,Material,Common, Crafting material (coming soon) -Phoenix Feather,Material,Legendary, Crafting material (coming soon) \ No newline at end of file +ItemId,Item Name, Type, Rarity, Description,Price,Effect +0,Apple,Material,Common,Crafting material (coming soon),0,Nothing +1,Basic Kibble,Consumable,Common,Adds +20 food resources,0,ADD_FOOD_RESOURCES_20 +2,Superfood Smoothie,Consumable,Uncommon,Adds +30 food resources; Restores 5 Intelligence,0,ADD_FOOD_RESOURCES_30 +3,Energy Drink,Consumable,Rare,Reduces Cooldown by 5 min,0,REDUCE_COOLDOWN_5 +4,Golden Apple,Consumable,Legendary,Adds +20 Intelligence (Permanent); Adds +100 food resources,0,ADD_INTELLIGENCE_20 +5,Healing Potion,Consumable,Uncommon,Adds +20 to Health; Adds +20 food resources,0,ADD_HEALTH_20_AND_FOOD_20 +6,Charisma Cookie,Consumable,Rare,Adds +2 Charisma (Permanent),0,ADD_CHARISMA_2 +7,XP Booster,Consumable,Rare,Award +10 XP,0,ADD_XP_10 +8,Sleeping Draught,Consumable,Common,Reduces Cooldown for resting by 10 min,0,REDUCE_REST_COOLDOWN_10 +9,Mystery Meat,Consumable,Uncommon,Randomly ±2 to one stat (Permanent),0,ADD_RANDOM_STAT_2 +10,Elixir of Vitality,Consumable,Legendary,Fully restores all stats and health; Adds +1 Level,0,RESTORE_STATS;ADD_LEVEL_1 +11,Leather Hat,Equipment,Common,Helmet: +5 Max Health,0,ADD_MAX_HEALTH_5 +12,Wizard Hat,Equipment,Rare,Helmet: +15 Max Intelligence,0,ADD_MAX_INTELLIGENCE_15 +13,Knight's Armor,Equipment,Rare,Chest: +15 Max Strength,0,ADD_MAX_STRENGTH_15 +14,Golden Boots,Equipment,Uncommon,Legging: +10 Max Charisma,0,ADD_MAX_CHARISMA_10 +15,Laser Pointer,Equipment,Common,Weapon: +5 Max Strength,0,ADD_MAX_STRENGTH_5 +16,Celestial Crown,Equipment,Legendary,Helmet: +20 max to all stats,0,ADD_MAX_ALL_STATS_20 +17,Dragon Scale Shield,Equipment,Legendary,Weapon: +50 Max Health,0,ADD_MAX_HEALTH_50 +18,Feathers,Material,Common,Crafting material (coming soon),0,Nothing +19,Phoenix Feather,Material,Legendary,Crafting material (coming soon),0,Nothing \ No newline at end of file diff --git a/Migrations/20250201173643_Initial.Designer.cs b/Migrations/20250201173643_Initial.Designer.cs deleted file mode 100644 index ff3a894..0000000 --- a/Migrations/20250201173643_Initial.Designer.cs +++ /dev/null @@ -1,141 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using PetCompanion.Data; - -#nullable disable - -namespace PetCompanion.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20250201173643_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.1"); - - modelBuilder.Entity("PetCompanion.Models.Pet", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("BasicActionCooldown") - .HasColumnType("TEXT"); - - b.Property("Class") - .HasColumnType("INTEGER"); - - b.Property("GatherActionSince") - .HasColumnType("TEXT"); - - b.Property("IsDead") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("PetBasicAction") - .HasColumnType("INTEGER"); - - b.Property("PetGatherAction") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Pets"); - }); - - modelBuilder.Entity("PetCompanion.Models.PetStats", b => - { - b.Property("PetId") - .HasColumnType("TEXT"); - - b.Property("Charisma") - .HasColumnType("INTEGER"); - - b.Property("Intelligence") - .HasColumnType("INTEGER"); - - b.Property("MaxCharisma") - .HasColumnType("INTEGER"); - - b.Property("MaxIntelligence") - .HasColumnType("INTEGER"); - - b.Property("MaxStrength") - .HasColumnType("INTEGER"); - - b.Property("Strength") - .HasColumnType("INTEGER"); - - b.HasKey("PetId"); - - b.ToTable("PetStats"); - }); - - modelBuilder.Entity("PetCompanion.Models.Resources", b => - { - b.Property("PetId") - .HasColumnType("TEXT"); - - b.Property("Food") - .HasColumnType("INTEGER"); - - b.Property("Gold") - .HasColumnType("INTEGER"); - - b.Property("Junk") - .HasColumnType("INTEGER"); - - b.Property("Wisdom") - .HasColumnType("INTEGER"); - - b.HasKey("PetId"); - - b.ToTable("Resources"); - }); - - modelBuilder.Entity("PetCompanion.Models.PetStats", b => - { - b.HasOne("PetCompanion.Models.Pet", null) - .WithOne("Stats") - .HasForeignKey("PetCompanion.Models.PetStats", "PetId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("PetCompanion.Models.Resources", b => - { - b.HasOne("PetCompanion.Models.Pet", null) - .WithOne("Resources") - .HasForeignKey("PetCompanion.Models.Resources", "PetId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("PetCompanion.Models.Pet", b => - { - b.Navigation("Resources") - .IsRequired(); - - b.Navigation("Stats") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Migrations/20250201173643_Initial.cs b/Migrations/20250201173643_Initial.cs deleted file mode 100644 index be951d9..0000000 --- a/Migrations/20250201173643_Initial.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace PetCompanion.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Pets", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - Class = table.Column(type: "INTEGER", nullable: false), - Level = table.Column(type: "INTEGER", nullable: false), - UserId = table.Column(type: "TEXT", nullable: false), - IsDead = table.Column(type: "INTEGER", nullable: false), - 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) - }, - constraints: table => - { - table.PrimaryKey("PK_Pets", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "PetStats", - columns: table => new - { - PetId = table.Column(type: "TEXT", nullable: false), - Intelligence = table.Column(type: "INTEGER", nullable: false), - Strength = table.Column(type: "INTEGER", nullable: false), - Charisma = table.Column(type: "INTEGER", nullable: false), - MaxIntelligence = table.Column(type: "INTEGER", nullable: false), - MaxStrength = table.Column(type: "INTEGER", nullable: false), - MaxCharisma = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_PetStats", x => x.PetId); - table.ForeignKey( - name: "FK_PetStats_Pets_PetId", - column: x => x.PetId, - principalTable: "Pets", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "Resources", - columns: table => new - { - PetId = table.Column(type: "TEXT", nullable: false), - Wisdom = table.Column(type: "INTEGER", nullable: false), - Gold = table.Column(type: "INTEGER", nullable: false), - Food = table.Column(type: "INTEGER", nullable: false), - Junk = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Resources", x => x.PetId); - table.ForeignKey( - name: "FK_Resources_Pets_PetId", - column: x => x.PetId, - principalTable: "Pets", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "PetStats"); - - migrationBuilder.DropTable( - name: "Resources"); - - migrationBuilder.DropTable( - name: "Pets"); - } - } -} diff --git a/Migrations/ApplicationDbContextModelSnapshot.cs b/Migrations/ApplicationDbContextModelSnapshot.cs deleted file mode 100644 index 3f11ec8..0000000 --- a/Migrations/ApplicationDbContextModelSnapshot.cs +++ /dev/null @@ -1,138 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using PetCompanion.Data; - -#nullable disable - -namespace PetCompanion.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - partial class ApplicationDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.1"); - - modelBuilder.Entity("PetCompanion.Models.Pet", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("BasicActionCooldown") - .HasColumnType("TEXT"); - - b.Property("Class") - .HasColumnType("INTEGER"); - - b.Property("GatherActionSince") - .HasColumnType("TEXT"); - - b.Property("IsDead") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("PetBasicAction") - .HasColumnType("INTEGER"); - - b.Property("PetGatherAction") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Pets"); - }); - - modelBuilder.Entity("PetCompanion.Models.PetStats", b => - { - b.Property("PetId") - .HasColumnType("TEXT"); - - b.Property("Charisma") - .HasColumnType("INTEGER"); - - b.Property("Intelligence") - .HasColumnType("INTEGER"); - - b.Property("MaxCharisma") - .HasColumnType("INTEGER"); - - b.Property("MaxIntelligence") - .HasColumnType("INTEGER"); - - b.Property("MaxStrength") - .HasColumnType("INTEGER"); - - b.Property("Strength") - .HasColumnType("INTEGER"); - - b.HasKey("PetId"); - - b.ToTable("PetStats"); - }); - - modelBuilder.Entity("PetCompanion.Models.Resources", b => - { - b.Property("PetId") - .HasColumnType("TEXT"); - - b.Property("Food") - .HasColumnType("INTEGER"); - - b.Property("Gold") - .HasColumnType("INTEGER"); - - b.Property("Junk") - .HasColumnType("INTEGER"); - - b.Property("Wisdom") - .HasColumnType("INTEGER"); - - b.HasKey("PetId"); - - b.ToTable("Resources"); - }); - - modelBuilder.Entity("PetCompanion.Models.PetStats", b => - { - b.HasOne("PetCompanion.Models.Pet", null) - .WithOne("Stats") - .HasForeignKey("PetCompanion.Models.PetStats", "PetId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("PetCompanion.Models.Resources", b => - { - b.HasOne("PetCompanion.Models.Pet", null) - .WithOne("Resources") - .HasForeignKey("PetCompanion.Models.Resources", "PetId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("PetCompanion.Models.Pet", b => - { - b.Navigation("Resources") - .IsRequired(); - - b.Navigation("Stats") - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Models/Enums/ItemEnums.cs b/Models/Enums/ItemEnums.cs deleted file mode 100644 index d09e729..0000000 --- a/Models/Enums/ItemEnums.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace PetCompanion.Models.Enums -{ - public enum ItemType - { - Consumable, - Equipment, - Material - } - - public enum ItemRarity - { - Common, - Uncommon, - Rare, - Legendary - } - - public enum EquipmentSlot - { - Helmet, - Chest, - Leggings, - Weapon - } -} diff --git a/Models/GameItems.cs b/Models/GameItems.cs deleted file mode 100644 index 552baac..0000000 --- a/Models/GameItems.cs +++ /dev/null @@ -1,79 +0,0 @@ -using PetCompanion.Models.Enums; - -namespace PetCompanion.Models -{ - public class MaterialItem : Item - { - public MaterialItem() - { - Type = ItemType.Material; - } - - public override void Use(ref Pet pet) - { - // Materials cannot be used directly - } - } - - public class StatBoostEquipment : EquipableItem - { - public Dictionary StatBoosts { get; set; } = new(); - - public override void Equip(ref Pet pet) - { - base.Equip(ref pet); - if (IsEquipped) - { - foreach (var boost in StatBoosts) - { - switch (boost.Key.ToLower()) - { - case "health": - pet.MaxHealth += boost.Value; - break; - case "intelligence": - pet.Stats.MaxIntelligence += boost.Value; - break; - case "strength": - pet.Stats.MaxStrength += boost.Value; - break; - case "charisma": - pet.Stats.MaxCharisma += boost.Value; - break; - } - } - } - } - - public override void Unequip(ref Pet pet) - { - if (IsEquipped) - { - foreach (var boost in StatBoosts) - { - switch (boost.Key.ToLower()) - { - case "health": - pet.MaxHealth -= boost.Value; - break; - case "intelligence": - pet.Stats.MaxIntelligence -= boost.Value; - break; - case "strength": - pet.Stats.MaxStrength -= boost.Value; - break; - case "charisma": - pet.Stats.MaxCharisma -= boost.Value; - break; - } - } - } - base.Unequip(ref pet); - } - - public override void Use(ref Pet pet) - { - Equip(ref pet); - } - } -} diff --git a/Models/Inventory.cs b/Models/Inventory.cs deleted file mode 100644 index dac9680..0000000 --- a/Models/Inventory.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace PetCompanion.Models -{ - public class Inventory - { - public List Items { get; set; } = new(); - public int Capacity { get; set; } - - public bool AddItem(Item item) - { - if (Items.Count < Capacity) - { - Items.Add(item); - return true; - } - return false; - } - - public bool RemoveItem(Item item) - { - return Items.Remove(item); - } - - public void TrashItem(Item item) - { - RemoveItem(item); - } - } -} diff --git a/Models/Item.cs b/Models/Item.cs deleted file mode 100644 index e481db4..0000000 --- a/Models/Item.cs +++ /dev/null @@ -1,50 +0,0 @@ -using PetCompanion.Models.Enums; - -namespace PetCompanion.Models -{ - public abstract class Item - { - public string Id { get; set; } - public string Name { get; set; } - public string Description { get; set; } - public ItemType Type { get; set; } - public ItemRarity Rarity { get; set; } - - public abstract void Use(ref Pet pet); - } - - public abstract class EquipableItem : Item - { - public EquipmentSlot Slot { get; set; } - public bool IsEquipped { get; set; } - - public virtual void Equip(ref Pet pet) - { - if (!IsEquipped) - { - pet.Equipment[Slot]?.Unequip(ref pet); - pet.Equipment[Slot] = this; - IsEquipped = true; - } - } - - public virtual void Unequip(ref Pet pet) - { - if (IsEquipped) - { - pet.Equipment[Slot] = null; - IsEquipped = false; - } - } - } - - public class ConsumableItem : Item - { - public Action Effect { get; set; } - - public override void Use(ref Pet pet) - { - Effect?.Invoke(pet); - } - } -} diff --git a/Models/Pet.cs b/Models/Pet.cs index d36b353..14430b3 100644 --- a/Models/Pet.cs +++ b/Models/Pet.cs @@ -27,6 +27,9 @@ namespace PetCompanion.Models public Dictionary Equipment { get; set; } = new(); + public int SkillPoints { get; set; } = 2; + public virtual ICollection Skills { get; set; } = new List(); + public Pet() { foreach (EquipmentSlot slot in Enum.GetValues(typeof(EquipmentSlot))) diff --git a/Models/PetStats.cs b/Models/PetStats.cs index 1ef94f2..2df7a4c 100644 --- a/Models/PetStats.cs +++ b/Models/PetStats.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; namespace PetCompanion.Models { @@ -7,10 +8,20 @@ namespace PetCompanion.Models { [Key, ForeignKey("Pet")] public string PetId { get; set; } + + // Display stats public int Intelligence { get; set; } public int Strength { get; set; } public int Charisma { get; set; } + // Hidden stats (not displayed to the API) + [JsonIgnore] + public int Luck { get; set; } // used for rarity of item finds + [JsonIgnore] + public int Agility { get; set; } // used for combat + [JsonIgnore] + public int Perception { get; set; } // used for number of itens found + public int MaxIntelligence { get; set; } public int MaxStrength { get; set; } public int MaxCharisma { get; set; } @@ -67,6 +78,10 @@ namespace PetCompanion.Models stats.MaxStrength = stats.Strength; stats.MaxCharisma = stats.Charisma; + stats.Luck = 1; + stats.Agility = 1; + stats.Perception = 1; + return stats; } } diff --git a/Models/Skill.cs b/Models/Skill.cs new file mode 100644 index 0000000..f7f79c4 --- /dev/null +++ b/Models/Skill.cs @@ -0,0 +1,82 @@ +using System.ComponentModel.DataAnnotations; + +namespace PetCompanion.Models +{ + public enum SkillType + { + GROUND, + GRAND + } + + public enum SkillTier + { + I = 1, + II = 2, + III = 3 + } + + public class Skill + { + [Key] + public int Id { get; set; } + 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 Effects { get; set; } + public virtual ICollection PetSkills { get; set; } + } + + public class SkillEffect + { + [Key] + public int Id { get; set; } + public int SkillId { get; set; } + public virtual Skill Skill { get; set; } + public SkillTier Tier { get; set; } + public string Effect { get; set; } + public decimal Value { get; set; } + + public void PetSkillUpgrade(ref Pet pet) + { + switch (Effect) + { + case "MaxHealth": + pet.MaxHealth += (int)Value; + break; + case "MaxIntelligence": + pet.Stats.MaxIntelligence += (int)Value; + break; + case "MaxStrength": + pet.Stats.MaxStrength += (int)Value; + break; + case "MaxCharisma": + pet.Stats.MaxCharisma += (int)Value; + break; + case "Luck": + pet.Stats.Luck += (int)Value; + break; + case "Agility": + pet.Stats.Agility += (int)Value; + break; + case "Perception": + pet.Stats.Perception += (int)Value; + break; + // Add more effects as needed + } + } + } + + public class PetSkill + { + [Key] + public int Id { get; set; } + public string PetId { get; set; } + public virtual Pet Pet { get; set; } + public int SkillId { get; set; } + public virtual Skill Skill { get; set; } + public SkillTier CurrentTier { get; set; } + } +} diff --git a/Program.cs b/Program.cs index b328482..3d9b3e7 100644 --- a/Program.cs +++ b/Program.cs @@ -28,6 +28,7 @@ namespace PetCompanion builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Repositories/ItemRepository.cs b/Repositories/ItemRepository.cs deleted file mode 100644 index 8c7056a..0000000 --- a/Repositories/ItemRepository.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System.Text.RegularExpressions; -using PetCompanion.Models; -using PetCompanion.Models.Enums; - -namespace PetCompanion.Repositories -{ - public class ItemRepository - { - private readonly Dictionary _itemTemplates = new(); - private readonly Dictionary> _itemsByRarity = new(); - private readonly Random _random = new(); - - public ItemRepository() - { - foreach (ItemRarity rarity in Enum.GetValues(typeof(ItemRarity))) - { - _itemsByRarity[rarity] = new List(); - } - LoadItemsFromCsv(); - } - - private void LoadItemsFromCsv() - { - var lines = File.ReadAllLines("Items.csv").Skip(1); // Skip header - foreach (var line in lines) - { - var parts = line.Split(','); - var name = parts[0]; - var type = Enum.Parse(parts[1]); - var rarity = Enum.Parse(parts[2]); - var description = parts[3]; - - Item item = type switch - { - ItemType.Consumable => CreateConsumableItem(name, description, rarity), - ItemType.Equipment => CreateEquipmentItem(name, description, rarity), - ItemType.Material => CreateMaterialItem(name, description, rarity), - _ => throw new ArgumentException($"Unknown item type: {type}") - }; - - _itemTemplates.Add(name, item); - _itemsByRarity[rarity].Add(name); - } - } - - private Item CreateConsumableItem(string name, string description, ItemRarity rarity) - { - var item = new ConsumableItem - { - Id = Guid.NewGuid().ToString(), - Name = name, - Description = description, - Type = ItemType.Consumable, - Rarity = rarity - }; - - // Parse effects from description - if (description.Contains("food resources")) - { - var foodAmount = int.Parse(Regex.Match(description, @"\+(\d+) food").Groups[1].Value); - item.Effect = (pet) => pet.Resources.Food += foodAmount; - } - else if (description.Contains("Intelligence")) - { - var amount = int.Parse(Regex.Match(description, @"\+(\d+)").Groups[1].Value); - item.Effect = (pet) => pet.IncrementIntelligence(amount); - } - // Add more effect parsing as needed - - return item; - } - - private Item CreateEquipmentItem(string name, string description, ItemRarity rarity) - { - var equipment = new StatBoostEquipment - { - Id = Guid.NewGuid().ToString(), - Name = name, - Description = description, - Type = ItemType.Equipment, - Rarity = rarity - }; - - // Parse slot and stats from description - var slotMatch = Regex.Match(description, @"(Helmet|Chest|Legging|Weapon):"); - if (slotMatch.Success) - { - equipment.Slot = Enum.Parse(slotMatch.Groups[1].Value); - } - - var statMatch = Regex.Match(description, @"\+(\d+) Max (\w+)"); - if (statMatch.Success) - { - var amount = int.Parse(statMatch.Groups[1].Value); - var stat = statMatch.Groups[2].Value; - equipment.StatBoosts.Add(stat, amount); - } - - return equipment; - } - - private Item CreateMaterialItem(string name, string description, ItemRarity rarity) - { - return new MaterialItem - { - Id = Guid.NewGuid().ToString(), - Name = name, - Description = description, - Type = ItemType.Material, - Rarity = rarity - }; - } - - public Item CreateItem(string itemName) - { - if (_itemTemplates.TryGetValue(itemName, out var template)) - { - // Create a new instance with a new ID but same properties - var json = System.Text.Json.JsonSerializer.Serialize(template); - var newItem = System.Text.Json.JsonSerializer.Deserialize(json); - newItem.Id = Guid.NewGuid().ToString(); - return newItem; - } - - throw new KeyNotFoundException($"Item template not found: {itemName}"); - } - - /// Usage: - /* var rarityProbabilities = new Dictionary - { - { ItemRarity.Common, 50 }, // 50% chance - { ItemRarity.Uncommon, 30 }, // 30% chance - { ItemRarity.Rare, 15 }, // 15% chance - { ItemRarity.Legendary, 5 } // 5% chance - }; - - var itemRepo = new ItemRepository(); - var randomItem = itemRepo.GenerateRandomItem(rarityProbabilities); - */ - public Item GenerateRandomItem(Dictionary rarityProbabilities) - { - // Validate probabilities - if (rarityProbabilities.Values.Sum() != 100) - { - throw new ArgumentException("Rarity probabilities must sum to 100"); - } - - // Generate random number between 0 and 100 - var roll = _random.Next(1, 101); - var currentThreshold = 0; - - // Determine which rarity we hit - ItemRarity selectedRarity = ItemRarity.Common; - foreach (var probability in rarityProbabilities) - { - currentThreshold += probability.Value; - if (roll <= currentThreshold) - { - selectedRarity = probability.Key; - break; - } - } - - // Get random item from selected rarity - var possibleItems = _itemsByRarity[selectedRarity]; - if (possibleItems.Count == 0) - { - // Fallback to common if no items of selected rarity - possibleItems = _itemsByRarity[ItemRarity.Common]; - } - - var randomItemName = possibleItems[_random.Next(possibleItems.Count)]; - return CreateItem(randomItemName); - } - } -} diff --git a/Services/SkillService.cs b/Services/SkillService.cs new file mode 100644 index 0000000..cc88eaa --- /dev/null +++ b/Services/SkillService.cs @@ -0,0 +1,76 @@ +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; + + public SkillService(ApplicationDbContext context, PetRepository petRepository) + { + this.context = context; + this.petRepository = petRepository; + } + + public async Task AllocateSkillPoint(string petId, string userId, int skillId) + { + var pet = await context.Pets + .Include(p => p.Skills) + .FirstOrDefaultAsync(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 = await context.Skills + .Include(s => s.Effects) + .FirstOrDefaultAsync(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); + } + else + { + var newSkill = new PetSkill + { + PetId = petId, + SkillId = skillId, + CurrentTier = SkillTier.I + }; + pet.Skills.Add(newSkill); + var effect = skill.Effects.FirstOrDefault(e => e.Tier == SkillTier.I); + if (effect != null) + effect.PetSkillUpgrade(ref pet); + } + + pet.SkillPoints--; + await context.SaveChangesAsync(); + + return pet.Skills.First(ps => ps.SkillId == skillId); + } + + public async Task> GetAvailableSkills() + { + return await context.Skills + .Include(s => s.Effects) + .ToListAsync(); + } + } +}