Simplify the code and change behaviour a bit

Example: accelerate instead of setting velocities
This commit is contained in:
HybridDog 2020-10-08 20:29:39 +02:00
parent 14f985a62b
commit 89f02386bc
2 changed files with 69 additions and 88 deletions

View File

@ -2,4 +2,4 @@ For a description of this Minetest mod, see
https://forum.minetest.net/viewtopic.php?f=9&t=10271. https://forum.minetest.net/viewtopic.php?f=9&t=10271.
TODO: TODO:
* Support mods liquids * Support more fluids which push items, e.g. river water

155
init.lua
View File

@ -1,31 +1,20 @@
-- Use the movement gravity for the downwards direction. Get the setting rarely -- Use the movement gravity for the downwards acceleration.
local cached_gravity -- The setting may change in-game but for simplicity we don't support this.
local function get_gravity() local movement_gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
if cached_gravity then
return cached_gravity
end
cached_gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
core.after(50, function()
cached_gravity = nil
end)
return cached_gravity
end
-- get_flow_target_raw determines position to which the water flows, or returns
-- nothing if no target position was found
local neighbour_offsets = { local neighbour_offsets = {
{x=-1, y=0, z=0}, {x=-1, y=0, z=0},
{x=1, y=0, z=0}, {x=1, y=0, z=0},
{x=0, y=0, z=-1}, {x=0, y=0, z=-1},
{x=0, y=0, z=1} {x=0, y=0, z=1}
} }
local neighbours_cache = {} local function get_flow_target_raw(pos)
setmetatable(neighbours_cache, {__mode = "kv"}) local param2 = minetest.get_node(pos).param2
local function get_neighbour_nodes(pos) if param2 > 7 then
-- Use previously found neighbours if they are not too old -- The liquid flows downwards
local vi = minetest.hash_node_position(pos) return
local t = minetest.get_us_time()
if neighbours_cache[vi]
and t - neighbours_cache[vi][1] < 10 * 1000000 then
return neighbours_cache[vi][2]
end end
local neighbours = {} local neighbours = {}
for n = 1, 4 do for n = 1, 4 do
@ -33,17 +22,6 @@ local function get_neighbour_nodes(pos)
neighbours[n] = p neighbours[n] = p
neighbours[n + 4] = minetest.get_node(p) neighbours[n + 4] = minetest.get_node(p)
end end
neighbours_cache[vi] = {t, neighbours}
return neighbours
end
-- This function determines position to which the water flows
local function get_flow_target(pos)
local neighbours = get_neighbour_nodes(pos)
local param2 = minetest.get_node(pos).param2
if param2 > 7 then
return
end
for i = 1, 4 do for i = 1, 4 do
-- If a neighbour has a lower height, flow to it -- If a neighbour has a lower height, flow to it
local node = neighbours[i + 4] local node = neighbours[i + 4]
@ -53,7 +31,7 @@ local function get_flow_target(pos)
end end
end end
for i = 1, 4 do for i = 1, 4 do
-- TODO -- Flow to a downwards-flowing neighbour if its height is not too small
local node = neighbours[i + 4] local node = neighbours[i + 4]
if node.name == "default:water_flowing" if node.name == "default:water_flowing"
and node.param2 >= 11 then and node.param2 >= 11 then
@ -61,7 +39,7 @@ local function get_flow_target(pos)
end end
end end
for i = 1, 4 do for i = 1, 4 do
-- TODO -- Flow to a neighbouring unsolid node
local node = neighbours[i + 4] local node = neighbours[i + 4]
if node.name ~= "default:water_flowing" then if node.name ~= "default:water_flowing" then
local def = minetest.registered_nodes[node.name] local def = minetest.registered_nodes[node.name]
@ -70,53 +48,64 @@ local function get_flow_target(pos)
end end
end end
end end
-- No neighbour found
end
-- get_flow_target caches the results from get_flow_target_raw for 10 s
local flow_target_cache = {}
setmetatable(flow_target_cache, {__mode = "kv"})
local function get_flow_target(pos)
local vi = minetest.hash_node_position(pos)
local t = minetest.get_us_time()
if flow_target_cache[vi]
and t - flow_target_cache[vi][1] < 10 * 1000000 then
return flow_target_cache[vi][2]
end
local flow_target = get_flow_target_raw(pos)
flow_target_cache[vi] = {t, flow_target}
return flow_target
end end
local item_entity = minetest.registered_entities["__builtin:item"] local item_entity = minetest.registered_entities["__builtin:item"]
local old_on_step = item_entity.on_step or function()end local old_on_step = item_entity.on_step
item_entity.makes_footstep_sound = true item_entity.makes_footstep_sound = true
-- The "bt_" prefix shows that the value comes from builtin_item
item_entity.bt_timer = 0 item_entity.bt_timer = 0
item_entity.on_step = function(self, dtime, ...) item_entity.on_step = function(self, dtime, ...)
-- Remember the velocity before an original on_step can change it
local vel_desired
if self.bt_reset_velocity then
vel_desired = self.object:get_velocity()
end
old_on_step(self, dtime, ...) old_on_step(self, dtime, ...)
--~ if not self.physical_state then -- Ignore the item if it should not interact with physics
--~ return if not self.physical_state then
--~ end return
-- Force-adjust an acceleration and/or velocity if needed
if self.bt_acc
and not vector.equals(self.object:get_acceleration(), self.bt_acc) then
self.object:set_acceleration(self.bt_acc)
end
if self.bt_vel
and not vector.equals(self.object:get_velocity(), self.bt_vel) then
self.object:set_velocity(self.bt_vel)
end end
-- TODO: was ist pyhsical state? -- Reset the velocity if needed
if self.bt_phys ~= nil if vel_desired
and self.physical_state ~= self.bt_phys then and not vector.equals(self.object:get_velocity(), vel_desired) then
self.physical_state = self.bt_phys self.object:set_velocity(vel_desired)
self.object:set_properties({
physical = self.bt_phys
})
end end
-- For performance reasons, skip the remaining code except every second -- For performance reasons, skip the remaining code in frequent steps
self.bt_timer = self.bt_timer + dtime self.bt_timer = self.bt_timer + dtime
if self.bt_timer < 1 then if self.bt_timer < 0.1 then
return return
end end
self.bt_timer = 0 self.bt_timer = 0
local p = self.object:get_pos() local p = self.object:get_pos()
local pos = vector.round(p) local pos = vector.round(p)
local nodename = minetest.get_node(pos).name
local name = minetest.get_node(pos).name -- Burn the item if it is in lava
if name == "default:lava_flowing" if nodename == "default:lava_flowing"
or name == "default:lava_source" then or nodename == "default:lava_source" then
-- TODO: more generic burn cases
minetest.sound_play("builtin_item_lava", {pos=p}) minetest.sound_play("builtin_item_lava", {pos=p})
minetest.add_particlespawner({ minetest.add_particlespawner({
amount = 3, amount = 3,
@ -144,38 +133,30 @@ item_entity.on_step = function(self, dtime, ...)
return return
end end
local def = minetest.registered_nodes[name] if self.bt_reset_velocity then
if not def then -- Set the item acceleration to its default (changed again below)
self.object:set_acceleration({x=0, y=-movement_gravity, z=0})
self.bt_reset_velocity = nil
end
local def = minetest.registered_nodes[nodename]
if not def or not def.liquidtype or def.liquidtype ~= "flowing" then
return return
end end
-- Adjust the acceleration in liquid nodes local pos_next = get_flow_target(pos)
self.bt_acc = nil if not pos_next then
self.bt_vel = nil
if def.liquidtype then
-- Set the strongest acceleration when we are in the middle of the node
local acc_strength = 1.0 - ((p.y - 0.5) % 1.0) * 0.9
local acc = {x = 0, y = -acc_strength * get_gravity(), z = 0}
self.object:set_acceleration(acc)
self.bt_acc = acc
end
if def.liquidtype ~= "flowing" then
return return
end end
local vec = get_flow_target(pos) local vel_current = vel_desired or self.object:get_velocity()
if not vec then local acc = vector.multiply(vector.subtract(pos_next, pos), 2.0)
return if math.abs(vel_current.x) > 1.0 then
acc.x = 0
end end
local v = vector.add( if math.abs(vel_current.z) > 1.0 then
self.object:get_velocity(), acc.z = 0
vector.multiply(vector.subtract(vec, pos),.5) end
) acc.y = -movement_gravity
self.bt_vel = v self.object:set_acceleration(acc)
self.object:set_velocity(v) self.bt_reset_velocity = true
self.physical_state = true
self.bt_phys = true
self.object:set_properties({
physical = true
})
end end
minetest.register_entity(":__builtin:item", item_entity) minetest.register_entity(":__builtin:item", item_entity)