Add vine growth
This commit is contained in:
parent
185711d248
commit
063677a511
@ -312,9 +312,13 @@ for _, npc_type_table in pairs(npc_types) do
|
|||||||
elseif iname == "rp_farming:cotton_1" then
|
elseif iname == "rp_farming:cotton_1" then
|
||||||
if npc_type == "farmer" then
|
if npc_type == "farmer" then
|
||||||
say(S("Did you know cotton seed not only grow on dirt, but also on sand? But it still needs water."), name)
|
say(S("Did you know cotton seed not only grow on dirt, but also on sand? But it still needs water."), name)
|
||||||
|
elseif npc_type == "carpenter" then
|
||||||
|
say(S("If you have grown a cotton plant, try using scissors to perform a precise cut."), name)
|
||||||
else
|
else
|
||||||
say(S("Every kid knows seeds need soil, water and sunlight."), name)
|
say(S("Every kid knows seeds need soil, water and sunlight."), name)
|
||||||
end
|
end
|
||||||
|
elseif iname == "rp_farming:cotton" then
|
||||||
|
say(S("This can be used to make cotton bales.", name))
|
||||||
elseif iname == "rp_default:book" then
|
elseif iname == "rp_default:book" then
|
||||||
say(S("A truly epic story!"), name)
|
say(S("A truly epic story!"), name)
|
||||||
elseif iname == "rp_default:pearl" then
|
elseif iname == "rp_default:pearl" then
|
||||||
@ -421,6 +425,14 @@ for _, npc_type_table in pairs(npc_types) do
|
|||||||
else
|
else
|
||||||
say(S("Algae grow underwater in different heights."), name)
|
say(S("Algae grow underwater in different heights."), name)
|
||||||
end
|
end
|
||||||
|
elseif iname == "rp_default:vine" then
|
||||||
|
if npc_type == "farmer" then
|
||||||
|
say(S("Place it at the ceiling and watch it grow."), name)
|
||||||
|
elseif npc_type == "carpenter" then
|
||||||
|
say(S("If you want the vine to stops growing, make a precise cut using scissors."), name)
|
||||||
|
else
|
||||||
|
say(S("It's climbing time!"), name)
|
||||||
|
end
|
||||||
elseif iname == "rp_default:dirt" then
|
elseif iname == "rp_default:dirt" then
|
||||||
if npc_type == "farmer" then
|
if npc_type == "farmer" then
|
||||||
say(S("Many wild plants as well as wheat and cotton grow on dirt, but they grow better when it's fertilized."), name)
|
say(S("Many wild plants as well as wheat and cotton grow on dirt, but they grow better when it's fertilized."), name)
|
||||||
|
635
mods/rp_boats/init_shove_boat
Normal file
635
mods/rp_boats/init_shove_boat
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
local S = minetest.get_translator("rp_boats")
|
||||||
|
|
||||||
|
local STATE_INIT = 0 -- initial state (after spawning)
|
||||||
|
local STATE_FALLING = 1 -- free fall
|
||||||
|
local STATE_SINKING = 2 -- inside a liquid and sinking
|
||||||
|
local STATE_FLOATING = 3 -- floating on liquid, stable
|
||||||
|
local STATE_FLOATING_UP = 4 -- floating on liquid, correcting upwards
|
||||||
|
local STATE_FLOATING_DOWN = 5 -- floating on liquid, correcting downwards
|
||||||
|
local STATE_STUCK = 6 -- disable movement
|
||||||
|
|
||||||
|
local GRAVITY = tonumber(minetest.settings:get("movement_gravity")) or 9.81 --gravity
|
||||||
|
local LIQUID_SINK_SPEED = 1 -- how fast the boat will sink inside a liquid
|
||||||
|
local FLOAT_UP_SPEED = 0.5 -- how fast the boat will move upwards if slightly below liquid surface
|
||||||
|
local FLOAT_DOWN_SPEED = -0.5 -- how fast the boat will move downwards if slightly above liquid surface
|
||||||
|
|
||||||
|
local DRAG_FACTOR = 0.1 -- How fast the boat will slow down
|
||||||
|
local DRAG_FACTOR_HIGH = 1 -- Higher slow down rate when not swimming/floating
|
||||||
|
local DRAG_CONSTANT = 0.01
|
||||||
|
local DRAG_CONSTANT_HIGH = 0.1
|
||||||
|
|
||||||
|
local CHECK_NODES_AFTER_LANDING = 10 -- number of water nodes to check above boat if it has gotten deep
|
||||||
|
-- below the water surface after falling fast
|
||||||
|
|
||||||
|
local SNEAK_DETACHES = true -- if true, sneak key will detach player
|
||||||
|
|
||||||
|
local BOOST_TIME = 1.25
|
||||||
|
|
||||||
|
local is_water = function(nodename)
|
||||||
|
local def = minetest.registered_nodes[nodename]
|
||||||
|
if not def then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if def.liquidtype ~= "none" and minetest.get_item_group(nodename, "water") ~= 0 then
|
||||||
|
return true, def.liquidtype
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local set_driver = function(self, driver, orig_collisionbox)
|
||||||
|
self._driver = driver
|
||||||
|
local colbox = table.copy(orig_collisionbox)
|
||||||
|
|
||||||
|
-- Add player height to boat collisionbox top Y
|
||||||
|
-- so the player will also collide.
|
||||||
|
local dcolbox = driver:get_properties().collisionbox
|
||||||
|
colbox[5] = colbox[5] + (dcolbox[5] - dcolbox[2])
|
||||||
|
local props = self.object:get_properties()
|
||||||
|
props.collisionbox = colbox
|
||||||
|
self.object:set_properties(props)
|
||||||
|
end
|
||||||
|
|
||||||
|
local unset_driver = function(self, orig_collisionbox)
|
||||||
|
self._driver = nil
|
||||||
|
local colbox = table.copy(orig_collisionbox)
|
||||||
|
local props = self.object:get_properties()
|
||||||
|
props.collisionbox = colbox
|
||||||
|
self.object:set_properties(props)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns false if not enough space to mount boat at pos
|
||||||
|
local check_space = function(pos, player, side, top)
|
||||||
|
local tiny = 0.01
|
||||||
|
local side = side - tiny
|
||||||
|
local bottom = -tiny
|
||||||
|
local top = top + tiny
|
||||||
|
|
||||||
|
local offsets = {
|
||||||
|
-- Format: { Xmin, Ymin, Zmin, Xmax, Ymax, Zmax }
|
||||||
|
-- middle vertical ray
|
||||||
|
{ 0, bottom, 0, 0, top, 0 },
|
||||||
|
-- for testing the 4 vertical edges of the collisionbox
|
||||||
|
{ -side, bottom, -side, -side, top, -side },
|
||||||
|
{ -side, bottom, side, -side, top, side },
|
||||||
|
{ side, bottom, -side, side, top, -side },
|
||||||
|
{ side, bottom, side, side, top, side },
|
||||||
|
}
|
||||||
|
-- Finally check the rays
|
||||||
|
for i=1, #offsets do
|
||||||
|
local off_start = vector.new(offsets[i][1], offsets[i][2], offsets[i][3])
|
||||||
|
local off_end = vector.new(offsets[i][4], offsets[i][5], offsets[i][6])
|
||||||
|
local ray_start = vector.add(pos, off_start)
|
||||||
|
local ray_end = vector.add(pos, off_end)
|
||||||
|
local ray = minetest.raycast(ray_start, ray_end, false, false)
|
||||||
|
local on_ground_only = true
|
||||||
|
local collide = false
|
||||||
|
while true do
|
||||||
|
local thing = ray:next()
|
||||||
|
if not thing then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if thing.type == "node" then
|
||||||
|
local node = minetest.get_node(thing.under)
|
||||||
|
local def = minetest.registered_nodes[node.name]
|
||||||
|
if def and def.walkable then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- detaches driver of boat 'self'.
|
||||||
|
-- assumes that the boat has an active driver
|
||||||
|
local detach_driver = function(self, detach_offset_y)
|
||||||
|
local driver = self._driver
|
||||||
|
driver:set_detach()
|
||||||
|
-- Put driver slightly above the boat
|
||||||
|
local dpos = vector.add(vector.new(0, detach_offset_y, 0), self.object:get_pos())
|
||||||
|
minetest.after(0.1, function(param)
|
||||||
|
if not param.driver or not param.driver:is_player() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
param.driver:set_pos(param.pos)
|
||||||
|
end, {driver=driver, pos=dpos})
|
||||||
|
end
|
||||||
|
|
||||||
|
local register_boat = function(name, def)
|
||||||
|
local itemstring = "rp_boats:"..name
|
||||||
|
if not def.attach_offset then
|
||||||
|
def.attach_offset = {x=0, y=0, z=0}
|
||||||
|
end
|
||||||
|
if not def.float_offset then
|
||||||
|
def.float_offset = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_entity(itemstring, {
|
||||||
|
physical = true,
|
||||||
|
collide_with_objects = true,
|
||||||
|
visual = "mesh",
|
||||||
|
|
||||||
|
collisionbox = def.collisionbox,
|
||||||
|
selectionbox = def.selectionbox,
|
||||||
|
textures = def.textures,
|
||||||
|
mesh = def.mesh,
|
||||||
|
hp_max = def.hp_max or 4,
|
||||||
|
|
||||||
|
_state = STATE_INIT,
|
||||||
|
_driver = nil,
|
||||||
|
_speed = 0,
|
||||||
|
_boost_timer = BOOST_TIME,
|
||||||
|
|
||||||
|
on_activate = function(self, staticdata, dtime_s)
|
||||||
|
local data = minetest.deserialize(staticdata)
|
||||||
|
if data then
|
||||||
|
self._state = data._state or STATE_FALLING
|
||||||
|
self._speed = data._speed or 0
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
get_staticdata = function(self)
|
||||||
|
local data = {
|
||||||
|
_state = self._state,
|
||||||
|
_speed = self._speed,
|
||||||
|
}
|
||||||
|
return minetest.serialize(data)
|
||||||
|
end,
|
||||||
|
on_step = function(self, dtime, moveresult)
|
||||||
|
local mypos_precise = self.object:get_pos()
|
||||||
|
local mypos = table.copy(mypos_precise)
|
||||||
|
mypos.y = math.floor(mypos.y)
|
||||||
|
local mynode = minetest.get_node(mypos)
|
||||||
|
local mydef = minetest.registered_nodes[mynode.name]
|
||||||
|
local mypos_below = vector.add(mypos, {x=0,y=-1,z=0})
|
||||||
|
local mynode_below = minetest.get_node(mypos_below)
|
||||||
|
local mydef_below = minetest.registered_nodes[mynode_below.name]
|
||||||
|
local mypos_above = vector.add(mypos, {x=0,y=1,z=0})
|
||||||
|
local mynode_above = minetest.get_node(mypos_above)
|
||||||
|
local mydef_above = minetest.registered_nodes[mynode_above.name]
|
||||||
|
local mypos_above2 = vector.add(mypos, {x=0,y=2,z=0})
|
||||||
|
local mynode_above2 = minetest.get_node(mypos_above2)
|
||||||
|
local mydef_above2 = minetest.registered_nodes[mynode_above2.name]
|
||||||
|
|
||||||
|
local curvel = self.object:get_velocity()
|
||||||
|
local v = curvel * math.sign(self._speed)
|
||||||
|
self._speed = math.sqrt(v.x ^ 2 + v.z ^ 2)
|
||||||
|
|
||||||
|
if self._boost_timer > 0 then
|
||||||
|
self._boost_timer = self._boost_timer - dtime
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update boat state (for Y movement)
|
||||||
|
if mydef and mydef_below and mydef_above then
|
||||||
|
local above2_water, a2lt = is_water(mynode_above2.name)
|
||||||
|
local above_water, alt = is_water(mynode_above.name)
|
||||||
|
local here_water, hlt = is_water(mynode.name)
|
||||||
|
local below_water, blt = is_water(mynode_below.name)
|
||||||
|
if here_water or below_water then
|
||||||
|
local water_y -- y of water surface
|
||||||
|
local tlt
|
||||||
|
if above2_water then
|
||||||
|
water_y = mypos_above2.y
|
||||||
|
tlt = a2lt
|
||||||
|
elseif above_water then
|
||||||
|
water_y = mypos_above.y
|
||||||
|
tlt = alt
|
||||||
|
elseif here_water then
|
||||||
|
water_y = mypos.y
|
||||||
|
tlt = hlt
|
||||||
|
elseif below_water then
|
||||||
|
water_y = mypos_below.y
|
||||||
|
tlt = blt
|
||||||
|
end
|
||||||
|
if tlt == "flowing" then
|
||||||
|
water_y = water_y - 0.5
|
||||||
|
end
|
||||||
|
local ydiff = mypos_precise.y - (water_y + 1) -- y difference between boat and water surface
|
||||||
|
local yvel = 0
|
||||||
|
local TOL = 0.01 -- tolerance
|
||||||
|
|
||||||
|
--[[ Special check for when boat got into water after
|
||||||
|
falling. Checks for a water surface
|
||||||
|
above and teleport the node back to surface if it exists.
|
||||||
|
This is because if the boat fell on the water at a high
|
||||||
|
speed, it might have "passed" the water surface so
|
||||||
|
the chance of sinking is pretty high.
|
||||||
|
This code should (mostly) ensure that boats will
|
||||||
|
land on the surface of the water rather than sinking if falling
|
||||||
|
into at high speed.
|
||||||
|
|
||||||
|
If the boat was ULTRA fast, the boat might *not* teleport
|
||||||
|
to surface because it was too deep below the surface and we only
|
||||||
|
check for a limited number of nodes. This is acceptable tho.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
* self: Boat object
|
||||||
|
* pos: Boat pos
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
* true if was teleported to water surface, false otherwise ]]
|
||||||
|
local function land_on_water(self, pos)
|
||||||
|
-- Boat must've been in falling state before
|
||||||
|
-- for the check to matter at all.
|
||||||
|
if self._state == STATE_FALLING then
|
||||||
|
-- Boat was falling, this implies we might "land" on water
|
||||||
|
local check_pos = table.copy(pos)
|
||||||
|
local float = false
|
||||||
|
local offset = 0
|
||||||
|
-- Check for nodes above the boat
|
||||||
|
for k=1,CHECK_NODES_AFTER_LANDING do
|
||||||
|
offset = k
|
||||||
|
check_pos.y = check_pos.y + 1
|
||||||
|
local cnode = minetest.get_node(check_pos)
|
||||||
|
if not is_water(cnode.name) then
|
||||||
|
-- Non-water node found! We will teleport
|
||||||
|
float = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if float then
|
||||||
|
-- Teleport boat to surface
|
||||||
|
local newpos = self.object:get_pos()
|
||||||
|
newpos.y = newpos.y + offset
|
||||||
|
self.object:set_pos(newpos)
|
||||||
|
-- Note: The caller still needs to update boat state manually
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update boat state
|
||||||
|
if above_water and (alt == "source" or above2_water) then
|
||||||
|
-- Sink if in water, float if boat has landed on water
|
||||||
|
if land_on_water(self, mypos) then
|
||||||
|
self._STATE = STATE_FLOATING
|
||||||
|
else
|
||||||
|
self._state = STATE_SINKING
|
||||||
|
end
|
||||||
|
-- Adjust boat Y position up/down when close to the water surface
|
||||||
|
elseif ydiff < def.float_max and ydiff > def.float_offset + TOL then
|
||||||
|
self._state = STATE_FLOATING_DOWN
|
||||||
|
elseif ydiff > def.float_min and ydiff < def.float_offset - TOL then
|
||||||
|
self._state = STATE_FLOATING_UP
|
||||||
|
-- Boat is at water surface, no Y adjustment needed (this is the "normal" floating state)
|
||||||
|
elseif ydiff > def.float_offset - TOL and ydiff < def.float_offset + TOL then
|
||||||
|
self._state = STATE_FLOATING
|
||||||
|
elseif ydiff < def.float_min then
|
||||||
|
-- Sink if in water, float if boat has landed on water
|
||||||
|
if land_on_water(self, mypos) then
|
||||||
|
self._state = STATE_FLOATING
|
||||||
|
else
|
||||||
|
self._state = STATE_SINKING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self._state = STATE_FALLING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self._state = STATE_FALLING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Should only happen if boat is inside unknown nodes
|
||||||
|
self._state = STATE_STUCK
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Boat controls
|
||||||
|
|
||||||
|
local horvel = {x=0, y=0, z=0}
|
||||||
|
local vertvel = {x=0, y=0, z=0}
|
||||||
|
local vertacc = {x=0, y=0, z=0}
|
||||||
|
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
local v = self._speed
|
||||||
|
local moved = false
|
||||||
|
if self._driver then
|
||||||
|
if not self._driver:is_player() then
|
||||||
|
unset_driver(self, def.collisionbox)
|
||||||
|
else
|
||||||
|
-- Read player controls
|
||||||
|
local ctrl = self._driver:get_player_control()
|
||||||
|
-- Sneak = detach
|
||||||
|
if SNEAK_DETACHES and ctrl.sneak then
|
||||||
|
detach_driver(self, def.detach_offset_y)
|
||||||
|
end
|
||||||
|
-- Left/right = Rotate left/right
|
||||||
|
if ctrl.left and not ctrl.right then
|
||||||
|
yaw = yaw + def.yaw_change_rate * dtime
|
||||||
|
self.object:set_yaw(yaw)
|
||||||
|
elseif ctrl.right and not ctrl.left then
|
||||||
|
yaw = yaw - def.yaw_change_rate * dtime
|
||||||
|
self.object:set_yaw(yaw)
|
||||||
|
end
|
||||||
|
local floating = self._state == STATE_FLOATING or self._state == STATE_FLOATING_UP or self._state == STATE_FLOATING_DOWN
|
||||||
|
|
||||||
|
do
|
||||||
|
-- Up = speed up
|
||||||
|
if ctrl.up and not ctrl.down then
|
||||||
|
if floating then
|
||||||
|
v = math.min(def.max_speed, v + def.speed_change_rate * dtime)
|
||||||
|
moved = true
|
||||||
|
elseif self._boost_timer <= 0 then
|
||||||
|
v = def.max_speed
|
||||||
|
moved = true
|
||||||
|
end
|
||||||
|
-- Down = slow down
|
||||||
|
elseif ctrl.down and not ctrl.up then
|
||||||
|
v = math.max(-def.max_speed, v - def.speed_change_rate * dtime)
|
||||||
|
moved = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Slow down boat if not moved by driver
|
||||||
|
if moved then
|
||||||
|
self._boost_timer = BOOST_TIME
|
||||||
|
else
|
||||||
|
local f, c
|
||||||
|
if self._state ~= STATE_FLOATING and self._state ~= STATE_FLOATING_UP and self._state ~= STATE_FLOATING_DOWN then
|
||||||
|
-- Higher slow down rate when not floating on liquid
|
||||||
|
f = DRAG_FACTOR_HIGH
|
||||||
|
c = DRAG_CONSTANT_HIGH
|
||||||
|
else
|
||||||
|
f = DRAG_FACTOR
|
||||||
|
c = DRAG_CONSTANT
|
||||||
|
end
|
||||||
|
local drag = dtime * math.sign(v) * (c + f * v * v)
|
||||||
|
if math.abs(v) <= math.abs(drag) then
|
||||||
|
v = 0
|
||||||
|
else
|
||||||
|
v = v - drag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if math.abs(v) < 0.001 then
|
||||||
|
v = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self._speed = v
|
||||||
|
local get_horvel = function(v, yaw)
|
||||||
|
local x = -math.sin(yaw) * v
|
||||||
|
local z = math.cos(yaw) * v
|
||||||
|
return {x=x, y=0, z=z}
|
||||||
|
end
|
||||||
|
horvel = get_horvel(v, yaw)
|
||||||
|
do
|
||||||
|
if self._state == STATE_FALLING then
|
||||||
|
vertacc = {x=0, y=-GRAVITY, z=0}
|
||||||
|
vertvel = {x=0, y=curvel.y, z=0}
|
||||||
|
elseif self._state == STATE_SINKING then
|
||||||
|
vertacc = {x=0, y=0, z=0}
|
||||||
|
vertvel = {x=0, y=-LIQUID_SINK_SPEED, z=0}
|
||||||
|
elseif self._state == STATE_STUCK then
|
||||||
|
vertacc = {x=0, y=0, z=0}
|
||||||
|
vertvel = {x=0, y=0, z=0}
|
||||||
|
elseif self._state == STATE_FLOATING then
|
||||||
|
vertacc = {x=0, y=0, z=0}
|
||||||
|
vertvel = {x=0, y=0, z=0}
|
||||||
|
elseif self._state == STATE_FLOATING_UP then
|
||||||
|
vertacc = {x=0, y=0, z=0}
|
||||||
|
vertvel = {x=0, y=FLOAT_UP_SPEED, z=0}
|
||||||
|
elseif self._state == STATE_FLOATING_DOWN then
|
||||||
|
vertacc = {x=0, y=0, z=0}
|
||||||
|
vertvel = {x=0, y=FLOAT_DOWN_SPEED, z=0}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.object:set_acceleration(vertacc)
|
||||||
|
self.object:set_velocity(vector.add(horvel, vertvel))
|
||||||
|
end,
|
||||||
|
on_rightclick = function(self, clicker)
|
||||||
|
if clicker and clicker:is_player() then
|
||||||
|
local cname = clicker:get_player_name()
|
||||||
|
if self._driver then
|
||||||
|
if self._driver == clicker then
|
||||||
|
-- Detach driver
|
||||||
|
detach_driver(self, def.detach_offset_y)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if clicker:get_attach() == nil then
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
if not check_space(pos, clicker, 0.49, 2) then
|
||||||
|
minetest.chat_send_player(
|
||||||
|
clicker:get_player_name(),
|
||||||
|
minetest.colorize("#FFFF00", S("Not enough space to enter!")))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
minetest.log("action", "[rp_boats] "..cname.." attaches to boat at "..minetest.pos_to_string(self.object:get_pos(),1))
|
||||||
|
set_driver(self, clicker, def.collisionbox)
|
||||||
|
rp_player.player_attached[cname] = true
|
||||||
|
self._driver:set_attach(self.object, "", def.attach_offset, {x=0,y=0,z=0}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_detach_child = function(self, child)
|
||||||
|
if child and child == self._driver then
|
||||||
|
local cname = child:get_player_name()
|
||||||
|
minetest.log("action", "[rp_boats] "..cname.." detaches from boat at "..minetest.pos_to_string(self.object:get_pos(),1))
|
||||||
|
rp_player.player_attached[cname] = false
|
||||||
|
unset_driver(self, def.collisionbox)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_death = function(self, killer)
|
||||||
|
minetest.log("action", "[rp_boats] Boat dies at "..minetest.pos_to_string(self.object:get_pos(),1))
|
||||||
|
-- Drop boat item (except in Creative Mode)
|
||||||
|
if killer and killer:is_player() and minetest.is_creative_enabled(killer:get_player_name()) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
minetest.add_item(self.object:get_pos(), itemstring)
|
||||||
|
end,
|
||||||
|
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
|
||||||
|
if damage >= 1 then
|
||||||
|
-- TODO: Add custom sound
|
||||||
|
minetest.sound_play({name = "default_dig_hard"}, {pos=self.object:get_pos()}, true)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_craftitem(itemstring, {
|
||||||
|
description = def.description,
|
||||||
|
_tt_help = def._tt_help,
|
||||||
|
liquids_pointable = true,
|
||||||
|
groups = { boat = 1 },
|
||||||
|
inventory_image = def.inventory_image,
|
||||||
|
wield_image = def.wield_image,
|
||||||
|
on_place = function(itemstack, placer, pointed_thing)
|
||||||
|
-- Boilerplace to handle pointed node's rightclick handler
|
||||||
|
if not placer or not placer:is_player() then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
if pointed_thing.type ~= "node" then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
local pos1 = pointed_thing.above
|
||||||
|
local node1 = minetest.get_node(pos1)
|
||||||
|
local ndef1 = minetest.registered_nodes[node1.name]
|
||||||
|
local pos2 = pointed_thing.under
|
||||||
|
local node2 = minetest.get_node(pos2)
|
||||||
|
local ndef2 = minetest.registered_nodes[node2.name]
|
||||||
|
if ndef2 and ndef2.on_rightclick and
|
||||||
|
((not placer) or (placer and not placer:get_player_control().sneak)) then
|
||||||
|
return ndef2.on_rightclick(pointed_thing.under, node2, placer, itemstack,
|
||||||
|
pointed_thing) or itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get place position
|
||||||
|
local place_pos = table.copy(pos1)
|
||||||
|
|
||||||
|
-- Offset when placed sideways
|
||||||
|
local spo = def.sideways_place_offset
|
||||||
|
if spo then
|
||||||
|
if pointed_thing.under.x > pointed_thing.above.x then
|
||||||
|
place_pos.x = place_pos.x - spo
|
||||||
|
elseif pointed_thing.under.x < pointed_thing.above.x then
|
||||||
|
place_pos.x = place_pos.x + spo
|
||||||
|
end
|
||||||
|
if pointed_thing.under.z > pointed_thing.above.z then
|
||||||
|
place_pos.z = place_pos.z - spo
|
||||||
|
elseif pointed_thing.under.z < pointed_thing.above.z then
|
||||||
|
place_pos.z = place_pos.z + spo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local on_liquid = false
|
||||||
|
if pos1.x == pos2.x and pos1.z == pos2.z and ndef2 and ndef2.liquidtype ~= "none" and minetest.get_item_group(node2.name, "fake_liquid") == 0 then
|
||||||
|
place_pos = vector.add(place_pos, {x=0, y=def.float_offset, z=0})
|
||||||
|
on_liquid = true
|
||||||
|
end
|
||||||
|
if ndef1 and not ndef1.walkable then
|
||||||
|
-- Optional function to check for available space for boat
|
||||||
|
if def.check_boat_space then
|
||||||
|
local res = def.check_boat_space(place_pos, on_liquid) -- returns true if enough space, false otherwise
|
||||||
|
if not res then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Place boat
|
||||||
|
local ent = minetest.add_entity(place_pos, itemstring)
|
||||||
|
if ent then
|
||||||
|
-- TODO: Add custom sound
|
||||||
|
minetest.sound_play({name = "default_place_node_hard"}, {pos=place_pos}, true)
|
||||||
|
ent:set_yaw(placer:get_look_horizontal())
|
||||||
|
minetest.log("action", "[rp_boats] "..placer:get_player_name().." spawns rp_boats:"..name.." at "..minetest.pos_to_string(place_pos, 1))
|
||||||
|
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Register boats
|
||||||
|
local log_boats = {
|
||||||
|
{ "wood", S("Wooden Log Boat"), "rp_default:tree" },
|
||||||
|
{ "birch", S("Birch Log Boat"), "rp_default:tree_birch" },
|
||||||
|
{ "oak", S("Oak Log Boat"), "rp_default:tree_oak" },
|
||||||
|
}
|
||||||
|
for l=1, #log_boats do
|
||||||
|
local id = log_boats[l][1]
|
||||||
|
register_boat("log_boat_"..id, {
|
||||||
|
description = log_boats[l][2],
|
||||||
|
_tt_help = S("Water vehicle"),
|
||||||
|
collisionbox = { -0.49, -0.49, -0.49, 0.49, 0.49, 0.49 },
|
||||||
|
selectionbox = { -0.6, -0.501, -0.6, 0.6, 0.501, 0.6 },
|
||||||
|
inventory_image = "rp_boats_boat_log_"..id.."_item.png",
|
||||||
|
wield_image = "rp_boats_boat_log_"..id.."_item.png",
|
||||||
|
textures = {
|
||||||
|
"rp_boats_boat_log_"..id.."_side.png",
|
||||||
|
"rp_boats_boat_log_"..id.."_end.png",
|
||||||
|
"rp_boats_boat_log_"..id.."_inner_side.png",
|
||||||
|
"rp_boats_boat_log_"..id.."_inner_end.png",
|
||||||
|
"rp_boats_boat_log_"..id.."_inner.png",
|
||||||
|
"rp_boats_boat_log_"..id.."_side.png",
|
||||||
|
},
|
||||||
|
mesh = "rp_boats_log_boat.obj",
|
||||||
|
hp_max = 4,
|
||||||
|
|
||||||
|
float_max = 0.0,
|
||||||
|
float_offset = -0.3,
|
||||||
|
float_min = -0.85,
|
||||||
|
attach_offset = { x=0, y=0, z=0 },
|
||||||
|
max_speed = 3.8,
|
||||||
|
speed_change_rate = 1.5,
|
||||||
|
yaw_change_rate = 0.6,
|
||||||
|
detach_offset_y = 0.8,
|
||||||
|
})
|
||||||
|
crafting.register_craft({
|
||||||
|
output = "rp_boats:log_boat_"..id,
|
||||||
|
items = {
|
||||||
|
log_boats[l][3] .. " 2",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local rafts = {
|
||||||
|
{ "wood", S("Wooden Raft"), "rp_default:planks" },
|
||||||
|
{ "birch", S("Birch Raft"), "rp_default:planks_birch" },
|
||||||
|
{ "oak", S("Oak Raft"), "rp_default:planks_oak" },
|
||||||
|
}
|
||||||
|
for r=1, #rafts do
|
||||||
|
local id = rafts[r][1]
|
||||||
|
register_boat("raft_"..id, {
|
||||||
|
description = rafts[r][2],
|
||||||
|
_tt_help = S("Water vehicle"),
|
||||||
|
collisionbox = { -0.74, -0.3, -0.74, 0.74, 0.1, 0.74 },
|
||||||
|
selectionbox = { -0.85, -0.301, -0.85, 0.85, 0.101, 0.85 },
|
||||||
|
inventory_image = "rp_boats_boat_raft_"..id.."_item.png",
|
||||||
|
wield_image = "rp_boats_boat_raft_"..id.."_item.png",
|
||||||
|
textures = {
|
||||||
|
"rp_boats_boat_raft_"..id..".png",
|
||||||
|
"rp_boats_boat_raft_"..id..".png",
|
||||||
|
"rp_boats_boat_raft_"..id.."_side.png",
|
||||||
|
"rp_boats_boat_raft_"..id.."_mini.png",
|
||||||
|
"rp_boats_boat_raft_"..id.."_front.png",
|
||||||
|
"rp_boats_boat_raft_"..id.."_back.png",
|
||||||
|
},
|
||||||
|
mesh = "rp_boats_raft.obj",
|
||||||
|
hp_max = 4,
|
||||||
|
|
||||||
|
float_max = -0.201,
|
||||||
|
float_offset = -0.401,
|
||||||
|
float_min = -1.001,
|
||||||
|
|
||||||
|
attach_offset = { x=0, y=1, z=0 },
|
||||||
|
max_speed = 6,
|
||||||
|
speed_change_rate = 1.5,
|
||||||
|
yaw_change_rate = 0.3,
|
||||||
|
detach_offset_y = 0.2,
|
||||||
|
check_boat_space = function(place_pos, on_liquid)
|
||||||
|
local ymin = 0
|
||||||
|
if on_liquid then
|
||||||
|
ymin = -1
|
||||||
|
end
|
||||||
|
for x=-1,1 do
|
||||||
|
for y=ymin,0 do
|
||||||
|
for z=-1,1 do
|
||||||
|
local pnode = minetest.get_node(vector.add({x=x, y=y, z=z}, place_pos))
|
||||||
|
local pdef = minetest.registered_nodes[pnode.name]
|
||||||
|
if pdef and pdef.walkable then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
sideways_place_offset = 1.0,
|
||||||
|
})
|
||||||
|
crafting.register_craft({
|
||||||
|
output = "rp_boats:raft_"..id,
|
||||||
|
items = {
|
||||||
|
rafts[r][3] .. " 8",
|
||||||
|
"rp_default:fiber 10",
|
||||||
|
"rp_default:stick 5",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
type = "fuel",
|
||||||
|
recipe = "group:boat",
|
||||||
|
burntime = 30,
|
||||||
|
})
|
||||||
|
|
@ -971,6 +971,30 @@ minetest.register_abm({
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
-- Grow vine
|
||||||
|
minetest.register_abm(
|
||||||
|
{
|
||||||
|
label = "Grow vines",
|
||||||
|
name = "rp_default:grow_vines",
|
||||||
|
nodenames = {"rp_default:vine"},
|
||||||
|
interval = 21,
|
||||||
|
chance = 120,
|
||||||
|
action = function(pos, node)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local age = node.param2
|
||||||
|
if node.param2 == 0 or node.param2 >= default.VINE_MAX_AGE then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local below = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||||
|
local nbelow = minetest.get_node(below)
|
||||||
|
if nbelow.name == "air" then
|
||||||
|
age = math.min(default.VINE_MAX_AGE, age + 1)
|
||||||
|
minetest.set_node(below, {name="rp_default:vine", param2 = age})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
-- Grow algae
|
-- Grow algae
|
||||||
minetest.register_abm(
|
minetest.register_abm(
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,9 @@ default.WEAK_TORCH_MAX_TIMER = 360
|
|||||||
-- is reduced by 100s*0.1 = 10s.
|
-- is reduced by 100s*0.1 = 10s.
|
||||||
default.SAPLING_FERTILIZER_TIME_BONUS_FACTOR = 0.1
|
default.SAPLING_FERTILIZER_TIME_BONUS_FACTOR = 0.1
|
||||||
|
|
||||||
|
-- Maximum 'age' of a vine (determines how long it'll grow)
|
||||||
|
default.VINE_MAX_AGE = 20
|
||||||
|
|
||||||
minetest.nodedef_default.stack_max = 60
|
minetest.nodedef_default.stack_max = 60
|
||||||
minetest.craftitemdef_default.stack_max = 60
|
minetest.craftitemdef_default.stack_max = 60
|
||||||
|
|
||||||
|
@ -113,13 +113,37 @@ minetest.register_node(
|
|||||||
groups = {_attached_node_top = 1, snappy = 3, plant = 1, vine = 1},
|
groups = {_attached_node_top = 1, snappy = 3, plant = 1, vine = 1},
|
||||||
sounds = rp_sounds.node_sound_leaves_defaults(),
|
sounds = rp_sounds.node_sound_leaves_defaults(),
|
||||||
after_dig_node = function(pos, node, metadata, digger)
|
after_dig_node = function(pos, node, metadata, digger)
|
||||||
|
-- Set random age of vine above, it it exists
|
||||||
|
local above = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||||
|
local aboven = minetest.get_node(above)
|
||||||
|
if aboven.name == "rp_default:vine" then
|
||||||
|
minetest.set_node(above, {name="rp_default:vine", param2 = math.random(1, default.VINE_MAX_AGE)})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detach vines below
|
||||||
util.dig_down(pos, node, digger)
|
util.dig_down(pos, node, digger)
|
||||||
end,
|
end,
|
||||||
on_flood = function(pos, oldnode, newnode)
|
on_flood = function(pos, oldnode, newnode)
|
||||||
|
-- Set random age of vine above, it it exists
|
||||||
|
local above = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||||
|
local aboven = minetest.get_node(above)
|
||||||
|
if aboven.name == "rp_default:vine" then
|
||||||
|
minetest.set_node(above, {name="rp_default:vine", param2 = math.random(1, default.VINE_MAX_AGE)})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Drop vine as item and detach vines below
|
||||||
minetest.add_item(pos, "rp_default:vine")
|
minetest.add_item(pos, "rp_default:vine")
|
||||||
util.dig_down(pos, oldnode, nil, "rp_default:vine")
|
util.dig_down(pos, oldnode, nil, "rp_default:vine")
|
||||||
end,
|
end,
|
||||||
on_blast = function(pos)
|
on_blast = function(pos)
|
||||||
|
-- Set random age of vine above, it it exists
|
||||||
|
local above = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||||
|
local aboven = minetest.get_node(above)
|
||||||
|
if aboven.name == "rp_default:vine" then
|
||||||
|
minetest.set_node(above, {name="rp_default:vine", param2 = math.random(1, default.VINE_MAX_AGE)})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Destroy the blasted node and detach vines below
|
||||||
local oldnode = minetest.get_node(pos)
|
local oldnode = minetest.get_node(pos)
|
||||||
minetest.remove_node(pos)
|
minetest.remove_node(pos)
|
||||||
util.dig_down(pos, oldnode)
|
util.dig_down(pos, oldnode)
|
||||||
@ -150,8 +174,24 @@ minetest.register_node(
|
|||||||
return itemstack
|
return itemstack
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local age = ceilingnode.param2
|
||||||
|
-- Update age
|
||||||
|
if ceilingnode.name == "rp_default:vine" then
|
||||||
|
-- Vine extended: Increase age by 1 or set random age
|
||||||
|
local meta_c = minetest.get_meta(place_floor)
|
||||||
|
if age > 0 then
|
||||||
|
local meta_new = minetest.get_meta(place_in)
|
||||||
|
age = math.min(default.VINE_MAX_AGE, age + 1)
|
||||||
|
else
|
||||||
|
age = math.random(1, default.VINE_MAX_AGE)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- New vine: Set random age
|
||||||
|
age = math.random(1, default.VINE_MAX_AGE)
|
||||||
|
end
|
||||||
|
|
||||||
-- Place vine
|
-- Place vine
|
||||||
minetest.set_node(place_in, {name = itemstack:get_name()})
|
minetest.set_node(place_in, {name = itemstack:get_name(), param2 = age})
|
||||||
|
|
||||||
-- Reduce item count
|
-- Reduce item count
|
||||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||||
@ -160,6 +200,31 @@ minetest.register_node(
|
|||||||
|
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
|
_on_trim = function(pos, node, player, itemstack)
|
||||||
|
-- Cut the vine, set age of a remaining vine to 0 to stop growth
|
||||||
|
-- Dig vine
|
||||||
|
minetest.remove_node(pos)
|
||||||
|
local is_creative = minetest.is_creative_enabled(player:get_player_name())
|
||||||
|
if not is_creative then
|
||||||
|
item_drop.drop_item(pos, "rp_default:vine")
|
||||||
|
end
|
||||||
|
util.dig_down(pos, node)
|
||||||
|
minetest.sound_play({name = "default_shears_cut", gain = 0.5}, {pos = player:get_pos(), max_hear_distance = 8}, true)
|
||||||
|
|
||||||
|
-- Reset age of vine above, if present
|
||||||
|
local above = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||||
|
local aboven = minetest.get_node(above)
|
||||||
|
if aboven.name == "rp_default:vine" then
|
||||||
|
minetest.set_node(above, {name="rp_default:vine", param2=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add tool wear
|
||||||
|
if not is_creative then
|
||||||
|
local def = itemstack:get_definition()
|
||||||
|
itemstack:add_wear_by_uses(def.tool_capabilities.groupcaps.snappy.uses)
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Fern
|
-- Fern
|
||||||
|
@ -248,7 +248,7 @@ local tt_pick = S("Digs hard, cracky blocks")
|
|||||||
local tt_shovel = S("Digs soft, crumbly blocks")
|
local tt_shovel = S("Digs soft, crumbly blocks")
|
||||||
local tt_axe = S("Chops wood")
|
local tt_axe = S("Chops wood")
|
||||||
local tt_spear = S("Melee weapon")
|
local tt_spear = S("Melee weapon")
|
||||||
local tt_shears = S("Cuts leaves and plants and shears sheep")
|
local tt_shears = S("Cuts leaves and plants and shears sheep").."\n"..S("“Place” key: Precise cut")
|
||||||
|
|
||||||
-- Pickaxes
|
-- Pickaxes
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user