1 2-- 3-- Helper functions 4-- 5 6local function is_water(pos) 7 local nn = minetest.get_node(pos).name 8 return minetest.get_item_group(nn, "water") ~= 0 9end 10 11local function get_sign(i) 12 if i == 0 then 13 return 0 14 else 15 return i / math.abs(i) 16 end 17end 18 19local function get_velocity(v, yaw, y) 20 local x = -math.sin(yaw) * v 21 local z = math.cos(yaw) * v 22 return {x = x, y = y, z = z} 23end 24 25local function get_v(v) 26 return math.sqrt(v.x ^ 2 + v.z ^ 2) 27end 28 29-- 30-- Boat entity 31-- 32 33local boat = { 34 physical = true, 35 collisionbox = {-0.5, -0.4, -0.5, 0.5, 0.3, 0.5}, 36 visual = "mesh", 37 mesh = "boat.x", 38 textures = {"default_wood.png"}, 39 40 driver = nil, 41 v = 0, 42 last_v = 0, 43 removed = false 44} 45 46function boat.on_rightclick(self, clicker) 47 if not clicker or not clicker:is_player() then 48 return 49 end 50 local name = clicker:get_player_name() 51 if self.driver and clicker == self.driver then 52 self.driver = nil 53 clicker:set_detach() 54 default.player_attached[name] = false 55 default.player_set_animation(clicker, "stand" , 30) 56 elseif not self.driver then 57 self.driver = clicker 58 clicker:set_attach(self.object, "", {x = 0, y = 11, z = -3}, {x = 0, y = 0, z = 0}) 59 default.player_attached[name] = true 60 minetest.after(0.2, function() 61 default.player_set_animation(clicker, "sit" , 30) 62 end) 63 self.object:setyaw(clicker:get_look_yaw() - math.pi / 2) 64 end 65end 66 67function boat.on_activate(self, staticdata, dtime_s) 68 self.object:set_armor_groups({immortal = 1}) 69 if staticdata then 70 self.v = tonumber(staticdata) 71 end 72 self.last_v = self.v 73end 74 75function boat.get_staticdata(self) 76 return tostring(self.v) 77end 78 79function boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, direction) 80 if not puncher or not puncher:is_player() or self.removed then 81 return 82 end 83 if self.driver and puncher == self.driver then 84 self.driver = nil 85 puncher:set_detach() 86 default.player_attached[puncher:get_player_name()] = false 87 end 88 if not self.driver then 89 self.removed = true 90 -- delay remove to ensure player is detached 91 minetest.after(0.1, function() 92 self.object:remove() 93 end) 94 if not minetest.setting_getbool("creative_mode") then 95 puncher:get_inventory():add_item("main", "boats:boat") 96 end 97 end 98end 99 100function boat.on_step(self, dtime) 101 self.v = get_v(self.object:getvelocity()) * get_sign(self.v) 102 if self.driver then 103 local ctrl = self.driver:get_player_control() 104 local yaw = self.object:getyaw() 105 if ctrl.up then 106 self.v = self.v + 0.1 107 elseif ctrl.down then 108 self.v = self.v - 0.1 109 end 110 if ctrl.left then 111 if self.v < 0 then 112 self.object:setyaw(yaw - (1 + dtime) * 0.03) 113 else 114 self.object:setyaw(yaw + (1 + dtime) * 0.03) 115 end 116 elseif ctrl.right then 117 if self.v < 0 then 118 self.object:setyaw(yaw + (1 + dtime) * 0.03) 119 else 120 self.object:setyaw(yaw - (1 + dtime) * 0.03) 121 end 122 end 123 end 124 local velo = self.object:getvelocity() 125 if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then 126 self.object:setpos(self.object:getpos()) 127 return 128 end 129 local s = get_sign(self.v) 130 self.v = self.v - 0.02 * s 131 if s ~= get_sign(self.v) then 132 self.object:setvelocity({x = 0, y = 0, z = 0}) 133 self.v = 0 134 return 135 end 136 if math.abs(self.v) > 4.5 then 137 self.v = 4.5 * get_sign(self.v) 138 end 139 140 local p = self.object:getpos() 141 p.y = p.y - 0.5 142 local new_velo = {x = 0, y = 0, z = 0} 143 local new_acce = {x = 0, y = 0, z = 0} 144 if not is_water(p) then 145 local nodedef = minetest.registered_nodes[minetest.get_node(p).name] 146 if (not nodedef) or nodedef.walkable then 147 self.v = 0 148 new_acce = {x = 0, y = 1, z = 0} 149 else 150 new_acce = {x = 0, y = -9.8, z = 0} 151 end 152 new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y) 153 self.object:setpos(self.object:getpos()) 154 else 155 p.y = p.y + 1 156 if is_water(p) then 157 local y = self.object:getvelocity().y 158 if y >= 4.5 then 159 y = 4.5 160 elseif y < 0 then 161 new_acce = {x = 0, y = 20, z = 0} 162 else 163 new_acce = {x = 0, y = 5, z = 0} 164 end 165 new_velo = get_velocity(self.v, self.object:getyaw(), y) 166 self.object:setpos(self.object:getpos()) 167 else 168 new_acce = {x = 0, y = 0, z = 0} 169 if math.abs(self.object:getvelocity().y) < 1 then 170 local pos = self.object:getpos() 171 pos.y = math.floor(pos.y) + 0.5 172 self.object:setpos(pos) 173 new_velo = get_velocity(self.v, self.object:getyaw(), 0) 174 else 175 new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y) 176 self.object:setpos(self.object:getpos()) 177 end 178 end 179 end 180 self.object:setvelocity(new_velo) 181 self.object:setacceleration(new_acce) 182end 183 184minetest.register_entity("boats:boat", boat) 185 186minetest.register_craftitem("boats:boat", { 187 description = "Boat", 188 inventory_image = "boat_inventory.png", 189 wield_image = "boat_wield.png", 190 wield_scale = {x = 2, y = 2, z = 1}, 191 liquids_pointable = true, 192 193 on_place = function(itemstack, placer, pointed_thing) 194 if pointed_thing.type ~= "node" then 195 return 196 end 197 if not is_water(pointed_thing.under) then 198 return 199 end 200 pointed_thing.under.y = pointed_thing.under.y + 0.5 201 minetest.add_entity(pointed_thing.under, "boats:boat") 202 if not minetest.setting_getbool("creative_mode") then 203 itemstack:take_item() 204 end 205 return itemstack 206 end, 207}) 208 209minetest.register_craft({ 210 output = "boats:boat", 211 recipe = { 212 {"", "", "" }, 213 {"group:wood", "", "group:wood"}, 214 {"group:wood", "group:wood", "group:wood"}, 215 }, 216}) 217 218