diff --git a/balloon.lua b/balloon.lua new file mode 100644 index 0000000..8021cff --- /dev/null +++ b/balloon.lua @@ -0,0 +1,273 @@ +local mod_name = minetest.get_current_modname() +local mod_path = minetest.get_modpath(mod_name) + +local me = sum_hot_air_balloon + +me.vars = { + lift = 4, + speed = 3, + fuel_time = 30, +} + +function me.on_activate(self, staticdata, dtime_s) + local data = minetest.deserialize(staticdata) + if type(data) == "table" then + self._itemstring = data.itemstring + if data._driver then + self._driver = minetest.get_player_by_name((data._driver:get_player_name())) + else + self._driver = nil + end + self._flags = data._flags + if self._driver then me.detach(self) end + end + self.object:set_animation({x=0, y=149}, 24) +end + +function me.get_staticdata(self) + return minetest.serialize({ + itemstring = self._itemstring, + _driver = ((self._driver and self._driver:is_player()) + and self._driver:get_player_name()) or nil, + _flags = self._flags, + }) +end + +function me.attach(self, player) + if not (player and player:is_player()) then + return false + end + self._driver = player + self._driver:set_attach(self.object, "", + {x = 0, y = -3.0, z = 0}, {x = 0, y = 0, z = 0}) + self._driver:set_look_horizontal(self.object:get_yaw()) +end + +function me.detach(self) + if not self._driver then return false end + self._driver:set_detach() + self._driver = nil + return true +end + + +function me.on_death(self, killer) + if killer and killer:is_player() + and not minetest.is_creative_enabled(killer:get_player_name()) then + local inv = killer:get_inventory() + inv:add_item("main", self._itemstring) + else + minetest.add_item(self.object:get_pos(), self._itemstring) + end + me.detach(self) + self._driver = nil +end + +function me.on_rightclick(self, clicker) + local item = clicker:get_wielded_item() + local item_name = item:get_name() + if clicker and item and item_name + and (string.find(item_name, ":coal") + or string.find(item_name, ":charcoal")) then + if not minetest.is_creative_enabled(clicker:get_player_name()) then + item:take_item() + clicker:set_wielded_item(item) + end + else + me.attach(self, clicker) + end +end + + +-- 10, 5, 5 +-- this system ensures collision kind of works with balloons. +-- does not include entity to entity collisions +local balloon = {} +balloon.offset = 4.5 +balloon.length = 3 +balloon.height = 2.5 +local balloon_nodes = {} +balloon_nodes[0] = { -- top + p = vector.new(0, balloon.offset + balloon.height, 0), + dir = vector.new(0, -5, 0),} +balloon_nodes[1] = { -- front + p = vector.new(0, balloon.offset, balloon.length), + dir = vector.new(0, -0.5, -1),} +balloon_nodes[2] = { -- back + p = vector.new(0, balloon.offset, -balloon.length), + dir = vector.new(0, 0, 1),} +balloon_nodes[3] = { -- left or right + p = vector.new(balloon.length, balloon.offset, 0), + dir = vector.new(-1, 0, 0),} +balloon_nodes[4] = { -- left or right + p = vector.new(-balloon.length, balloon.offset, 0), + dir = vector.new(1, 0, 0),} +-- diagonals +local vdiag = 0.7 +balloon_nodes[5] = { + p = vector.new(-balloon.length*vdiag, balloon.offset, -balloon.length*vdiag), + dir = vector.new(vdiag, 0, vdiag),} +balloon_nodes[6] = { + p = vector.new(balloon.length*vdiag, balloon.offset, -balloon.length*vdiag), + dir = vector.new(-vdiag, 0, vdiag),} +balloon_nodes[7] = { + p = vector.new(-balloon.length*vdiag, balloon.offset, balloon.length*vdiag), + dir = vector.new(vdiag, 0, -vdiag),} +balloon_nodes[8] = { + p = vector.new(balloon.length*vdiag, balloon.offset, balloon.length*vdiag), + dir = vector.new(-vdiag, 0, -vdiag),} + +function me.get_balloon_collide(self) + local force = vector.new() + local o = self.object:get_pos() + for _, check in pairs(balloon_nodes) do + local n = minetest.get_node(vector.add(check.p, o)) + -- minetest.add_particle({ + -- pos = vector.add(o, check.p), + -- velocity = vector.new(0, 1, 0), + -- expirationtime = math.random(0.5, 2), + -- size = math.random(0.1, 4), + -- texture = "sum_wood_ash_planks.png", + -- }) + if n and minetest.registered_nodes[n.name] + and minetest.registered_nodes[n.name].walkable then + force = vector.add(force, check.dir) + end + end + return force +end + +function me.on_step(self, dtime, moveresult) + local exit = false + local ctrl = nil + -- allow to exit + if self._driver and self._driver:is_player() then + local name = self._driver:get_player_name() + ctrl = player_info.p[name] + exit = ctrl.just_pressed.sneak + end + if exit then + me.detach(self) + return false + end + + local climb = 0 + local right = 0 + local forward = 0 + local v = self.object:get_velocity() + local p = self.object:get_pos() + local node_below = minetest.get_node(vector.offset(p, 0, -0.8, 0)).name + local is_on_floor = minetest.registered_nodes[node_below].walkable + local in_water = minetest.get_item_group(minetest.get_node(p).name, "liquid") ~= 0 + local on_water = (minetest.get_item_group(minetest.get_node(vector.offset(p, 0, -0.2, 0)).name, "liquid") ~= 0 and not in_water) + + if ctrl then + if ctrl.ctrl.up then forward = 1 + elseif ctrl.ctrl.down then forward = -1 end + if ctrl.ctrl.jump then climb = 1 + elseif ctrl.ctrl.aux1 then climb = -1 end + if ctrl.ctrl.right then right = 1 + elseif ctrl.ctrl.left then right = -1 end + local yaw = self.object:get_yaw() + local dir = minetest.yaw_to_dir(yaw) + self.object:set_yaw(yaw - right * dtime) + local added_vel = vector.multiply(dir, forward * dtime * me.vars.speed) + added_vel.y = added_vel.y + (climb * dtime * me.vars.lift) + v = vector.add(v, added_vel) + end + + if self._driver then + local collide_force = me.get_balloon_collide(self) + -- collide_force = vector.normalize(collide_force) + if collide_force ~= vector.new() then + collide_force = vector.multiply(collide_force, 0.1) + v = vector.multiply(v, 0.95) + end + v = vector.add(v, collide_force) + end + + if not self._driver then + v.y = v.y - dtime + end + + if sum_air_currents ~= nil then + if self._driver or not is_on_floor then + local wind_vel = sum_air_currents.get_wind(p) + wind_vel = vector.multiply(wind_vel, dtime) + v = vector.add(wind_vel, v) + end + end + if in_water then + v.y = 1 + elseif on_water and not self._driver then + v.y = 0 + end + + if (not self._driver) and is_on_floor then + v.x = v.x * 0.8 + v.y = v.y * 0.95 + v.z = v.z * 0.8 + else + v.x = v.x * (1 - (dtime * 0.5)) + v.y = v.y * (1 - (dtime * 0.5)) + v.z = v.z * (1 - (dtime * 0.5)) + end + self.object:set_velocity(v) +end + + +minetest.register_entity("sum_hot_air_balloon:balloon_ENTITY", { + physical = true, + pointable = true, + collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.2, 0.5}, + selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.2, 0.5}, + hp_max = 3, + visual = "mesh", + mesh = "sum_hot_air_balloon.b3d", + backface_culling = false, + textures = { + "sum_hot_air_balloon_canvas.png", -- balloon + "sum_wood_ash_planks.png", -- cradle + "sum_wood_oak_plank.png", -- lining + "sum_wood_oak_plank.png", -- strut + "sum_wood_oak_plank.png", -- strut + "sum_hot_air_balloon_canvas.png", -- burner + "sum_wood_oak_plank.png", -- small strut + "sum_wood_oak_plank.png", -- small strut + }, + on_rightclick = me.on_rightclick, + on_activate = me.on_activate, + get_staticdata = me.get_staticdata, + on_death = me.on_death, + on_step = me.on_step, + _driver = nil, + _removed = false, + _flags = {}, + _itemstring = "sum_hot_air_balloon:balloon_item", +}) + +minetest.register_craftitem("sum_hot_air_balloon:balloon_item", { + description = "Hot air balloon", + inventory_image = "sum_hot_air_balloon_item.png", + groups = { vehicle = 1, airship = 1, transport = 1}, + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + local node = minetest.get_node(pointed_thing.under) + if placer and not placer:get_player_control().sneak then + if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then + return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack + end + end + local pos = vector.offset(pointed_thing.above, 0, 0.5, 0) + local self = minetest.add_entity(pos, "sum_hot_air_balloon:balloon_ENTITY"):get_luaentity() + if not minetest.is_creative_enabled(placer:get_player_name()) then + itemstack:take_item() + end + return itemstack + end, +}) + + + diff --git a/init.lua b/init.lua index 239ba1c..6005bc8 100644 --- a/init.lua +++ b/init.lua @@ -14,15 +14,6 @@ local boat_side_offset = 1.001 local boat_max_hp = 4 --- make sure silly people don't try to run it without the needed dependencies. -if not (minetest.get_modpath("mcl_boats") -and minetest.get_modpath("mcl_wool") -and minetest.get_modpath("mcl_core")) -and not minetest.get_modpath("default") then - error("\n\n===\nYou need either mcl2 or minetest_game to run sum_airship mod. \n" .. - "These are listed in the optional dependencies for cross compatibility, " .. - "but at least one is needed.\n===\n") -end local has_air_currents = minetest.get_modpath("sum_air_currents") ~= nil local mcl = minetest.get_modpath("mcl_player") ~= nil @@ -262,8 +253,8 @@ minetest.register_on_respawnplayer(detach_object) function boat.on_rightclick(self, clicker) local item = clicker:get_wielded_item() local item_name = item:get_name() - if clicker and (string.find(item_name, ":coal_lump") - or string.find(item_name, ":charcoal_lump")) then + if clicker and (string.find(item_name, ":coal") + or string.find(item_name, ":charcoal")) then if not minetest.is_creative_enabled(clicker:get_player_name()) then item:take_item() clicker:set_wielded_item(item)