forked from paramat/airboat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinit.lua
454 lines (385 loc) · 16.1 KB
/
init.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
-- This is an edited version of Paramat's airboat mod:
-- https://github.com/paramat/airboat
local acceleration = 2
local friction = 0.2
local acceleration_y = 1
local friction_y = 0.35
local yaw_speed = 0.015
local hyaw_speed = yaw_speed * 0.1
-- Functions
local function airboat_manage_attachment(player, obj, is_passenger)
if not player then
return
end
local status = obj ~= nil
local player_name = player:get_player_name()
if player_api.player_attached[player_name] == status then
return
end
player_api.player_attached[player_name] = status
if status then
local attach = player:get_attach()
if attach and attach:get_luaentity() then
local luaentity = attach:get_luaentity()
if luaentity.driver then
luaentity.driver = nil
end
player:set_detach()
end
player:set_attach(obj, "", {x = is_passenger and 5 or -5, y = -3, z = 0}, {x = 0, y = 0, z = 0})
player:set_eye_offset({x = 0, y = 6, z = 0}, {x = 0, y = 9, z = 0})
else
player:set_detach()
player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
player_api.set_animation(player, "stand" , 30)
end
end
-- Airboat entity
local airboat_entity = {
initial_properties = {
physical = true,
collide_with_objects = false, -- Workaround fix for a MT engine bug
collisionbox = {-1.4, -2, -1.4, 1.4, 1.8, 1.4},
visual = "wielditem",
visual_size = {x = 2.65, y = 2.65}, -- Scale up of nodebox is these * 1.5, so 4
textures = {"airboat:airboat_nodebox"},
},
-- Custom fields
driver = nil, -- Current driver
passenger = nil, -- Current passenger
auto = false, -- Cruise control
}
function airboat_entity:on_rightclick(clicker)
if not clicker or not clicker:is_player() then
return
end
local name = clicker:get_player_name()
if self.driver and name == self.driver then
self.driver = nil
self.auto = false
-- Detach
airboat_manage_attachment(clicker, nil)
elseif self.passenger and name == self.passenger then
self.passenger = nil
-- Detach
airboat_manage_attachment(clicker, nil)
elseif not self.driver then
self.driver = name
airboat_manage_attachment(clicker, self.object)
-- player_api does not update the animation
-- when the player is attached, reset to default animation
minetest.after(0.2, function()
player_api.set_animation(clicker, "sit" , 30)
end)
clicker:set_look_horizontal(self.object:getyaw())
elseif not self.passenger then
self.passenger = name
airboat_manage_attachment(clicker, self.object, true)
-- player_api does not update the animation
-- when the player is attached, reset to default animation
minetest.after(0.2, function()
player_api.set_animation(clicker, "sit" , 30)
end)
clicker:set_look_horizontal(self.object:getyaw())
end
end
function airboat_entity:on_activate()
self.object:set_armor_groups({immortal = 1})
end
function airboat_entity:on_detach_child(child)
if child and child:get_player_name() == self.driver then
airboat.cancel_go(self.driver)
self.driver = nil
self.auto = false
end
if child and child:get_player_name() == self.passenger then
airboat.cancel_go(self.passenger)
self.passenger = nil
end
end
function airboat_entity:on_punch(puncher)
if not puncher or not puncher:is_player() then
return
end
-- Player can only pick it up if no-one is inside
if not self.driver and not self.passenger then
-- Move to inventory
creative.add_to_inventory(puncher, "airboat:airboat", self.object:get_pos())
minetest.after(0, function()
self.object:remove()
end)
end
end
airboat = {}
airboat.player_targets = {}
function airboat.go(player, location)
minetest.log("info", "[airboat] " .. player .. " is going to (" .. location.x .. ", " .. location.z .. ")")
airboat.player_targets[player] = location
end
function airboat.cancel_go(player)
minetest.log("info", "[airboat] " .. player .. " is going nowhere")
airboat.player_targets[player] = nil
end
-- If we have the sethome2 mod, have a way to drive to a home.
if sethome then
minetest.register_chatcommand("drivehome", {
description = "Drive you to your home point",
privs = { home = true },
func = function(name, param)
name = name or "" -- fallback to blank name if nil
local number = param and tonumber(param) or 1
local player = minetest.get_player_by_name(name)
if not player then
return false, "Player not found!"
end
local home = sethome.get(player, number)
if home then
airboat.go(name, home)
return true, "[airboat] Driving you to home " .. number ..
"! (if you are the driver of an airboat)"
end
return false, "Set home " .. number .. " using /sethome " .. number
end,
})
minetest.register_chatcommand("drivecancel", {
description = "Cancel driving",
privs = { },
func = function(name, param)
name = name or "" -- fallback to blank name if nil
local number = param and tonumber(param) or 1
local player = minetest.get_player_by_name(name)
if not player then
return false, "Player not found!"
end
airboat.cancel_go(player)
return true, "[airboat] Route cancelled"
end,
})
end
function airboat_entity:on_step()
local object_v = self.object:get_velocity()
-- local horiz_vel_sq = object_v.x * object_v.x + object_v.y * object_v.y
local vx_acc = -object_v.x * friction
local vz_acc = -object_v.z * friction
local curr_yaw = self.object:getyaw()
local new_yaw = curr_yaw
local vx_acc_mult = -math.sin(curr_yaw)
local vz_acc_mult = math.cos(curr_yaw)
local vy_acc = -object_v.y * friction_y
local horiz_stop = math.abs(object_v.x) < 0.1 and math.abs(object_v.z) < 0.1
-- Controls
if self.driver then
local driver_objref = minetest.get_player_by_name(self.driver)
if driver_objref then
-- If the player is controlling, we don't override it.
-- Otherwise, if there is a destination, go towards it
local ctrl = driver_objref:get_player_control()
local dest = airboat.player_targets[self.driver]
local ctrl_acc = ctrl.up or ctrl.down
-- Forward / backward
if ctrl.down and not ctrl.up then
vx_acc = vx_acc - acceleration * vx_acc_mult * 1.1
vz_acc = vz_acc - acceleration * vz_acc_mult * 1.1
if self.auto then
self.auto = false
minetest.chat_send_player(self.driver, "[airboat] Cruise off")
end
if dest then
dest = nil
airboat.player_targets[self.driver] = nil
minetest.chat_send_player(self.driver, "[airboat] Route cancelled")
end
horiz_stop = false
elseif (ctrl.up and ctrl.down) or (self.auto and not dest) then -- Cruise is more efficient...
vx_acc = vx_acc + acceleration * vx_acc_mult * 1.2
vz_acc = vz_acc + acceleration * vz_acc_mult * 1.2
horiz_stop = false
if not dest and not self.auto then
self.auto = true
minetest.chat_send_player(self.driver, "[airboat] Cruise on")
end
elseif ctrl.up then
vx_acc = vx_acc + acceleration * vx_acc_mult
vz_acc = vz_acc + acceleration * vz_acc_mult
horiz_stop = false
end
local ctrl_rot = ctrl.left or ctrl.right
if ctrl.left and ctrl.right then
-- Do nothing
elseif ctrl.left then
new_yaw = new_yaw + yaw_speed
elseif ctrl.right then
new_yaw = new_yaw - yaw_speed
end
if ctrl.jump then
vy_acc = vy_acc + acceleration_y
horiz_stop = false
elseif ctrl.sneak then
vy_acc = vy_acc - acceleration_y
horiz_stop = false
end
-- If the player isn't controlling, we just rotate and go towards the dest.
if dest and not ctrl_rot and not ctrl_acc then
-- dot between horiz_vel and the direction
local horiz_vel_dir = object_v.x * vx_acc_mult + object_v.z * vz_acc_mult
-- minetest.chat_send_player(self.driver, "[airboat] Getting you to your destination")
local pos = self.object:get_pos()
local target_dir_x = dest.x - pos.x
local target_dir_z = dest.z - pos.z
local tdir_len = math.sqrt(target_dir_x * target_dir_x + target_dir_z * target_dir_z)
-- normalise
local tdir_x = target_dir_x / tdir_len
local tdir_z = target_dir_z / tdir_len
-- Get the dot product of v*_acc_mult and tdir_*
-- and turn if it is < 0.95
local dot = tdir_x * vx_acc_mult + tdir_z * vz_acc_mult
local left_h_dot = tdir_x * -math.sin(new_yaw + hyaw_speed) + tdir_z * math.cos(new_yaw + hyaw_speed)
local left_dot = tdir_x * -math.sin(new_yaw + yaw_speed) + tdir_z * math.cos(new_yaw + yaw_speed)
local right_h_dot = tdir_x * -math.sin(new_yaw - hyaw_speed) + tdir_z * math.cos(new_yaw - hyaw_speed)
local right_dot = tdir_x * -math.sin(new_yaw - yaw_speed) + tdir_z * math.cos(new_yaw - yaw_speed)
if tdir_len > 4 then
if left_dot > dot and left_dot > left_h_dot then
new_yaw = new_yaw + yaw_speed
elseif left_h_dot > dot then
new_yaw = new_yaw + hyaw_speed
elseif right_dot > dot and right_dot > right_h_dot then
new_yaw = new_yaw - yaw_speed
elseif right_h_dot > dot then
new_yaw = new_yaw - hyaw_speed
end
end
if tdir_len < 2 then
if pos.y > dest.y + 4 then
-- Go down
vy_acc = vy_acc - acceleration_y
horiz_stop = true
else
minetest.chat_send_player(self.driver, "[airboat] Arrived; route reset")
-- reset destination
airboat.cancel_go(self.driver)
horiz_stop = true
end
-- Go high
else
if pos.y < dest.y + 10 and tdir_len > 3 then
vy_acc = vy_acc + acceleration_y
end
if pos.y > dest.y + 3 then
if
dot > 0.95
and (tdir_len > 50
or (tdir_len > 10 and horiz_vel_dir < 4)
or (tdir_len > 2.5 and horiz_vel_dir < 1)
or horiz_vel_dir < 0.2
) then
-- accelerate
vx_acc = vx_acc + acceleration * vx_acc_mult * 1.2
vz_acc = vz_acc + acceleration * vz_acc_mult * 1.2
horiz_stop = false
elseif (horiz_vel_dir > 5 and tdir_len < 50)
or (horiz_vel_dir > 2 and tdir_len < 10)
or (horiz_vel_dir > 1 and tdir_len < 2.5) then
-- Slow down
vx_acc = vx_acc - acceleration * vx_acc_mult * 1.3
vz_acc = vz_acc - acceleration * vz_acc_mult * 1.3
horiz_stop = false
end
end
end
end
else
-- Player left server while driving
airboat.cancel_go(self.driver)
self.driver = nil
self.auto = false
minetest.log("warning", "[airboat] Driver left server while" ..
" driving. This may cause some 'Pushing ObjectRef to" ..
" removed/deactivated object' warnings.")
end
end
if horiz_stop then
vx_acc = 0
vz_acc = 0
self.object:set_velocity({ x = 0, y = object_v.y, z = 0 })
end
if not self.driver then
vy_acc = vy_acc - 0.5
-- Slow down when there's no driver
vx_acc = vx_acc * 3
vz_acc = vz_acc * 3
end
local new_acce = { x = vx_acc, y = vy_acc, z = vz_acc }
-- Bouyancy in liquids
local p = self.object:get_pos()
p.y = p.y - 2
local def = minetest.registered_nodes[minetest.get_node(p).name]
if def and (def.liquidtype == "source" or def.liquidtype == "flowing") then
new_acce.y = new_acce.y + 10
end
self.object:set_acceleration(new_acce)
if new_yaw ~= curr_yaw then
self.object:set_yaw(new_yaw)
end
end
minetest.register_entity("airboat:airboat", airboat_entity)
-- Craftitem
minetest.register_craftitem("airboat:airboat", {
description = "Airboat",
inventory_image = "airboat_airboat_inv.png",
wield_scale = {x = 2, y = 2, z = 2},
liquids_pointable = true,
on_place = function(itemstack, placer, pointed_thing)
local under = pointed_thing.under
local node = minetest.get_node(under)
local udef = minetest.registered_nodes[node.name]
-- Run any on_rightclick function of pointed node instead
if udef and udef.on_rightclick and
not (placer and placer:is_player() and
placer:get_player_control().sneak) then
return udef.on_rightclick(under, node, placer, itemstack,
pointed_thing) or itemstack
end
if pointed_thing.type ~= "node" then
return itemstack
end
pointed_thing.under.y = pointed_thing.under.y + 2.5
local airboat = minetest.add_entity(pointed_thing.under,
"airboat:airboat")
if airboat then
if placer then
airboat:setyaw(placer:get_look_horizontal())
end
creative.take_from_inventory(placer, itemstack)
end
return itemstack
end,
stack_max = 1,
groups = { creative = 1 }
})
-- Nodebox for entity wielditem visual
minetest.register_node("airboat:airboat_nodebox", {
description = "Airboat Nodebox",
tiles = { -- Top, base, right, left, front, back
"airboat_airboat_top.png",
"airboat_airboat_base.png",
"airboat_airboat_right.png",
"airboat_airboat_left.png",
"airboat_airboat_front.png",
"airboat_airboat_back.png",
},
paramtype = "light",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
-- Wmin, hmin, lmin, wmax, hmax, lmax
{ -5/16, -1/8, -1/2, 5/16, 3/8, 1/2 }, -- Envelope
{ -3/16, -1/2, -1/4, 3/16, -1/8, 1/4 }, -- Gondola
{ -1/32, 3/8, -1/2, 1/32, 1/2, -1/4 }, -- Top fin
{ -1/32, -1/4, -1/2, 1/32, -1/8, -1/4 }, -- Base fin
{ -7/16, 3/32, -1/2, -5/16, 5/32, -1/4 }, -- Left fin
{ 5/16, 3/32, -1/2, 7/16, 5/32, -1/4 }, -- Right fin
},
},
groups = { not_in_creative_inventory = 1 },
})