Skip to content
7 changes: 6 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

/ThirdRun.Tests/ # Unit and integration tests
/Content/ # Game assets (textures, fonts, sounds)
/doc/ # Documentation files (implementation guides, design docs)
```

## Game Description
Expand Down Expand Up @@ -78,4 +79,8 @@ Ce projet est un jeu de rôle en 2D où le groupe de personnages joue de manièr
- Use interfaces for testable components
- Implement proper dispose patterns for MonoGame resources
- Handle content loading asynchronously when possible
- Maintain clean separation between UI logic and game logic
- Maintain clean separation between UI logic and game logic

## Documentation
- All implementation documentation and design documents should be placed in the `/doc/` directory
- This includes feature implementation guides, architecture decisions, and technical specifications
5 changes: 3 additions & 2 deletions ThirdRun.Tests/AuraIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ public void HunterRegenerationBuff_WorksOnFriendlyTarget()

hunter.UpdateGameTime(10f);

// Act - Hunter should use regeneration on damaged warrior
hunter.UseAbilities();
// Get the regeneration ability and use it directly
var regenAbility = hunter.Abilities.First(a => a.Name == "Regeneration");
regenAbility.Use(hunter, warrior, 10f);

// Assert - Warrior should have regeneration aura
Assert.Single(warrior.ActiveAuras);
Expand Down
115 changes: 115 additions & 0 deletions ThirdRun.Tests/DungeonButtonTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using Xunit;
using ThirdRun.Data;
using MonogameRPG.Map;
using Microsoft.Xna.Framework;
using System.Linq;

namespace ThirdRun.Tests
{
public class DungeonButtonTests
{
[Fact]
public void UIManager_HasDungeonState()
{
// Verify the UIManager.State class has the new IsInDungeon property
var state = new ThirdRun.UI.UiManager.State();

Assert.False(state.IsInDungeon);
state.IsInDungeon = true;
Assert.True(state.IsInDungeon);
}

[Fact]
public void EnterDungeon_RequiresValidCharacters()
{
var random = new Random(12345);
var worldMap = new WorldMap(random);
worldMap.Initialize();

// Test with no characters
Assert.False(worldMap.IsInDungeon);
worldMap.EnterDungeon(); // Should not crash with no characters

// Test with characters
var characters = new[]
{
new Character("TestChar", CharacterClass.Guerrier, 100, 10, worldMap.CurrentMap, worldMap)
};

worldMap.SetCharacters(characters.ToList());
worldMap.EnterDungeon();

Assert.True(worldMap.IsInDungeon);
Assert.NotNull(worldMap.CurrentDungeon);
}

[Fact]
public void DungeonSystem_Integration_WorksCorrectly()
{
var random = new Random(12345);
var worldMap = new WorldMap(random);
worldMap.Initialize();

var characters = new[]
{
new Character("Warrior", CharacterClass.Guerrier, 100, 10, worldMap.CurrentMap, worldMap),
new Character("Mage", CharacterClass.Mage, 80, 8, worldMap.CurrentMap, worldMap)
};

// Give some experience to increase level
for (int i = 0; i < 15; i++)
{
characters[0].GainExperience(new MonogameRPG.Monsters.Monster(
new MonogameRPG.Monsters.MonsterType("TestMonster", 10, 5, "test", 2),
worldMap.CurrentMap, worldMap, random));
}

worldMap.SetCharacters(characters.ToList());

// Test full dungeon flow
Assert.False(worldMap.IsInDungeon);

// Enter dungeon
worldMap.EnterDungeon();
Assert.True(worldMap.IsInDungeon);
Assert.NotNull(worldMap.CurrentDungeon);

// Verify appropriate dungeon was selected
var meanLevel = (characters[0].Level + characters[1].Level) / 2;
Assert.True(worldMap.CurrentDungeon.IsAppropriateForLevel(meanLevel));

// Exit dungeon
worldMap.ExitDungeon();
Assert.False(worldMap.IsInDungeon);
Assert.Null(worldMap.CurrentDungeon);
}

[Fact]
public void ButtonsPanel_DungeonButton_Integration()
{
// Verify that the ButtonsPanel class exists and can be instantiated
// This tests the structural integrity of our button changes
var random = new Random(12345);
var worldMap = new WorldMap(random);
worldMap.Initialize();
var gameState = new GameState
{
Player = new ThirdRun.Data.Player(worldMap, random),
WorldMap = worldMap
};

// We can't actually instantiate the ButtonsPanel without MonoGame setup
// but we can verify the types and methods we need exist
Assert.NotNull(gameState.WorldMap);
Assert.NotNull(typeof(ThirdRun.UI.Panels.ButtonsPanel));

// Verify the WorldMap has the new dungeon methods
var worldMapType = typeof(WorldMap);
Assert.NotNull(worldMapType.GetMethod("EnterDungeon"));
Assert.NotNull(worldMapType.GetMethod("ExitDungeon"));
Assert.NotNull(worldMapType.GetProperty("IsInDungeon"));
Assert.NotNull(worldMapType.GetProperty("CurrentDungeon"));
}
}
}
138 changes: 138 additions & 0 deletions ThirdRun.Tests/DungeonSystemTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using System;
using System.Linq;
using Xunit;
using ThirdRun.Data.Dungeons;
using MonogameRPG.Map;
using Microsoft.Xna.Framework;

namespace ThirdRun.Tests
{
public class DungeonSystemTests
{
[Fact]
public void DungeonRepository_HasCorrectLevelRanges()
{
var dungeons = DungeonRepository.GetAllDungeons();

Assert.True(dungeons.Count >= 5);

// Check that level ranges don't overlap
var sortedDungeons = dungeons.OrderBy(d => d.MinLevel).ToList();
for (int i = 0; i < sortedDungeons.Count - 1; i++)
{
Assert.True(sortedDungeons[i].MaxLevel < sortedDungeons[i + 1].MinLevel,
$"Dungeon level ranges overlap: {sortedDungeons[i].Name} (max {sortedDungeons[i].MaxLevel}) and {sortedDungeons[i + 1].Name} (min {sortedDungeons[i + 1].MinLevel})");
}
}

[Fact]
public void DungeonRepository_FindsDungeonForLevel()
{
var level1Dungeon = DungeonRepository.GetDungeonForLevel(1);
Assert.NotNull(level1Dungeon);
Assert.True(level1Dungeon.IsAppropriateForLevel(1));

var level5Dungeon = DungeonRepository.GetDungeonForLevel(5);
Assert.NotNull(level5Dungeon);
Assert.True(level5Dungeon.IsAppropriateForLevel(5));

var level10Dungeon = DungeonRepository.GetDungeonForLevel(10);
Assert.NotNull(level10Dungeon);
Assert.True(level10Dungeon.IsAppropriateForLevel(10));
}

[Fact]
public void Dungeon_HasBossOnFinalMap()
{
var dungeons = DungeonRepository.GetAllDungeons();

foreach (var dungeon in dungeons)
{
Assert.True(dungeon.Maps.Count > 0, $"Dungeon {dungeon.Name} should have at least one map");

var finalMap = dungeon.Maps.Last();
Assert.True(finalMap.HasBoss, $"Final map of dungeon {dungeon.Name} should have a boss");
}
}

[Fact]
public void WorldMap_EnterDungeon_CreatesAppropriateLevel()
{
var random = new Random(12345);
var worldMap = new WorldMap(random);
worldMap.Initialize();

// Create characters at different levels
var characters = new[]
{
new Character("Char1", CharacterClass.Guerrier, 100, 10, worldMap.CurrentMap, worldMap),
new Character("Char2", CharacterClass.Mage, 80, 8, worldMap.CurrentMap, worldMap),
new Character("Char3", CharacterClass.Prêtre, 90, 9, worldMap.CurrentMap, worldMap),
new Character("Char4", CharacterClass.Chasseur, 85, 7, worldMap.CurrentMap, worldMap)
};

// Give different experience levels
for (int i = 0; i < 10; i++)
{
characters[0].GainExperience(new MonogameRPG.Monsters.Monster(new MonogameRPG.Monsters.MonsterType("TestMonster", 10, 5, "test", 1), worldMap.CurrentMap, worldMap, random));
}

worldMap.SetCharacters(characters.ToList());

Assert.False(worldMap.IsInDungeon);

worldMap.EnterDungeon();

Assert.True(worldMap.IsInDungeon);
Assert.NotNull(worldMap.CurrentDungeon);
}

[Fact]
public void WorldMap_ExitDungeon_ReturnsToNormalMap()
{
var random = new Random(12345);
var worldMap = new WorldMap(random);
worldMap.Initialize();

var characters = new[]
{
new Character("Char1", CharacterClass.Guerrier, 100, 10, worldMap.CurrentMap, worldMap)
};

worldMap.SetCharacters(characters.ToList());

var originalPosition = worldMap.CurrentMapPosition;

worldMap.EnterDungeon();
Assert.True(worldMap.IsInDungeon);

worldMap.ExitDungeon();
Assert.False(worldMap.IsInDungeon);
Assert.Null(worldMap.CurrentDungeon);
Assert.Equal(originalPosition, worldMap.CurrentMapPosition);
}

[Fact]
public void WorldMap_CannotEnterDungeonFromTown()
{
var random = new Random(12345);
var worldMap = new WorldMap(random);
worldMap.Initialize();

var characters = new[]
{
new Character("Char1", CharacterClass.Guerrier, 100, 10, worldMap.CurrentMap, worldMap)
};

worldMap.SetCharacters(characters.ToList());

// Enter town mode
worldMap.ToggleTownMode();
Assert.True(worldMap.IsInTown);

// Try to enter dungeon - should fail
worldMap.EnterDungeon();
Assert.False(worldMap.IsInDungeon);
}
}
}
Loading
Loading