Refactor skill allocation to upgrade; implement skill requirements and resource checks; remove experience from Pet model; update README for skill tree progress

This commit is contained in:
Jose Henrique 2025-02-08 22:46:19 -03:00
parent f553196ca0
commit 653cc451d2
13 changed files with 723 additions and 355 deletions

@ -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)

@ -51,6 +51,11 @@ namespace PetCompanion.Data
.WithMany(s => s.Effects)
.HasForeignKey(se => se.SkillId);
modelBuilder.Entity<SkillRequirement>()
.HasOne(se => se.Skill)
.WithMany(s => s.SkillRequirements)
.HasForeignKey(se => se.SkillId);
modelBuilder.Entity<EquippedItem>()
.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<Skill>().HasData(skill);
}
foreach (var skill in skills)
{
foreach (var effect in skill.Effects)
{
modelBuilder.Entity<SkillEffect>().HasData(effect);
}
}
modelBuilder.Entity<Skill>().HasData(skills);
modelBuilder.Entity<SkillRequirement>().HasData(requirements);
modelBuilder.Entity<SkillEffect>().HasData(effects);
}
}
}

@ -4,7 +4,7 @@ namespace PetCompanion.Data
{
public static class SkillsData
{
public static IEnumerable<Skill> GetInitialSkills()
public static IEnumerable<Skill> GetInitialSkillsWithoutRelations()
{
return new List<Skill>
{
@ -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<SkillEffect>
{
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<SkillEffect>
{
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<SkillEffect>
{
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<SkillEffect>
{
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<int> { 4 },
Effects = new List<SkillEffect>
{
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<int> { 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<int> { 3 },
Effects = new List<SkillEffect>
{
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<int> { 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<int> { 2 },
Effects = new List<SkillEffect>
{
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<int> { 2 }
},
new Skill
{
Id = 8,
Name = "Resourcefulness",
Description = "Increases the amount of resources gathered by your pet.",
Type = SkillType.GRAND,
Icon = "📦",
SkillsIdRequired = new List<int> { 5, 6, 7 }
}
};
}
public static IEnumerable<SkillRequirement> GetInitialSkillRequirements()
{
return new List<SkillRequirement>
{
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<SkillEffect> GetInitialSkillEffects()
{
return new List<SkillEffect>
{
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
}
};
}

@ -11,7 +11,7 @@ using PetCompanion.Data;
namespace PetCompanion.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250204213845_InitialCreate")]
[Migration("20250209011029_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
@ -108,9 +108,6 @@ namespace PetCompanion.Migrations
b.Property<int>("Class")
.HasColumnType("INTEGER");
b.Property<int>("Experience")
.HasColumnType("INTEGER");
b.Property<DateTime>("GatherActionSince")
.HasColumnType("TEXT");
@ -136,9 +133,6 @@ namespace PetCompanion.Migrations
b.Property<int>("PetGatherAction")
.HasColumnType("INTEGER");
b.Property<int>("SkillPoints")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
@ -250,9 +244,6 @@ namespace PetCompanion.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("PointsCost")
.HasColumnType("INTEGER");
b.PrimitiveCollection<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Cost")
.HasColumnType("INTEGER");
b.Property<string>("Resource")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("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
}

@ -40,7 +40,6 @@ namespace PetCompanion.Migrations
Name = table.Column<string>(type: "TEXT", nullable: false),
Class = table.Column<int>(type: "INTEGER", nullable: false),
Level = table.Column<int>(type: "INTEGER", nullable: false),
Experience = table.Column<int>(type: "INTEGER", nullable: false),
Health = table.Column<int>(type: "INTEGER", nullable: false),
MaxHealth = table.Column<int>(type: "INTEGER", nullable: false),
UserId = table.Column<string>(type: "TEXT", nullable: false),
@ -48,8 +47,7 @@ namespace PetCompanion.Migrations
PetGatherAction = table.Column<int>(type: "INTEGER", nullable: false),
GatherActionSince = table.Column<DateTime>(type: "TEXT", nullable: false),
PetBasicAction = table.Column<int>(type: "INTEGER", nullable: false),
BasicActionCooldown = table.Column<DateTime>(type: "TEXT", nullable: false),
SkillPoints = table.Column<int>(type: "INTEGER", nullable: false)
BasicActionCooldown = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
@ -65,7 +63,6 @@ namespace PetCompanion.Migrations
Name = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: false),
Type = table.Column<int>(type: "INTEGER", nullable: false),
PointsCost = table.Column<int>(type: "INTEGER", nullable: false),
Icon = table.Column<string>(type: "TEXT", nullable: false),
SkillsIdRequired = table.Column<string>(type: "TEXT", nullable: true)
},
@ -216,18 +213,40 @@ namespace PetCompanion.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "SkillRequirement",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
SkillId = table.Column<int>(type: "INTEGER", nullable: false),
Resource = table.Column<string>(type: "TEXT", nullable: false),
Cost = table.Column<int>(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");
}
/// <inheritdoc />
@ -305,6 +353,9 @@ namespace PetCompanion.Migrations
migrationBuilder.DropTable(
name: "SkillEffects");
migrationBuilder.DropTable(
name: "SkillRequirement");
migrationBuilder.DropTable(
name: "GameItems");

@ -105,9 +105,6 @@ namespace PetCompanion.Migrations
b.Property<int>("Class")
.HasColumnType("INTEGER");
b.Property<int>("Experience")
.HasColumnType("INTEGER");
b.Property<DateTime>("GatherActionSince")
.HasColumnType("TEXT");
@ -133,9 +130,6 @@ namespace PetCompanion.Migrations
b.Property<int>("PetGatherAction")
.HasColumnType("INTEGER");
b.Property<int>("SkillPoints")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
@ -247,9 +241,6 @@ namespace PetCompanion.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("PointsCost")
.HasColumnType("INTEGER");
b.PrimitiveCollection<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Cost")
.HasColumnType("INTEGER");
b.Property<string>("Resource")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("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
}

@ -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<PetSkill> Skills { get; set; } = new List<PetSkill>();
public virtual Inventory Inventory { get; set; }
public virtual ICollection<EquippedItem> EquippedItemsList { get; set; } = new List<EquippedItem>();

@ -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<int>? SkillsIdRequired { get; set; }
public virtual ICollection<SkillRequirement> SkillRequirements { get; set; }
public virtual ICollection<SkillEffect> Effects { get; set; }
[JsonIgnore]
public virtual ICollection<PetSkill> 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; }

@ -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)

@ -13,10 +13,19 @@ namespace PetCompanion.Repositories
_context = context;
}
public IEnumerable<Skill> 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);
}

@ -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,

@ -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;

@ -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<Skill> GetAvailableSkills()
{
return _context.Skills
.Include(s => s.Effects)
.ToList();
return _petSkillRepository.GetAvailableSkills();
}
}
}