Add networked Fighter3D minigame

This commit is contained in:
ud18010
2026-08-10 16:47:16 +08:00
parent bff617091d
commit ef7f24738e
53 changed files with 5458 additions and 19 deletions
@@ -0,0 +1,109 @@
using Fighter3D.Core;
using XWorld.Framework;
namespace XWorld.Server.Host.Tests
{
public class Fighter3DLogicTests
{
private static FighterState NewState()
{
return new FighterLogic().CreateInitial(
new RoomConfig { GameId = "fighter3d", Version = 1, Seed = 1, PlayerCount = 2 },
new DeterministicRandom(1));
}
[Fact]
public void InitialState_StartsTwoFightersMirrored()
{
var state = NewState();
Assert.Equal(FighterPhase.Running, state.Phase);
Assert.Equal(1000, state.Fighters[0].Hp);
Assert.Equal(1000, state.Fighters[1].Hp);
Assert.True(state.Fighters[0].X < state.Fighters[1].X);
Assert.Equal(1, state.Fighters[0].Facing);
Assert.Equal(-1, state.Fighters[1].Facing);
}
[Fact]
public void Movement_ClampsToStageBounds()
{
var logic = new FighterLogic();
var state = NewState();
state = logic.Step(state, FighterInput.Controls(0, -1, FighterButtons.None), null).State;
for (int i = 0; i < 600; i++)
state = logic.Step(state, FighterInput.Tick(1f / FighterLogic.TickRate), null).State;
Assert.Equal(-FighterLogic.StageHalfWidth, state.Fighters[0].X, 3);
}
[Fact]
public void LightAttack_HitsOpponentAndAppliesStun()
{
var logic = new FighterLogic();
var state = NewState();
state.Fighters[0].X = -0.4f;
state.Fighters[1].X = 0.4f;
state = logic.Step(state, FighterInput.Controls(0, 0, FighterButtons.Light), null).State;
for (int i = 0; i < 10; i++)
state = logic.Step(state, FighterInput.Tick(1f / FighterLogic.TickRate), null).State;
Assert.True(state.Fighters[1].Hp < 1000);
Assert.Equal(FighterAction.HitStun, state.Fighters[1].Action);
}
[Fact]
public void HoldingAway_BlocksIncomingAttack()
{
var logic = new FighterLogic();
var state = NewState();
state.Fighters[0].X = -0.4f;
state.Fighters[1].X = 0.4f;
state = logic.Step(state, FighterInput.Controls(1, 1, FighterButtons.None), null).State;
state = logic.Step(state, FighterInput.Controls(0, 0, FighterButtons.Light), null).State;
for (int i = 0; i < 10; i++)
state = logic.Step(state, FighterInput.Tick(1f / FighterLogic.TickRate), null).State;
Assert.Equal(1000, state.Fighters[1].Hp);
Assert.Equal(FighterAction.BlockStun, state.Fighters[1].Action);
}
[Fact]
public void Timeout_WinnerIsHigherHealthSeat()
{
var logic = new FighterLogic();
var state = NewState();
state.Fighters[1].Hp = 900;
state = logic.Step(state, FighterInput.Tick(FighterLogic.RoundSeconds), null).State;
Assert.Equal(FighterPhase.Finished, state.Phase);
Assert.Equal(0, state.WinnerSeat);
}
[Fact]
public void EncodeDecode_RoundTripsSnapshot()
{
var logic = new FighterLogic();
var state = NewState();
state.Tick = 123;
state.ElapsedSeconds = 2.5f;
state.Fighters[0].Hp = 777;
state.Fighters[1].Action = FighterAction.BlockStun;
state.Fighters[1].ActionFrame = 6;
state.WinnerSeat = 1;
var back = logic.Decode(logic.Encode(state));
Assert.Equal(123, back.Tick);
Assert.Equal(2.5f, back.ElapsedSeconds, 3);
Assert.Equal(777, back.Fighters[0].Hp);
Assert.Equal(FighterAction.BlockStun, back.Fighters[1].Action);
Assert.Equal(6, back.Fighters[1].ActionFrame);
Assert.Equal(1, back.WinnerSeat);
}
}
}