-
Notifications
You must be signed in to change notification settings - Fork 575
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Carts: Move entity definition to seperate file
- Loading branch information
1 parent
9bbe7f9
commit 81f8c8a
Showing
2 changed files
with
385 additions
and
384 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,384 @@ | ||
local cart_entity = { | ||
physical = false, -- otherwise going uphill breaks | ||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, | ||
visual = "mesh", | ||
mesh = "carts_cart.b3d", | ||
visual_size = {x=1, y=1}, | ||
textures = {"carts_cart.png"}, | ||
|
||
driver = nil, | ||
punched = false, -- used to re-send velocity and position | ||
velocity = {x=0, y=0, z=0}, -- only used on punch | ||
old_dir = {x=1, y=0, z=0}, -- random value to start the cart on punch | ||
old_pos = nil, | ||
old_switch = 0, | ||
railtype = nil, | ||
attached_items = {} | ||
} | ||
|
||
function cart_entity:on_rightclick(clicker) | ||
if not clicker or not clicker:is_player() then | ||
return | ||
end | ||
local player_name = clicker:get_player_name() | ||
if self.driver and player_name == self.driver then | ||
self.driver = nil | ||
carts:manage_attachment(clicker, nil) | ||
elseif not self.driver then | ||
self.driver = player_name | ||
carts:manage_attachment(clicker, self.object) | ||
end | ||
end | ||
|
||
function cart_entity:on_activate(staticdata, dtime_s) | ||
self.object:set_armor_groups({immortal=1}) | ||
if string.sub(staticdata, 1, string.len("return")) ~= "return" then | ||
return | ||
end | ||
local data = minetest.deserialize(staticdata) | ||
if not data or type(data) ~= "table" then | ||
return | ||
end | ||
self.railtype = data.railtype | ||
if data.old_dir then | ||
self.old_dir = data.old_dir | ||
end | ||
if data.old_vel then | ||
self.old_vel = data.old_vel | ||
end | ||
end | ||
|
||
function cart_entity:get_staticdata() | ||
return minetest.serialize({ | ||
railtype = self.railtype, | ||
old_dir = self.old_dir, | ||
old_vel = self.old_vel | ||
}) | ||
end | ||
|
||
function cart_entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) | ||
local pos = self.object:getpos() | ||
if not self.railtype then | ||
local node = minetest.get_node(pos).name | ||
self.railtype = minetest.get_item_group(node, "connect_to_raillike") | ||
end | ||
|
||
if not puncher or not puncher:is_player() then | ||
local cart_dir = carts:get_rail_direction(pos, self.old_dir, nil, nil, self.railtype) | ||
if vector.equals(cart_dir, {x=0, y=0, z=0}) then | ||
return | ||
end | ||
self.velocity = vector.multiply(cart_dir, 3) | ||
self.punched = true | ||
return | ||
end | ||
|
||
if puncher:get_player_control().sneak then | ||
if self.sound_handle then | ||
minetest.sound_stop(self.sound_handle) | ||
end | ||
-- Pick up cart: Drop all attachments | ||
if self.driver then | ||
if self.old_pos then | ||
self.object:setpos(self.old_pos) | ||
end | ||
local player = minetest.get_player_by_name(self.driver) | ||
carts:manage_attachment(player, nil) | ||
end | ||
for _,obj_ in ipairs(self.attached_items) do | ||
if obj_ then | ||
obj_:set_detach() | ||
end | ||
end | ||
|
||
local leftover = puncher:get_inventory():add_item("main", "carts:cart") | ||
if not leftover:is_empty() then | ||
minetest.add_item(self.object:getpos(), leftover) | ||
end | ||
self.object:remove() | ||
return | ||
end | ||
|
||
local vel = self.object:getvelocity() | ||
if puncher:get_player_name() == self.driver then | ||
if math.abs(vel.x + vel.z) > carts.punch_speed_max then | ||
return | ||
end | ||
end | ||
|
||
local punch_dir = carts:velocity_to_dir(puncher:get_look_dir()) | ||
punch_dir.y = 0 | ||
local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, self.railtype) | ||
if vector.equals(cart_dir, {x=0, y=0, z=0}) then | ||
return | ||
end | ||
|
||
local punch_interval = 1 | ||
if tool_capabilities and tool_capabilities.full_punch_interval then | ||
punch_interval = tool_capabilities.full_punch_interval | ||
end | ||
time_from_last_punch = math.min(time_from_last_punch or punch_interval, punch_interval) | ||
local f = 2 * (time_from_last_punch / punch_interval) | ||
|
||
self.velocity = vector.multiply(cart_dir, f) | ||
self.old_dir = cart_dir | ||
self.punched = true | ||
end | ||
|
||
local function rail_on_step_event(handler, obj, dtime) | ||
if handler then | ||
handler(obj, dtime) | ||
end | ||
end | ||
|
||
-- sound refresh interval = 1.0sec | ||
local function rail_sound(self, dtime) | ||
if not self.sound_ttl then | ||
self.sound_ttl = 1.0 | ||
return | ||
elseif self.sound_ttl > 0 then | ||
self.sound_ttl = self.sound_ttl - dtime | ||
return | ||
end | ||
self.sound_ttl = 1.0 | ||
if self.sound_handle then | ||
local handle = self.sound_handle | ||
self.sound_handle = nil | ||
minetest.after(0.2, minetest.sound_stop, handle) | ||
end | ||
local vel = self.object:getvelocity() | ||
local speed = vector.length(vel) | ||
if speed > 0 then | ||
self.sound_handle = minetest.sound_play( | ||
"carts_cart_moving", { | ||
object = self.object, | ||
gain = (speed / carts.speed_max) / 2, | ||
loop = true, | ||
}) | ||
end | ||
end | ||
|
||
local function rail_on_step(self, dtime) | ||
local pos = self.object:getpos() | ||
local node = minetest.get_node(pos) | ||
local railparams = carts.railparams[node.name] or {} | ||
|
||
local vel = self.object:getvelocity() | ||
local update = {} | ||
if self.punched then | ||
vel = vector.add(vel, self.velocity) | ||
self.object:setvelocity(vel) | ||
self.old_dir.y = 0 | ||
elseif vector.equals(vel, {x=0, y=0, z=0}) then | ||
return | ||
end | ||
|
||
-- stop cart if velocity vector flips | ||
if self.old_vel and (((self.old_vel.x * vel.x) < 0) or | ||
((self.old_vel.z * vel.z) < 0)) and | ||
(self.old_vel.y == 0) then | ||
self.old_dir = {x = 0, y = 0, z = 0} | ||
self.old_vel = {x = 0, y = 0, z = 0} | ||
self.velocity = {x = 0, y = 0, z = 0} | ||
self.old_pos = pos | ||
self.object:setvelocity(vector.new()) | ||
self.object:setacceleration(vector.new()) | ||
rail_on_step_event(railparams.on_step, self, dtime) | ||
return | ||
end | ||
self.old_vel = vector.new(vel) | ||
|
||
if self.old_pos and not self.punched then | ||
local flo_pos = vector.round(pos) | ||
local flo_old = vector.round(self.old_pos) | ||
if vector.equals(flo_pos, flo_old) then | ||
-- Do not check one node multiple times | ||
rail_on_step_event(railparams.on_step, self, dtime) | ||
return | ||
end | ||
end | ||
|
||
local ctrl, player | ||
|
||
-- Get player controls | ||
if self.driver then | ||
player = minetest.get_player_by_name(self.driver) | ||
if player then | ||
ctrl = player:get_player_control() | ||
end | ||
end | ||
|
||
if self.old_pos then | ||
-- Detection for "skipping" nodes | ||
local expected_pos = vector.add(self.old_pos, self.old_dir) | ||
local found_path = carts:pathfinder( | ||
pos, expected_pos, self.old_dir, ctrl, self.old_switch, self.railtype | ||
) | ||
|
||
if not found_path then | ||
-- No rail found: reset back to the expected position | ||
pos = vector.new(self.old_pos) | ||
update.pos = true | ||
end | ||
end | ||
|
||
local cart_dir = carts:velocity_to_dir(vel) | ||
|
||
-- dir: New moving direction of the cart | ||
-- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node | ||
local dir, switch_keys = carts:get_rail_direction( | ||
pos, cart_dir, ctrl, self.old_switch, self.railtype | ||
) | ||
|
||
local new_acc = {x=0, y=0, z=0} | ||
if vector.equals(dir, {x=0, y=0, z=0}) then | ||
vel = {x=0, y=0, z=0} | ||
pos = vector.round(pos) | ||
update.pos = true | ||
update.vel = true | ||
else | ||
-- If the direction changed | ||
if dir.x ~= 0 and self.old_dir.z ~= 0 then | ||
vel.x = dir.x * math.abs(vel.z) | ||
vel.z = 0 | ||
pos.z = math.floor(pos.z + 0.5) | ||
update.pos = true | ||
end | ||
if dir.z ~= 0 and self.old_dir.x ~= 0 then | ||
vel.z = dir.z * math.abs(vel.x) | ||
vel.x = 0 | ||
pos.x = math.floor(pos.x + 0.5) | ||
update.pos = true | ||
end | ||
-- Up, down? | ||
if dir.y ~= self.old_dir.y then | ||
vel.y = dir.y * math.abs(vel.x + vel.z) | ||
pos = vector.round(pos) | ||
update.pos = true | ||
end | ||
|
||
-- Slow down or speed up.. | ||
local acc = dir.y * -4.0 | ||
|
||
-- no need to check for railparams == nil since we always make it exist. | ||
local speed_mod = railparams.acceleration | ||
if speed_mod and speed_mod ~= 0 then | ||
-- Try to make it similar to the original carts mod | ||
acc = acc + speed_mod | ||
else | ||
-- Handbrake | ||
if ctrl and ctrl.down then | ||
acc = acc - 1.6 | ||
else | ||
acc = acc - 0.4 | ||
end | ||
end | ||
|
||
new_acc = vector.multiply(dir, acc) | ||
end | ||
|
||
-- Limits | ||
local max_vel = carts.speed_max | ||
for _,v in ipairs({"x","y","z"}) do | ||
if math.abs(vel[v]) > max_vel then | ||
vel[v] = carts:get_sign(vel[v]) * max_vel | ||
new_acc[v] = 0 | ||
update.vel = true | ||
end | ||
end | ||
|
||
self.object:setacceleration(new_acc) | ||
self.old_pos = vector.new(pos) | ||
if not vector.equals(dir, {x=0, y=0, z=0}) then | ||
self.old_dir = vector.new(dir) | ||
end | ||
self.old_switch = switch_keys | ||
|
||
if self.punched then | ||
-- Collect dropped items | ||
for _,obj_ in ipairs(minetest.get_objects_inside_radius(pos, 1)) do | ||
if not obj_:is_player() and | ||
obj_:get_luaentity() and | ||
not obj_:get_luaentity().physical_state and | ||
obj_:get_luaentity().name == "__builtin:item" then | ||
|
||
obj_:set_attach(self.object, "", {x=0, y=0, z=0}, {x=0, y=0, z=0}) | ||
self.attached_items[#self.attached_items + 1] = obj_ | ||
end | ||
end | ||
self.punched = false | ||
update.vel = true | ||
end | ||
|
||
if not (update.vel or update.pos) then | ||
rail_on_step_event(railparams.on_step, self, dtime) | ||
return | ||
end | ||
|
||
local yaw = 0 | ||
if self.old_dir.x < 0 then | ||
yaw = 0.5 | ||
elseif self.old_dir.x > 0 then | ||
yaw = 1.5 | ||
elseif self.old_dir.z < 0 then | ||
yaw = 1 | ||
end | ||
self.object:setyaw(yaw * math.pi) | ||
|
||
local anim = {x=0, y=0} | ||
if dir.y == -1 then | ||
anim = {x=1, y=1} | ||
elseif dir.y == 1 then | ||
anim = {x=2, y=2} | ||
end | ||
self.object:set_animation(anim, 1, 0) | ||
|
||
self.object:setvelocity(vel) | ||
if update.pos then | ||
self.object:setpos(pos) | ||
end | ||
|
||
-- call event handler | ||
rail_on_step_event(railparams.on_step, self, dtime) | ||
end | ||
|
||
function cart_entity:on_step(dtime) | ||
rail_on_step(self, dtime) | ||
rail_sound(self, dtime) | ||
end | ||
|
||
minetest.register_entity("carts:cart", cart_entity) | ||
|
||
minetest.register_craftitem("carts:cart", { | ||
description = "Cart (Sneak+Click to pick up)", | ||
inventory_image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png", "carts_cart_side.png"), | ||
wield_image = "carts_cart_side.png", | ||
on_place = function(itemstack, placer, pointed_thing) | ||
if not pointed_thing.type == "node" then | ||
return | ||
end | ||
if carts:is_rail(pointed_thing.under) then | ||
minetest.add_entity(pointed_thing.under, "carts:cart") | ||
elseif carts:is_rail(pointed_thing.above) then | ||
minetest.add_entity(pointed_thing.above, "carts:cart") | ||
else | ||
return | ||
end | ||
|
||
minetest.sound_play({name = "default_place_node_metal", gain = 0.5}, | ||
{pos = pointed_thing.above}) | ||
|
||
if not minetest.setting_getbool("creative_mode") then | ||
itemstack:take_item() | ||
end | ||
return itemstack | ||
end, | ||
}) | ||
|
||
minetest.register_craft({ | ||
output = "carts:cart", | ||
recipe = { | ||
{"default:steel_ingot", "", "default:steel_ingot"}, | ||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, | ||
}, | ||
}) |
Oops, something went wrong.