-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgrapplinghook.lua
482 lines (382 loc) · 20.8 KB
/
grapplinghook.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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
-- This is a local (only visible in this file) function used in the function "enableGrapplingHook"
-- moves a character to an object with a speed in meters per second.
-- odfName is the ODF name of the deployed autoturret
local moveUnitToObject = function(character, object, speed, odfName)
local unit = GetCharacterUnit(character)
--check if unit is alive (will be nil if dead)
if unit then
-- get start and end coordinates
local xStart, yStart, zStart = GetWorldPosition(unit)
local xEnd, yEnd, zEnd = GetWorldPosition(object)
-- position deltas - create the vector
local dX = xEnd - xStart
local dY = yEnd - yStart
local dZ = zEnd - zStart
local distance = math.sqrt(dX * dX + dY * dY + dZ * dZ)
local objLocation = GetEntityMatrix(object)
-- create temporary other turret so we can calculate its rotation
tempturret = CreateEntity(odfName, CreateMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, objLocation), "temp_turret")
local xTemp, yTemp, zTemp = GetWorldPosition(tempturret)
DeleteEntity(tempturret)
tempturret = nil
--calculate angle (y-axis rotation)
local adjacent = xTemp - xEnd
local opposite = zTemp - zEnd
local hypotenuse = math.sqrt(((xTemp - xEnd) * (xTemp - xEnd)) + ((zTemp - zEnd) * (zTemp - zEnd)))
local phi = math.acos(adjacent / hypotenuse)
--correct the angle
-- this was a pain in the ass
if opposite < 0 then
phi = phi + (math.pi) / 2
else
phi = -(phi - (math.pi) / 2)
end
local count = 0
--every time interval move this distance. These two variables control speed
-- units in meters per second
local incrementDistance = speed / 100
local timeInterval = 0.01 -- one hundredth of a second
moveUnitTimer = CreateTimer("moveUnitTimer")
SetTimerValue(moveUnitTimer, timeInterval) -- in seconds
moveUnitTimerTracker = OnTimerElapse(function(timer)
count = count + incrementDistance -- move 1 meter per time interval unit we hit distance
--print("timer count is " .. tostring(count))
---- Tried to do this, but it crashed. Maybe you can figure it out
----draw the cable. DrawBeamBetween is the C function used in galactic conquest
--DrawBeamBetween(
-- GetEntityMatrix(unit),
-- objLocation,
-- "troop_icon", 1.0, 255, 255, 255, 255, 0, 1
--)
local factor = count / distance
--check if unit is alive
if GetCharacterUnit(character) then
-- move character once every timer interval
-- stop a little above the destination to avoid impact damage
-- by making the last argument "nil" you can use absolute position/rotation
SetEntityMatrix(unit, CreateMatrix(phi, 0.0, 1.0, 0.0, xStart + ((dX) * factor), yStart + ((dY + 0.5) * factor), zStart + ((dZ) * factor), nil))
else
--print(" unit is dead. stopping ")
count = 0
DestroyTimer(timer)
moveUnitTimer = nil
ReleaseTimerElapse(moveUnitTimerTracker)
moveUnitTimerTracker = nil
end
if count < distance then
--print("continuing")
--StopTimer(timer)
SetTimerValue(timer, timeInterval)
StartTimer(timer)
else
--print("stopping")
count = 0
DestroyTimer(timer)
moveUnitTimer = nil
ReleaseTimerElapse(moveUnitTimerTracker)
moveUnitTimerTracker = nil
end
end
, moveUnitTimer)
StartTimer(moveUnitTimer)
end
--print("moving unit")
end
-- example: "rep_bldg_inf_autoturret", 100
-- odfName, a string which is the name of the deployed autoturret ODF
-- speed, a number, the speed to travel the grappling hook in meters per second
enableGrapplingHook = function(odfName, speed)
-- grappling hook code
grappleLaunch = OnCharacterDispenseControllable(
(function(player, controllable)
--print("dispensed entity")
if GetEntityClass(controllable) == FindEntityClass(odfName) then
--print("dispensed autoturret")
-- this timer will track the grapple's timer over time.
-- If it detects that the grapple is stopped, it will pull the unit towards it.
-- If the grapple dies or never stops, the timer stops and nothing happens.
local alive = true
local grappleCoordTable = {}
local loopCount = 0
local grappleTimeInterval = 0.25 -- check 4 times a second
moveGrappleTimer = CreateTimer("moveGrappleTimer")
SetTimerValue(moveGrappleTimer, grappleTimeInterval) -- in seconds
moveGrappleTimerTracker = OnTimerElapse(function(timer)
loopCount = loopCount + 1
--print("timer count is " .. tostring(loopCount))
if alive == true then
--print("hook still alive, continuing")
local xGrap, yGrap, zGrap = GetWorldPosition(controllable)
--print("grapple position is" .. xGrap .. " " .. yGrap .. " " .. zGrap)
table.insert(grappleCoordTable, { x = xGrap, y = yGrap, z = zGrap })
-- check if there is more than 1 entry in the table and if the last and second to last are equal
-- probably should clean out all but the last 2 of the table....
if table.getn(grappleCoordTable) > 1
and grappleCoordTable[table.getn(grappleCoordTable)].x == grappleCoordTable[table.getn(grappleCoordTable) - 1].x
and grappleCoordTable[table.getn(grappleCoordTable)].y == grappleCoordTable[table.getn(grappleCoordTable) - 1].y
and grappleCoordTable[table.getn(grappleCoordTable)].z == grappleCoordTable[table.getn(grappleCoordTable) - 1].z
then
--print("grapple is stationary")
-- call the function that does the actual moving of our unit
moveUnitToObject(player, controllable, speed, odfName)
--exit the timer, clean up
loopCount = 0
grappleCoordTable = {}
alive = true
--StopTimer(timer) -- timer should already be stopped if we are in this function?
DestroyTimer(timer)
moveGrappleTimer = nil
ReleaseTimerElapse(moveGrappleTimerTracker)
moveGrappleTimerTracker = nil
--exit the callback function
return
end
--StopTimer(moveUnitTimer)
SetTimerValue(timer, grappleTimeInterval)
StartTimer(timer)
elseif alive == false then
--print("hook is dead, stopping")
-- clean up
loopCount = 0
grappleCoordTable = {}
alive = true
--StopTimer(timer)
DestroyTimer(timer)
moveGrappleTimer = nil
ReleaseTimerElapse(moveGrappleTimerTracker)
moveGrappleTimerTracker = nil
else
--print("Grapple code: something else happened!")
end
end
, moveGrappleTimer)
-- this event listener simply sets alive to false.
-- I tried to use IsObjectAlive but that crashed when it was dead. Lol
-- Could potentially run into some synchronization issues, but that hasn't happened during testing
-- If there are glitches with the weapon then consider using the "LifeTime" value in the autoturret odf ...
-- ... instead of this event. make the timer only go until a the LifeTime of the autoturret
hookDeath = OnObjectKill(function(object, killer)
--print("object died")
if object == controllable then
--print(" hook died")
alive = false
--print("alive is " .. tostring(alive))
ReleaseObjectKill(hookDeath)
hookDeath = nil
end
end)
-- kick off the timer to track the grapple's position over time
StartTimer(moveGrappleTimer)
end
end))
end
---------------
-- the below functions are similar to those above except the unit follows the trajectory the grappling hook was thrown
---------------
-- This is a local (only visible in this file) function used in the function "enableGrapplingHook"
-- moves a character to an object with a speed in meters per second.
-- odfName is the ODF name of the deployed autoturret
local moveUnitToObjectAlongTrajectory = function(character, object, speed, odfName, trajectoryCoordTable)
local unit = GetCharacterUnit(character)
--check if unit is alive (will be nil if dead)
if unit then
--print("unit is alive, starting move")
-- get start and end coordinates
local xStart, yStart, zStart = GetWorldPosition(unit)
--local xEnd, yEnd, zEnd = GetWorldPosition(object)
-- variable to track the coordinate index
local coordIndex = 1
local xEnd = trajectoryCoordTable[coordIndex].x
local yEnd = trajectoryCoordTable[coordIndex].y
local zEnd = trajectoryCoordTable[coordIndex].z
-- position deltas - create the vector
local dX = xEnd - xStart
local dY = yEnd - yStart
local dZ = zEnd - zStart
local distance = math.sqrt(dX * dX + dY * dY + dZ * dZ)
local objLocation = GetEntityMatrix(object)
-- create temporary other turret so we can calculate its rotation
tempturret = CreateEntity(odfName, CreateMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, objLocation), "temp_turret")
local xTemp, yTemp, zTemp = GetWorldPosition(tempturret)
DeleteEntity(tempturret)
tempturret = nil
--calculate angle (y-axis rotation)
local adjacent = xTemp - xEnd
local opposite = zTemp - zEnd
local hypotenuse = math.sqrt(((xTemp - xEnd) * (xTemp - xEnd)) + ((zTemp - zEnd) * (zTemp - zEnd)))
local phi = math.acos(adjacent / hypotenuse)
--correct the angle
-- this was a pain in the ass
if opposite < 0 then
phi = phi + (math.pi) / 2
else
phi = -(phi - (math.pi) / 2)
end
local count = 0
--every time interval move this distance. These two variables control speed
-- units in meters per second
local incrementDistance = speed / 100
local timeInterval = 1/100 -- one hundredth of a second or 100 fps. Change this to control how "smooth" the unit moves
moveUnitTimer = CreateTimer("moveUnitTimer")
SetTimerValue(moveUnitTimer, timeInterval) -- in seconds
-- variable to track the coordinate index
local coordIndex = 1
moveUnitTimerTracker = OnTimerElapse(function(timer)
count = count + incrementDistance -- move 1 meter per time interval unit we hit distance
--print("timer count is " .. tostring(count))
---- Tried to do this, but it crashed. Maybe you can figure it out
----draw the cable. DrawBeamBetween is the C function used in galactic conquest
--DrawBeamBetween(
-- GetEntityMatrix(unit),
-- objLocation,
-- "troop_icon", 1.0, 255, 255, 255, 255, 0, 1
--)
local factor = count / distance
--check if unit is alive
if GetCharacterUnit(character) then
--print("unit is alive, moving")
-- move character once every timer interval
-- stop a little above the destination to avoid impact damage
-- by making the last argument "nil" you can use absolute position/rotation
SetEntityMatrix(unit, CreateMatrix(phi, 0.0, 1.0, 0.0, xStart + ((dX) * factor), yStart + ((dY + 0.5) * factor), zStart + ((dZ) * factor), nil))
else
--print(" unit is dead. stopping ")
count = 0
coordIndex = 1
DestroyTimer(timer)
moveUnitTimer = nil
ReleaseTimerElapse(moveUnitTimerTracker)
moveUnitTimerTracker = nil
end
if count < distance then
--print("continuing")
--StopTimer(timer)
SetTimerValue(timer, timeInterval)
StartTimer(timer)
else
coordIndex = coordIndex + 1
-- if there are more grapple coords
if coordIndex < table.getn(trajectoryCoordTable) then
--print("moving to next coord")
count = 0
xStart = trajectoryCoordTable[coordIndex-1].x
yStart = trajectoryCoordTable[coordIndex-1].y
zStart = trajectoryCoordTable[coordIndex-1].z
-- recalculate the deltas
dX = trajectoryCoordTable[coordIndex].x - xStart
dY = trajectoryCoordTable[coordIndex].y - yStart
dZ = trajectoryCoordTable[coordIndex].z - zStart
distance = math.sqrt(dX * dX + dY * dY + dZ * dZ)
--start timer again
SetTimerValue(timer, timeInterval)
StartTimer(timer)
-- if we've reached the last grapple coord
else
--print("stopping")
count = 0
coordIndex = 1
DestroyTimer(timer)
moveUnitTimer = nil
ReleaseTimerElapse(moveUnitTimerTracker)
moveUnitTimerTracker = nil
end
end
end
, moveUnitTimer)
--print("starting move timer")
StartTimer(moveUnitTimer)
else
--print("unit is dead, not moving")
end
--print("moving unit")
end
-- example: "rep_bldg_inf_autoturret", 100
-- odfName, a string which is the name of the deployed autoturret ODF
-- speed, a number, the speed to travel the grappling hook in meters per second
enableGrapplingHookFollowTrajectory = function(odfName, speed)
-- grappling hook code
grappleLaunch = OnCharacterDispenseControllable(
(function(player, controllable)
--print("dispensed entity")
if GetEntityClass(controllable) == FindEntityClass(odfName) then
--print("dispensed autoturret")
-- this timer will track the grapple's position over time.
-- If it detects that the grapple is stopped, it will pull the unit towards it.
-- If the grapple dies or never stops, the timer stops and nothing happens.
local alive = true
local grappleCoordTable = {}
local loopCount = 0
local grappleTimeInterval = 1/60 --record the path of the grappling hook at 60 fps so it looks nice! Can lower if desired
moveGrappleTimer = CreateTimer("moveGrappleTimer")
SetTimerValue(moveGrappleTimer, grappleTimeInterval) -- in seconds
moveGrappleTimerTracker = OnTimerElapse(function(timer)
loopCount = loopCount + 1
--print("timer count is " .. tostring(loopCount))
if alive == true then
--print("hook still alive, continuing")
local xGrap, yGrap, zGrap = GetWorldPosition(controllable)
table.insert(grappleCoordTable, { x = xGrap, y = yGrap, z = zGrap })
-- check if there is more than 1 entry in the table and if the last and second to last are equal
-- probably should clean out all but the last 2 of the table....
if table.getn(grappleCoordTable) > 1
and grappleCoordTable[table.getn(grappleCoordTable)].x == grappleCoordTable[table.getn(grappleCoordTable) - 1].x
and grappleCoordTable[table.getn(grappleCoordTable)].y == grappleCoordTable[table.getn(grappleCoordTable) - 1].y
and grappleCoordTable[table.getn(grappleCoordTable)].z == grappleCoordTable[table.getn(grappleCoordTable) - 1].z
then
--print("grapple is stationary")
-- call the function that does the actual moving of our unit
moveUnitToObjectAlongTrajectory(player, controllable, speed, odfName, grappleCoordTable)
--exit the timer, clean up
loopCount = 0
grappleCoordTable = {}
alive = true
--StopTimer(timer) -- timer should already be stopped if we are in this function?
DestroyTimer(timer)
moveGrappleTimer = nil
ReleaseTimerElapse(moveGrappleTimerTracker)
moveGrappleTimerTracker = nil
--print("exiting")
--exit the callback function
return
end
--StopTimer(moveUnitTimer)
SetTimerValue(timer, grappleTimeInterval)
StartTimer(timer)
elseif alive == false then
--print("hook is dead, stopping")
-- clean up
loopCount = 0
grappleCoordTable = {}
alive = true
--StopTimer(timer)
DestroyTimer(timer)
moveGrappleTimer = nil
ReleaseTimerElapse(moveGrappleTimerTracker)
moveGrappleTimerTracker = nil
else
--print("Grapple code: something else happened!")
end
end
, moveGrappleTimer)
-- this event listener simply sets alive to false.
-- I tried to use IsObjectAlive but that crashed when it was dead. Lol
-- Could potentially run into some synchronization issues, but that hasn't happened during testing
-- If there are glitches with the weapon then consider using the "LifeTime" value in the autoturret odf ...
-- ... instead of this event. make the timer only go until a the LifeTime of the autoturret
hookDeath = OnObjectKill(function(object, killer)
--print("object died")
if object == controllable then
--print(" hook died")
alive = false
--print("alive is " .. tostring(alive))
ReleaseObjectKill(hookDeath)
hookDeath = nil
end
end)
--print("starting grapple timer")
-- kick off the timer to track the grapple's position over time
StartTimer(moveGrappleTimer)
end
end
))
end