aboutsummaryrefslogtreecommitdiff
path: root/integration_test.go
blob: 4525b18355826f0bc392b691af29c80ee0c75df9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

package main

import (
	"testing"
	"time"

	"age_of_empires/physics"

	"github.com/stretchr/testify/assert"
)

func TestCameraScreenToWorldRoundTrip(t *testing.T) {
	zooms := []float64{0.25, 0.5, 1.0, 2.0, 3.0}
	points := []physics.Point{
		{X: 1000, Y: 1000},
		{X: 0, Y: 0},
		{X: 3200, Y: 2400},
		{X: 1600, Y: 1200},
	}
	for _, zoom := range zooms {
		cam := Camera{X: 1600, Y: 1200, Zoom: zoom}
		for _, p := range points {
			sx, sy := cam.WorldToScreen(p)
			got := cam.ScreenToWorld(int(sx), int(sy))
			assert.Equal(t, p, got, "zoom=%.2f point=%v", zoom, p)
		}
	}
}

func TestCameraClampsToBounds(t *testing.T) {
	cases := []struct{ x, y float64 }{
		{-99999, -99999},
		{99999, 99999},
		{-99999, 99999},
		{99999, -99999},
	}
	for _, c := range cases {
		cam := Camera{X: c.x, Y: c.y, Zoom: 1.0}
		cam.clampToBounds()
		halfW := (ScreenWidth / 2.0) / cam.Zoom
		halfH := (ScreenHeight / 2.0) / cam.Zoom
		assert.GreaterOrEqual(t, cam.X, halfW, "X=%v should be >= halfW", c.x)
		assert.LessOrEqual(t, cam.X, float64(WorldWidth)-halfW, "X=%v should be <= maxX", c.x)
		assert.GreaterOrEqual(t, cam.Y, halfH, "Y=%v should be >= halfH", c.y)
		assert.LessOrEqual(t, cam.Y, float64(WorldHeight)-halfH, "Y=%v should be <= maxY", c.y)
	}
}

func TestCameraZoomTowardCursor(t *testing.T) {
	zooms := []struct{ before, after float64 }{
		{1.0, 2.0},
		{1.0, 0.5},
		{2.0, 3.0},
	}
	cursorSX, cursorSY := 400, 300
	for _, z := range zooms {
		cam := Camera{X: 1600, Y: 1200, Zoom: z.before}
		worldBefore := cam.ScreenToWorld(cursorSX, cursorSY)

		// replicate the zoom-toward-cursor logic from Camera.Update
		wx0 := float64(cursorSX-ScreenWidth/2)/cam.Zoom + cam.X
		wy0 := float64(cursorSY-ScreenHeight/2)/cam.Zoom + cam.Y
		cam.Zoom = z.after
		wx1 := float64(cursorSX-ScreenWidth/2)/cam.Zoom + cam.X
		wy1 := float64(cursorSY-ScreenHeight/2)/cam.Zoom + cam.Y
		cam.X -= wx1 - wx0
		cam.Y -= wy1 - wy0

		worldAfter := cam.ScreenToWorld(cursorSX, cursorSY)
		assert.Equal(t, worldBefore, worldAfter, "zoom %.1f→%.1f: world point under cursor should be stable", z.before, z.after)
	}
}

func TestGatherLoop(t *testing.T) {
	// Initialize Game
	game := &Game{}

	// Town Center (Storage) at -100,0 (different grid cell from unit at 0,0)
	tcBuilder := NewEntityBuilder().
		WithPosition(physics.Point{X: -100, Y: 0}).
		WithResourceStorage()
	_ = game.Append(tcBuilder.Build())

	// Mine (Source) at 0, 200
	mineBuilder := NewEntityBuilder().
		WithPosition(physics.Point{X: 0, Y: 200}).
		WithResourceSource(100)
	mineID := game.Append(mineBuilder.Build())

	// Unit (Gatherer) at 0, 0
	unitBuilder := NewEntityBuilder().
		WithPosition(physics.Point{X: 0, Y: 0}).
		WithMove().
		WithOrder().
		WithResourceGatherer(10)
	unitID := game.Append(unitBuilder.Build())

	// Set up simulated time
	simTime := time.Now()
	game.NowFunc = func() time.Time { return simTime }

	// Simulate user selection: select the unit
	game.GlobalSelection.Selected = append(game.GlobalSelection.Selected, unitID)

	// Simulate right-click on mine position to trigger Gather via MainAction
	minePosition := game.Position[mineID]
	MainAction(game, unitID, minePosition, game.getMoveMap())

	initialResources := game.ResourceAmount
	dt := 16 * time.Millisecond
	maxTicks := 100_000

	deposited := false

	for i := 0; i < maxTicks; i++ {
		simTime = simTime.Add(dt)
		game.Update()

		if game.ResourceAmount > initialResources {
			deposited = true
			break
		}
	}

	if !deposited {
		t.Errorf("Unit failed to gather resources within %d ticks. ResourceAmount: %d", maxTicks, game.ResourceAmount)
	}
}

func TestTwoEntitiesMovingToSameCellDontOverlap(t *testing.T) {
	// Two units close together both ordered to the same cell should not overlap.
	game := &Game{}

	target := physics.Point{X: 500, Y: 0}

	unitA := game.Append(NewEntityBuilder().
		WithPosition(physics.Point{X: 200, Y: 0}).
		WithMove().WithOrder().Build())
	unitB := game.Append(NewEntityBuilder().
		WithPosition(physics.Point{X: 100, Y: 0}).
		WithMove().WithOrder().Build())

	moveMap := game.getMoveMap()
	MainAction(game, unitA, target, moveMap)
	MainAction(game, unitB, target, moveMap)

	// MOVE_SPEED=10, cell=100 → 10 ticks per cell. B detours around A → ~60 ticks.
	for range 60 {
		game.Update()
	}

	assert.False(t, game.Move[unitA].IsActive, "Entity A should have stopped moving")
	assert.False(t, game.Move[unitB].IsActive, "Entity B should have stopped moving")
	assert.NotEqual(t, game.Position[unitA], game.Position[unitB],
		"Entity A and B should NOT overlap: A at %s, B at %s", game.Position[unitA], game.Position[unitB])
}