-
Notifications
You must be signed in to change notification settings - Fork 0
/
scenariohelper.lua
244 lines (211 loc) · 9.67 KB
/
scenariohelper.lua
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
-- This Source Code Form is subject to the terms of the bCDDL, v. 1.1.
-- If a copy of the bCDDL was not distributed with this
-- file, You can obtain one at http://beamng.com/bCDDL-1.1.txt
local M = {}
local logTag = 'scenarioHelper'
--sends the specified command to a vehicle of choice
local function queueLuaCommand(vehicle, command)
vehicle:queueLuaCommand(command)
end
--returns a BeamObject for given name
local function getVehicleByName(name)
return scenetree.findObject(name)
end
--sends the specified command to a vehicle via its name in the scenetree
local function queueLuaCommandByName(vehicleName, command)
local vehicle = getVehicleByName(vehicleName)
if vehicle then
queueLuaCommand(vehicle, command)
else
log('E', logTag, 'Failed - queueLuaCommandByName('..vehicleName..','..command..') - Vehicle not found.')
end
end
--breaks a single break group in a vehicle
local function breakBreakGroup(vehicleName, group)
queueLuaCommand(getVehicleByName(vehicleName), 'beamstate.breakBreakGroup("'..group..'")')
end
--trigger a deform group, switch to a broken material (ie:break a window)
local function triggerDeformGroup(vehicleName, group)
queueLuaCommand(getVehicleByName(vehicleName), 'beamstate.triggerDeformGroup("'..group..'")')
end
--enables tracking for the specified vehicle with a custom tracking name
local function trackVehicle(vehicleName, trackingName)
queueLuaCommandByName(vehicleName, 'mapmgr.enableTracking("'..trackingName..'")')
end
-- sets ai vehicle mode and optionaly specifies the target Vehicle from targetVehicleName
local function setAiMode(vehicleName, mode, targetVehicleName)
if targetVehicleName ~= nil then
local vehicle = getVehicleByName(targetVehicleName)
local vehicleID = vehicle.obj:getID()
queueLuaCommandByName(vehicleName, 'ai.setState({mode="'..mode..'", targetObjectID='..vehicleID..'})')
else
queueLuaCommandByName(vehicleName, 'ai.setState({mode="'..mode..'"})')
end
if scenario_scenarios then
scenario_scenarios.updateVehicleAiState(vehicleName, {mode = mode})
end
end
local function setAiAggression(vehicleName, aggression)
queueLuaCommandByName(vehicleName, 'ai.setAggression('..aggression..')')
if scenario_scenarios then
scenario_scenarios.updateVehicleAiState(vehicleName, {aggression = aggression})
end
end
-- dynamic Aggression mode for the AI manual mode
-- aggreMode = 'rubberBand', makes AI aggression vary with opponent distance.
local function setAiAggressionMode(vehicleName, aggrMode)
queueLuaCommandByName(vehicleName, 'ai.setAggressionMode("'..aggrMode..'")')
if scenario_scenarios then
scenario_scenarios.updateVehicleAiState(vehicleName, {aggressionMode = aggrMode})
end
end
local function setAiTarget(vehicleName, target)
queueLuaCommandByName(vehicleName, 'ai.setTarget("'..target..'")')
if scenario_scenarios then
scenario_scenarios.updateVehicleAiState(vehicleName, {target = target})
end
end
--enables automatic waypoint progression for the specified ai vehicle (mode needs to be manual or flee)
local function setAiRoute(vehicleName, waypoints)
queueLuaCommandByName(vehicleName, 'ai.driveUsingPath({wpTargetList = '..serialize(waypoints)..'})')
if scenario_scenarios then
scenario_scenarios.updateVehicleAiState(vehicleName, {waypoints = waypoints})
end
end
local function setCutOffDrivability(vehicleName, drivability)
queueLuaCommandByName(vehicleName, 'ai.setCutOffDrivability('..drivability..')')
if scenario_scenarios then
scenario_scenarios.updateVehicleAiState(vehicleName, {drivability = drivability})
end
end
local function setAiPath(arg)
--[[ USAGE
Function Arguments: arg -> a table with the following keys:
!!! Note: All arguments keys except "vehicleName" and "waypoints" are OPTIONAL !!!
-- vehicleName
The name of the vehicle to set as AI vehicle
-- waypoints (type: a list of strings. Required field)
The AI will figure out the shortest route between any consecutive waypoints entered in the list.
-- routeSpeed (type: number)
Speed in m/s this will apply to the entire route defined by "waypoints"
-- routeSpeedMode (type: string)
Options -> 'limit' / 'set'
defines whether the routeSpeed argument above will act as a limiter (AI will not exceed difined speed) or will be forced on the AI vehicle
-- driveInLane (type: string)
Options -> 'on' / 'off'
AI will drive in the appropriate side (lane) of the road in two way streets.
Currently only works correctly with bidirectional roads one lane in each direction.
-- lapCount (type: number)
Defines the number of laps a vehicle will do on a circuit.
In order for this to work the first and last waypoints in the "waypoints" list above should be the same (i.e. define a closed route).
-- aggression (type: number)
Acts as a multipliyer to the AI internal Aggression
e.g. if aggression here is set to 1 the AI actuall aggression will be 1 * 0.7 = 0.7.
The maximum value is 2.
-- aggressionMode (type: string)
Options -> 'rubberBand' (currently the only option)
will adjust the aggression of the AI depending on the AI distance from an opponent. The closer the AI to the opponent the less aggressive it will be
This option applies only to the AI manual mode. The chase and flee modes are set to a rubberBand aggression by default.
-- resetLearning (type: boolean. When not defined same as setting to false)
when set to true it will reset the learned acceleration envelopes (traction limits) of the vehicle
when the function is called. To be used when driving concistency is more important than performance
Example 1: AI vehicle will go from "scenario_wp01" to "scenario_wp02".
local arg = {vehicleName = aiInstance,
waypoints = {"scenario_wp01", "scenario_wp02"},
aggression = 1.2,
routeSpeed = 5,
routeSpeedMode = 'limit',
driveInLane = 'on',
resetLearning = true}
setAiPath(arg)
Example 2: AI vehicle will do 5 laps on the route "scenario_wp01" - ... - "scenario_wp01".
local arg = {vehicleName = aiInstance,
waypoints = {"scenario_wp01", "scenario_wp02", "scenario_wp03", "scenario_wp04", "scenario_wp01"},
lapCount = 5,
aggression = 1.2}
setAiPath(arg)
--]]
local vehicleName = arg.vehicleName
local waypoints = arg.waypoints
local routeSpeed = arg.routeSpeed or 0
local routeSpeedMode = arg.routeSpeedMode or 'off'
local driveInLane = arg.driveInLane or 'off'
local speeds = arg.speeds or {}
local lapCount = arg.lapCount or 0
local aggression = arg.aggression or 1
local aggressionMode = arg.aggressionMode or '' -- rubberBand or nil (aggression decreases with distance from opponent)
local resetLearning = arg.resetLearning and 'true' or 'false'
queueLuaCommandByName(vehicleName, 'ai.driveUsingPath({wpTargetList = '..serialize(waypoints)..', routeSpeed = '..routeSpeed..', routeSpeedMode = "'..routeSpeedMode..'", driveInLane = "'..driveInLane..'", wpSpeeds = '..serialize(speeds)..', noOfLaps = '..lapCount..', aggression = '..aggression..', aggressionMode = "'..aggressionMode..'", resetLearning = '..resetLearning..'})')
-- we need this stored somewhere so when we reset ai vehicles we can set this again
if core_checkpoints then
core_checkpoints.saveAIPath(vehicleName, arg)
end
if scenario_scenarios then
scenario_scenarios.updateVehicleAiState(vehicleName, {pathArgs = arg})
end
end
local function flashUiMessage(msg, duration, useBiggerText)
if useBiggerText ~= true then
useBiggerText = false
end
guihooks.trigger('ScenarioFlashMessage', {{msg, duration, 0, useBiggerText}} )
end
local function realTimeUiDisplay (msg)
guihooks.trigger('ScenarioRealtimeDisplay', {msg = msg} )
end
local function characterRealTimeUiDisplay(msg, img, context)
guihooks.trigger('CharacterRealtimeDisplay', {msg = msg, img = img, context = context})
end
local function clearCharacterRealTimeUiDisplay()
guihooks.trigger('CharacterRealtimeDisplay')
end
local function getDistanceBetweenSceneObjects(sceneObjectName1, sceneObjectName2)
local sceneObject1 = scenetree.findObject(sceneObjectName1)
local sceneObject2 = scenetree.findObject(sceneObjectName2)
if sceneObject1 and sceneObject2 then
return (sceneObject1:getPosition() - sceneObject2:getPosition()):len()
else
return -1
end
end
local function setAiLine(vehicleName, arg)
local nodes = arg['line']
local fauxPath = {}
local cling = arg['cling'] or 'true'
local z = 0
local speedList = {}
for idx, n in ipairs(nodes) do
local pos = vec3(n['pos'][1], n['pos'][2], 10000)
pos.z = z
local fauxNode = {
pos = pos,
radius = 0,
radiusOrig = 0,
}
table.insert(speedList, n['speed'])
table.insert(fauxPath, fauxNode)
end
queueLuaCommandByName(vehicleName, 'ai.driveUsingPath({script = '..serialize(fauxPath)..', wpSpeeds = '..serialize(speedList)..', routeSpeedMode="limit"})')
end
-- public interface
M.queueLuaCommand = queueLuaCommand
M.queueLuaCommandByName = queueLuaCommandByName
M.getVehicleByName = getVehicleByName
M.breakBreakGroup = breakBreakGroup
M.triggerDeformGroup = triggerDeformGroup
M.trackVehicle = trackVehicle
M.setAiMode = setAiMode
M.setAiAggression = setAiAggression
M.setAiAggressionMode = setAiAggressionMode
--M.setAiTargetVehicle = setAiTargetVehicle
M.setAiTarget = setAiTarget
M.setAiRoute = setAiRoute
M.setAiPath = setAiPath
M.setCutOffDrivability = setCutOffDrivability
M.flashUiMessage = flashUiMessage
M.realTimeUiDisplay = realTimeUiDisplay
M.characterRealTimeUiDisplay = characterRealTimeUiDisplay
M.clearCharacterRealTimeUiDisplay = clearCharacterRealTimeUiDisplay
M.getDistanceBetweenSceneObjects = getDistanceBetweenSceneObjects
M.setAiLine = setAiLine
return M