-
-
Notifications
You must be signed in to change notification settings - Fork 72
Expand file tree
/
Copy pathCpCourseGenerator.lua
More file actions
163 lines (147 loc) · 6.95 KB
/
CpCourseGenerator.lua
File metadata and controls
163 lines (147 loc) · 6.95 KB
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
157
158
159
160
161
162
163
--- Specialization implementing the course generator related functionality
---
---@class CpCourseGenerator
CpCourseGenerator = {}
CpCourseGenerator.MOD_NAME = g_currentModName
CpCourseGenerator.NAME = ".cpCourseGenerator"
CpCourseGenerator.SPEC_NAME = CpCourseGenerator.MOD_NAME .. CpCourseGenerator.NAME
function CpCourseGenerator.register(typeManager,typeName,specializations)
if CpCourseGenerator.prerequisitesPresent(specializations) then
typeManager:addSpecialization(typeName, CpCourseGenerator.SPEC_NAME)
end
end
function CpCourseGenerator.prerequisitesPresent(specializations)
return SpecializationUtil.hasSpecialization(CpAIWorker, specializations)
end
function CpCourseGenerator.registerEventListeners(vehicleType)
SpecializationUtil.registerEventListener(vehicleType, "onUpdate", CpCourseGenerator)
SpecializationUtil.registerEventListener(vehicleType, "onLoad", CpCourseGenerator)
SpecializationUtil.registerEventListener(vehicleType, "onReadStream", CpCourseGenerator)
SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", CpCourseGenerator)
end
function CpCourseGenerator.registerFunctions(vehicleType)
SpecializationUtil.registerFunction(vehicleType, 'cpDetectFieldBoundary', CpCourseGenerator.cpDetectFieldBoundary)
SpecializationUtil.registerFunction(vehicleType, 'cpIsFieldBoundaryDetectionRunning', CpCourseGenerator.cpIsFieldBoundaryDetectionRunning)
SpecializationUtil.registerFunction(vehicleType, 'cpGetFieldPosition', CpCourseGenerator.cpGetFieldPosition)
SpecializationUtil.registerFunction(vehicleType, 'cpGetFieldPolygon', CpCourseGenerator.cpGetFieldPolygon)
SpecializationUtil.registerFunction(vehicleType, 'cpGetIslandPolygons', CpCourseGenerator.cpGetIslandPolygons)
SpecializationUtil.registerFunction(vehicleType, 'cpDrawFieldPolygon', CpCourseGenerator.cpDrawFieldPolygon)
end
-- shortcut to access the spec
function CpCourseGenerator.getSpec(self)
return self["spec_" .. CpCourseGenerator.SPEC_NAME]
end
function CpCourseGenerator:onLoad(savegame)
CpCourseGenerator.getSpec(self).logger = Logger(CpCourseGenerator.SPEC_NAME, nil, CpDebug.DBG_COURSES)
-- make sure cpGetFieldPosition always has spec.position
CpCourseGenerator.getSpec(self).position = {}
end
---@param x number world X coordinate to start the detection at
---@param z number world Z coordinate to start the detection at
---@param object table|nil optional object with callback
---@param onFinishedFunc function callback function to call when finished: onFinishedFunc([object,] vehicle, fieldPolygon, islandPolygons)
function CpCourseGenerator:cpDetectFieldBoundary(x, z, object, onFinishedFunc)
local spec = CpCourseGenerator.getSpec(self)
if spec.isFieldBoundaryDetectionRunning then
spec.logger:warning(self, 'Not starting field boundary detection for %.1f/%.1f, previous for %.1f/%.1f is still running',
x, z, spec.position.x, spec.position.z)
return
end
spec.position = { x = x, z = z }
spec.object = object
spec.onFinishedFunc = onFinishedFunc
spec.fieldBoundaryDetector = FieldBoundaryDetector(x, z, self)
spec.isFieldBoundaryDetectionRunning = true
end
---@return boolean true if field boundary detection is running. Field and island polygons returned while running may
--- be nil or invalid.
function CpCourseGenerator:cpIsFieldBoundaryDetectionRunning()
return CpCourseGenerator.getSpec(self).isFieldBoundaryDetectionRunning
end
---@return number|nil, number|nil world X and Z coordinates of the last field boundary detection start position, nil
--- if no previous detection was started
function CpCourseGenerator:cpGetFieldPosition()
local spec = CpCourseGenerator.getSpec(self)
return spec.position.x, spec.position.z
end
function CpCourseGenerator:onUpdate(dt)
local spec = CpCourseGenerator.getSpec(self)
if spec.fieldBoundaryDetector then
if not spec.fieldBoundaryDetector:update(dt) then
-- done
spec.isFieldBoundaryDetectionRunning = false
spec.fieldPolygon = spec.fieldBoundaryDetector:getFieldPolygon()
spec.islandPolygons = spec.fieldBoundaryDetector:getIslandPolygons()
spec.fieldBoundaryDetector = nil
if spec.object and spec.onFinishedFunc then
spec.onFinishedFunc(spec.object, self, spec.fieldPolygon, spec.islandPolygons)
elseif spec.onFinishedFunc then
spec.onFinishedFunc(self, spec.fieldPolygon, spec.islandPolygons)
else
spec.logger:debug('Field boundary detection finished, but no callback given')
end
FieldPolygonChangedEvent.sendEvent(self)
end
end
end
---@return table|nil [{x, y, z}] field polygon with game vertices
function CpCourseGenerator:cpGetFieldPolygon()
return CpCourseGenerator.getSpec(self).fieldPolygon
end
---@return table|nil [[{x, y, z}]] array of island polygons with game vertices (x, y, z)
function CpCourseGenerator:cpGetIslandPolygons()
return CpCourseGenerator.getSpec(self).islandPolygons
end
-- For debug, if there is a field polygon or island polygons, draw them
function CpCourseGenerator:cpDrawFieldPolygon()
local spec= CpCourseGenerator.getSpec(self)
local function drawPolygon(polygon)
for i = 2, #polygon do
local p, n = polygon[i - 1], polygon[i]
Utils.renderTextAtWorldPosition(p.x, p.y + 1.2, p.z, tostring(i - 1), getCorrectTextSize(0.012), 0)
DebugUtil.drawDebugLine(p.x, p.y + 1, p.z, n.x, n.y + 1, n.z, 0, 1, 0)
end
end
if spec.fieldPolygon then
drawPolygon(spec.fieldPolygon)
end
if spec.islandPolygons then
for _, p in ipairs(spec.islandPolygons) do
drawPolygon(p)
end
end
end
function CpCourseGenerator:onReadStream(streamId, connection)
local spec= CpCourseGenerator.getSpec(self)
local numVertices = streamReadInt32(streamId)
if numVertices == 0 then
spec.fieldPolygon = nil
else
spec.fieldPolygon = {}
for _ = 1, numVertices do
local x = streamReadFloat32(streamId)
local y = streamReadFloat32(streamId)
local z = streamReadFloat32(streamId)
table.insert(spec.fieldPolygon, { x = x, y = y, z = z })
end
end
end
function CpCourseGenerator:onWriteStream(streamId, connection)
local spec= CpCourseGenerator.getSpec(self)
if spec.fieldPolygon then
streamWriteInt32(streamId, #spec.fieldPolygon)
for _, point in ipairs(spec.fieldPolygon) do
streamWriteFloat32(streamId, point.x)
if point.y ~= nil then
streamWriteFloat32(streamId, point.y)
else
streamWriteFloat32(streamId,
getTerrainHeightAtWorldPos(
g_currentMission.terrainRootNode, point.x, 1, point.z))
end
streamWriteFloat32(streamId, point.z)
end
else
streamWriteInt32(streamId, 0)
end
end