-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathentitylib.lua
349 lines (311 loc) · 10.6 KB
/
entitylib.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
--[[
Minecart
========
Copyright (C) 2019-2021 Joachim Stolberg
MIT
See license.txt for more information
]]--
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P2H = minetest.hash_node_position
local H2P = minetest.get_position_from_hash
local LEN = function(t) local c = 0; for _ in pairs(t) do c = c + 1 end; return c; end
local MAX_SPEED = minecart.MAX_SPEED
local dot2dir = minecart.dot2dir
local get_waypoint = minecart.get_waypoint
local recording_waypoints = minecart.recording_waypoints
local recording_junctions = minecart.recording_junctions
local set_junctions = minecart.set_junctions
local player_ctrl = minecart.player_ctrl
local tEntityNames = minecart.tEntityNames
local function stop_cart(self, cart_pos)
local player
self.is_running = false
self.arrival_time = 0
if self.driver then
player = minetest.get_player_by_name(self.driver)
if player then
minecart.stop_recording(self, cart_pos)
minecart.manage_attachment(player, self, false)
end
end
if not minecart.get_buffer_pos(cart_pos, self.owner) then
-- Probably somewhere in the pampas
minecart.delete_cart_waypoint(cart_pos)
end
minecart.entity_to_node(cart_pos, self)
if player then
player:set_pos(cart_pos)
end
end
local function get_ctrl(self, pos)
-- Use player ctrl or junction data from recorded routes
return (self.driver and self.ctrl) or (self.junctions and self.junctions[P2H(pos)]) or {}
end
local function new_speed(self, new_dir)
self.cart_speed = self.cart_speed or 0
local rail_speed = (self.waypoint.speed or 0) / 10
if rail_speed <= 0 then
rail_speed = math.max(self.cart_speed + rail_speed, 0)
elseif rail_speed <= self.cart_speed then
rail_speed = math.max((self.cart_speed + rail_speed) / 2, 0)
end
-- Speed corrections
if new_dir.y == 1 then
if rail_speed < 1 then rail_speed = 0 end
else
if rail_speed < 0.4 then rail_speed = 0 end
end
self.cart_speed = rail_speed -- store for next cycle
return rail_speed
end
local function running(self)
local rot = self.object:get_rotation()
local dir = minetest.yaw_to_dir(rot.y)
dir.y = math.floor((rot.x / (math.pi/4)) + 0.5)
dir = vector.round(dir)
-- If running in a 45 degree direction (extra cycle), use the old dir
-- to calculate face_dir. Otherwise the junction detection will not work as expected.
local facedir
if self.waypoint and self.waypoint.old_dir then
facedir = minetest.dir_to_facedir(self.waypoint.old_dir)
else
facedir = minetest.dir_to_facedir(dir)
end
local cart_pos, wayp_pos, is_junction
if self.reenter then -- through monitoring
cart_pos = H2P(self.reenter[1])
-- pos correction on slopes
if not minecart.is_rail(cart_pos) then
cart_pos.y = cart_pos.y - 1
end
wayp_pos = cart_pos
is_junction = false
self.waypoint = {pos = H2P(self.reenter[2]), power = 0, dot = self.reenter[4]}
self.cart_speed = self.reenter[3]
self.speed_limit = MAX_SPEED
self.reenter = nil
elseif not self.waypoint then
-- get waypoint
cart_pos = vector.round(self.object:get_pos())
wayp_pos = cart_pos
is_junction = false
self.waypoint = get_waypoint(cart_pos, facedir, get_ctrl(self, cart_pos), true)
if self.no_normal_start then
-- Probably somewhere in the pampas
minecart.delete_waypoint(cart_pos)
self.no_normal_start = nil
end
self.cart_speed = 2 -- push speed
self.speed_limit = MAX_SPEED
else
-- next waypoint
cart_pos = vector.new(self.waypoint.cart_pos or self.waypoint.pos)
wayp_pos = self.waypoint.pos
local vel = self.object:get_velocity()
self.waypoint, is_junction = get_waypoint(wayp_pos, facedir, get_ctrl(self, wayp_pos), self.cart_speed < 0.1)
end
if not self.waypoint then
stop_cart(self, wayp_pos)
return
end
if is_junction then
if self.is_recording then
set_junctions(self, wayp_pos)
end
self.ctrl = nil
end
--print("running", LEN(self.junctions))
local dist = vector.distance(cart_pos, self.waypoint.cart_pos or self.waypoint.pos)
local new_dir = dot2dir(self.waypoint.dot)
local new_speed = new_speed(self, new_dir)
local straight_ahead = vector.equals(new_dir, dir)
-- If straight_ahead, then it's probably a speed limit sign
if straight_ahead then
self.speed_limit = minecart.get_speedlimit(wayp_pos, facedir) or self.speed_limit
end
new_speed = math.min(new_speed, self.speed_limit)
local new_cart_pos, extra_cycle = minecart.get_current_cart_pos_correction(
wayp_pos, facedir, dir.y, self.waypoint.dot) -- TODO: Why has self.waypoint no dot?
if extra_cycle and not vector.equals(cart_pos, new_cart_pos) then
self.waypoint = {pos = wayp_pos, cart_pos = new_cart_pos, old_dir = vector.new(dir)}
new_dir = vector.direction(cart_pos, new_cart_pos)
dist = vector.distance(cart_pos, new_cart_pos)
--print("extra_cycle", P2S(cart_pos), P2S(wayp_pos), P2S(new_cart_pos), new_speed)
end
-- Slope corrections
--print("Slope corrections", P2S(new_dir), P2S(cart_pos))
if new_dir.y ~= 0 then
cart_pos.y = cart_pos.y + 0.2
end
-- Calc velocity, rotation and arrival_time
local yaw = minetest.dir_to_yaw(new_dir)
local pitch = new_dir.y * math.pi/4
--print("new_speed", new_speed / (new_dir.y ~= 0 and 1.41 or 1))
local vel = vector.multiply(new_dir, new_speed / ((new_dir.y ~= 0) and 1.41 or 1))
self.arrival_time = self.timebase + (dist / new_speed)
-- needed for recording
self.curr_speed = new_speed
self.num_sections = (self.num_sections or 0) + 1
-- Got stuck somewhere
if new_speed < 0.1 or dist < 0 then
minetest.log("warning", "[Minecart] Got stuck somewhere " .. new_speed .. " " .. dist)
stop_cart(self, wayp_pos)
return
end
self.object:set_pos(cart_pos)
self.object:set_rotation({x = pitch, y = yaw, z = 0})
self.object:set_velocity(vel)
return
end
local function play_sound(self)
if self.sound_handle then
local handle = self.sound_handle
self.sound_handle = nil
minetest.after(0.2, minetest.sound_stop, handle)
end
if self.object then
self.sound_handle = minetest.sound_play(
"carts_cart_moving", {
object = self.object,
gain = (self.curr_speed or 0) / MAX_SPEED,
})
end
end
local function on_step(self, dtime)
self.timebase = (self.timebase or 0) + dtime
if self.is_running then
if self.arrival_time <= self.timebase then
running(self)
end
if (self.sound_ttl or 0) <= self.timebase then
play_sound(self)
self.sound_ttl = self.timebase + 1.0
end
else
if self.sound_handle then
minetest.sound_stop(self.sound_handle)
self.sound_handle = nil
end
end
if self.driver then
if self.is_recording then
if self.rec_time <= self.timebase then
recording_waypoints(self)
self.rec_time = self.rec_time + 2.0
end
if recording_junctions(self) then
local pos = vector.round(self.object:get_pos())
minecart.stop_recording(self, pos, true)
local player = minetest.get_player_by_name(self.driver)
minecart.manage_attachment(player, self, false)
minecart.entity_to_node(pos, self)
end
else
if player_ctrl(self) then
local pos = vector.round(self.object:get_pos())
local player = minetest.get_player_by_name(self.driver)
minecart.manage_attachment(player, self, false)
minecart.entity_to_node(pos, self)
end
end
end
end
local function on_entitycard_activate(self, staticdata, dtime_s)
self.object:set_armor_groups({immortal=1})
end
-- Start the entity cart (or dig by shift+leftclick)
local function on_entitycard_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
if minecart.is_owner(puncher, self.owner) then
if puncher:get_player_control().sneak then
if not self.only_dig_if_empty or not next(self.cargo) then
-- drop items
local pos = vector.round(self.object:get_pos())
for _,item in ipairs(self.cargo or {}) do
minetest.add_item(pos, ItemStack(item))
end
-- Dig cart
if self.driver then
-- remove cart as driver
minecart.stop_recording(self, pos, true)
minecart.monitoring_remove_cart(self.owner, self.userID)
minecart.remove_entity(self, pos, puncher)
minecart.manage_attachment(puncher, self, false)
else
-- remove cart from outside
minecart.monitoring_remove_cart(self.owner, self.userID)
minecart.remove_entity(self, pos, puncher)
end
end
elseif not self.is_running then
-- start the cart
local pos = vector.round(self.object:get_pos())
if puncher then
local yaw = puncher:get_look_horizontal()
dir = minetest.yaw_to_dir(yaw)
self.object:set_rotation({x = 0, y = yaw, z = 0})
end
local facedir = minetest.dir_to_facedir(dir or {x=0, y=0, z=0})
minecart.start_entitycart(self, pos, facedir or 0)
minecart.start_recording(self, pos)
end
end
end
-- Player get on / off
local function on_entitycard_rightclick(self, clicker)
if clicker and clicker:is_player() and self.driver_allowed then
-- Get on / off
if self.driver then
-- get off
local pos = vector.round(self.object:get_pos())
minecart.stop_recording(self, pos, true)
minecart.manage_attachment(clicker, self, false)
minecart.entity_to_node(pos, self)
else
-- get on
local pos = vector.round(self.object:get_pos())
minecart.stop_recording(self, pos, true)
minecart.manage_attachment(clicker, self, true)
end
end
end
local function on_entitycard_detach_child(self, child)
if child and child:get_player_name() == self.driver then
self.driver = nil
end
end
function minecart.get_entitycart_nearby(pos, param2, radius)
local pos2 = param2 and vector.add(pos, minecart.param2_to_dir(param2)) or pos
for _, object in pairs(minetest.get_objects_inside_radius(pos2, radius or 0.5)) do
local entity = object:get_luaentity()
if entity and entity.name and tEntityNames[entity.name] then
local vel = object:get_velocity()
if vector.equals(vel, {x=0, y=0, z=0}) then -- still standing?
return entity
end
end
end
end
function minecart.push_entitycart(self, punch_dir)
--print("push_entitycart")
local vel = self.object:get_velocity()
punch_dir.y = 0
local yaw = minetest.dir_to_yaw(punch_dir)
self.object:set_rotation({x = 0, y = yaw, z = 0})
self.is_running = true
self.arrival_time = 0
end
function minecart.register_cart_entity(entity_name, node_name, cart_type, entity_def)
entity_def.entity_name = entity_name
entity_def.node_name = node_name
entity_def.on_activate = on_entitycard_activate
entity_def.on_punch = on_entitycard_punch
entity_def.on_step = on_step
entity_def.on_rightclick = on_entitycard_rightclick
entity_def.on_detach_child = on_entitycard_detach_child
entity_def.owner = nil
entity_def.driver = nil
entity_def.cargo = {}
minetest.register_entity(entity_name, entity_def)
-- register node for punching
minecart.register_cart_names(node_name, entity_name, cart_type)
end