diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1bd5d3d --- /dev/null +++ b/LICENSE @@ -0,0 +1,167 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +Copyright Alexsandro Percy - 2021 diff --git a/README.md b/README.md index 0274769..72763b8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,56 @@ -# demoiselle -Minetest Demoiselle Mod +Minetest 5.4 mod: Demoiselle +======================================== + +The Demoiselle + +This is a historic aircraft, the Demoiselle. It was an aircraft developed between 1907 and 1909 +by the Brazilian inventor Alberto Santos Dumont. This mod for minetest is a cartooned version +adapted to the engine, with a fun and slow flight. I hope you have fun exploring the map and +experiencing the beginnings of aviation. +In order to fly, it is necessary to first supply the airplane with biofuel. +Then with a bottle or gallon selected, punch it against the airplane floater. +You can use 5 bottles them to fill the tank. To embark, click with the right button. +While the machine is off, it is possible to move it using the sneak and jump keys (shift an space). +W ans S controls the pitch (elevator). +Right and Left (A and D) controls the yaw (rudder and ailerons). + +Then to fly, start the engine with the special key E. Press jump (space) +to increase the engine power. Set it to the maximum. Pull the elevator control (S) +when it have the speed to lift. + +Use the climb indicator to stabilize altitude, +as at high altitudes you lose sustentation and you spend more fuel. + +For landing, just lower the power and stabilize the airplane. Pay attention at air speed +indicator, keeping it at green range, otherwise you will stall. + +Care must be taken with impacts, as it causes damage to the aircraft and the pilot, +so training landings is essential. + +To brake the aircraft, use the sneak (shift) key until it comes to a complete stop. +Do not stop the engine before this, or it will reverse when it stops + +**Controls overview:** +* Right click: enter in/get off plane +* Left click (with biofuel): add fuel to plane +* E: Start engine +* Jump: Increase power, forward on ground +* Sneak: Decrease power, brake on ground +* Backward: go up flying - nose up +* Forward: go down flying - nose down +* Left/right: Turn to left/right, work on and out ground. +* Left and Right together: center all commands +* Up and Down together: enable/disable HUD + +**Chat Commands: ** + +/demoiselle_eject - ejects from the vehicle + +----------------------- +License of source code: +LGPL v3 (see file LICENSE) + +License of media (textures and sounds): +CC0 + + diff --git a/demoiselle_control.lua b/demoiselle_control.lua new file mode 100644 index 0000000..1d44037 --- /dev/null +++ b/demoiselle_control.lua @@ -0,0 +1,228 @@ +--global constants +demoiselle.vector_up = vector.new(0, 1, 0) +demoiselle.ideal_step = 0.02 +demoiselle.rudder_limit = 30 +demoiselle.elevator_limit = 40 + +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_utilities.lua") + +function demoiselle.check_node_below(obj) + local pos_below = obj:get_pos() + pos_below.y = pos_below.y - 0.1 + local node_below = minetest.get_node(pos_below).name + local nodedef = minetest.registered_nodes[node_below] + local touching_ground = not nodedef or -- unknown nodes are solid + nodedef.walkable or false + local liquid_below = not touching_ground and nodedef.liquidtype ~= "none" + return touching_ground, liquid_below +end + +function demoiselle.powerAdjust(self,dtime,factor,dir,max_power) + local max = max_power or 100 + local add_factor = factor + add_factor = add_factor * (dtime/demoiselle.ideal_step) --adjusting the command speed by dtime + local power_index = self._power_lever + + if dir == 1 then + if self._power_lever < max then + self._power_lever = self._power_lever + add_factor + end + if self._power_lever > max then + self._power_lever = max + end + end + if dir == -1 then + if self._power_lever > 0 then + self._power_lever = self._power_lever - add_factor + if self._power_lever < 0 then self._power_lever = 0 end + end + if self._power_lever <= 0 then + self._power_lever = 0 + end + end + if power_index ~= self._power_lever then + demoiselle.engineSoundPlay(self) + end + +end + +function demoiselle.control(self, dtime, hull_direction, longit_speed, longit_drag, + later_speed, later_drag, accel, player, is_flying) + if demoiselle.last_time_command > 1 then demoiselle.last_time_command = 1 end + --if self.driver_name == nil then return end + local retval_accel = accel + + local stop = false + local ctrl = nil + + -- player control + if player then + ctrl = player:get_player_control() + + --engine and power control + if ctrl.aux1 and demoiselle.last_time_command > 0.5 then + demoiselle.last_time_command = 0 + if self._engine_running then + self._engine_running = false + -- sound and animation + if self.sound_handle then + minetest.sound_stop(self.sound_handle) + self.sound_handle = nil + end + self.engine:set_animation_frame_speed(0) + self._power_lever = 0 --zero power + elseif self._engine_running == false and self._energy > 0 then + self._engine_running = true + -- sound and animation + demoiselle.engineSoundPlay(self) + self.engine:set_animation_frame_speed(60) + end + end + + self._acceleration = 0 + if self._engine_running then + --engine acceleration calc + local engineacc = (self._power_lever * demoiselle.max_engine_acc) / 100; + self.engine:set_animation_frame_speed(60 + self._power_lever) + + local factor = 1 + + --increase power lever + if ctrl.jump then + demoiselle.powerAdjust(self, dtime, factor, 1) + end + --decrease power lever + if ctrl.sneak then + demoiselle.powerAdjust(self, dtime, factor, -1) + if self._power_lever <= 0 and is_flying == false then + --break + if longit_speed > 0 then + engineacc = -1 + if (longit_speed + engineacc) < 0 then engineacc = longit_speed * -1 end + end + if longit_speed < 0 then + engineacc = 1 + if (longit_speed + engineacc) > 0 then engineacc = longit_speed * -1 end + end + if abs(longit_speed) < 0.1 then + stop = true + end + end + end + --do not exceed + local max_speed = 6 + if longit_speed > max_speed then + engineacc = engineacc - (longit_speed-max_speed) + if engineacc < 0 then engineacc = 0 end + end + self._acceleration = engineacc + else + local paddleacc = 0 + if longit_speed < 1.0 then + if ctrl.jump then paddleacc = 0.5 end + end + if longit_speed > -1.0 then + if ctrl.sneak then paddleacc = -0.5 end + end + self._acceleration = paddleacc + end + + local hull_acc = vector.multiply(hull_direction,self._acceleration) + retval_accel=vector.add(retval_accel,hull_acc) + + --pitch + local pitch_cmd = 0 + if ctrl.up then pitch_cmd = 1 elseif ctrl.down then pitch_cmd = -1 end + demoiselle.set_pitch(self, pitch_cmd, dtime) + + -- yaw + local yaw_cmd = 0 + if ctrl.right then yaw_cmd = 1 elseif ctrl.left then yaw_cmd = -1 end + demoiselle.set_yaw(self, yaw_cmd, dtime) + + --I'm desperate, center all! + if ctrl.right and ctrl.left then + self._elevator_angle = 0 + self._rudder_angle = 0 + end + end + + if longit_speed > 0 then + if ctrl then + if ctrl.right or ctrl.left then + else + demoiselle.rudder_auto_correction(self, longit_speed, dtime) + end + else + demoiselle.rudder_auto_correction(self, longit_speed, dtime) + end + demoiselle.elevator_auto_correction(self, longit_speed, dtime) + end + + return retval_accel, stop +end + +function demoiselle.set_pitch(self, dir, dtime) + local pitch_factor = 10 + if dir == -1 then + self._elevator_angle = math.max(self._elevator_angle-pitch_factor*dtime,-demoiselle.elevator_limit) + elseif dir == 1 then + if self._angle_of_attack < 0 then pitch_factor = 1 end --lets reduce the command power to avoid accidents + self._elevator_angle = math.min(self._elevator_angle+pitch_factor*dtime,demoiselle.elevator_limit) + end +end + +function demoiselle.set_yaw(self, dir, dtime) + local yaw_factor = 30 + if dir == 1 then + self._rudder_angle = math.max(self._rudder_angle-yaw_factor*dtime,-demoiselle.rudder_limit) + elseif dir == -1 then + self._rudder_angle = math.min(self._rudder_angle+yaw_factor*dtime,demoiselle.rudder_limit) + end +end + +function demoiselle.rudder_auto_correction(self, longit_speed, dtime) + local factor = 1 + if self._rudder_angle > 0 then factor = -1 end + local correction = (demoiselle.rudder_limit*(longit_speed/1000)) * factor * (dtime/demoiselle.ideal_step) + local before_correction = self._rudder_angle + local new_rudder_angle = self._rudder_angle + correction + if math.sign(before_correction) ~= math.sign(new_rudder_angle) then + self._rudder_angle = 0 + else + self._rudder_angle = new_rudder_angle + end +end + +function demoiselle.elevator_auto_correction(self, longit_speed, dtime) + local factor = 1 + --if self._elevator_angle > -1.5 then factor = -1 end --here is the "compensator" adjusto to keep it stable + if self._elevator_angle > 0 then factor = -1 end + local correction = (demoiselle.elevator_limit*(longit_speed/10000)) * factor * (dtime/demoiselle.ideal_step) + local before_correction = self._elevator_angle + local new_elevator_angle = self._elevator_angle + correction + if math.sign(before_correction) ~= math.sign(new_elevator_angle) then + self._elevator_angle = 0 + else + self._elevator_angle = new_elevator_angle + end +end + +function demoiselle.engineSoundPlay(self) + --sound + if self.sound_handle then minetest.sound_stop(self.sound_handle) end + self.sound_handle = minetest.sound_play({name = "demoiselle_engine"}, + {object = self.object, gain = 2.0, + pitch = 0.5 + ((self._power_lever/100)/2),max_hear_distance = 32, + loop = true,}) +end + +function getAdjustFactor(curr_y, desired_y) + local max_difference = 0.1 + local adjust_factor = 0.5 + local difference = math.abs(curr_y - desired_y) + if difference > max_difference then difference = max_difference end + return (difference * adjust_factor) / max_difference +end + + diff --git a/demoiselle_crafts.lua b/demoiselle_crafts.lua new file mode 100644 index 0000000..e1d8767 --- /dev/null +++ b/demoiselle_crafts.lua @@ -0,0 +1,69 @@ +-- wing +minetest.register_craftitem("demoiselle:wings",{ + description = "Demoiselle wings", + inventory_image = "demoiselle_wings.png", +}) +-- fuselage +minetest.register_craftitem("demoiselle:body",{ + description = "Demoiselle body", + inventory_image = "demoiselle_body.png", +}) + +-- trike +minetest.register_craftitem("demoiselle:demoiselle", { + description = "Demoiselle", + inventory_image = "demoiselle.png", + liquids_pointable = true, + + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return + end + + local pointed_pos = pointed_thing.under + --local node_below = minetest.get_node(pointed_pos).name + --local nodedef = minetest.registered_nodes[node_below] + + pointed_pos.y=pointed_pos.y+0.5 + local demoiselle = minetest.add_entity(pointed_pos, "demoiselle:demoiselle") + if demoiselle and placer then + local ent = demoiselle:get_luaentity() + local owner = placer:get_player_name() + ent.owner = owner + demoiselle:set_yaw(placer:get_look_horizontal()) + itemstack:take_item() + end + + return itemstack + end, +}) + +-- +-- crafting +-- + +if minetest.get_modpath("default") then + minetest.register_craft({ + output = "demoiselle:wings", + recipe = { + {"wool:white", "farming:string", "wool:white"}, + {"group:wood", "group:wood", "group:wood"}, + {"wool:white", "farming:string", "wool:white"}, + } + }) + minetest.register_craft({ + output = "demoiselle:body", + recipe = { + {"default:steel_ingot", "default:mese_block", "default:steel_ingot"}, + {"group:wood", "group:wood", "group:wood"}, + {"default:steel_ingot", "group:wood", "default:steel_ingot"}, + } + }) + minetest.register_craft({ + output = "demoiselle:demoiselle", + recipe = { + {"demoiselle:wings",}, + {"demoiselle:body",}, + } + }) +end diff --git a/demoiselle_custom_physics.lua b/demoiselle_custom_physics.lua new file mode 100644 index 0000000..fc95030 --- /dev/null +++ b/demoiselle_custom_physics.lua @@ -0,0 +1,42 @@ + +local min = math.min +local abs = math.abs +--local deg = math.deg + +function demoiselle.physics(self) + local friction = 0.99 + local vel=self.object:get_velocity() + -- dumb friction + if self.isonground and not self.isinliquid then + self.object:set_velocity({x=vel.x*friction, + y=vel.y, + z=vel.z*friction}) + end + + -- bounciness + if self.springiness and self.springiness > 0 then + local vnew = vector.new(vel) + + if not self.collided then -- ugly workaround for inconsistent collisions + for _,k in ipairs({'y','z','x'}) do + if vel[k]==0 and abs(self.lastvelocity[k])> 0.1 then + vnew[k]=-self.lastvelocity[k]*self.springiness + end + end + end + + if not vector.equals(vel,vnew) then + self.collided = true + else + if self.collided then + vnew = vector.new(self.lastvelocity) + end + self.collided = false + end + + self.object:set_velocity(vnew) + end + + self.object:set_acceleration({x=0,y=mobkit.gravity,z=0}) + +end diff --git a/demoiselle_entities.lua b/demoiselle_entities.lua new file mode 100644 index 0000000..69a1679 --- /dev/null +++ b/demoiselle_entities.lua @@ -0,0 +1,296 @@ +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_global_definitions.lua") + +-- +-- entity +-- + +demoiselle.vector_up = vector.new(0, 1, 0) + +minetest.register_entity('demoiselle:engine',{ +initial_properties = { + physical = false, + collide_with_objects=false, + pointable=false, + visual = "mesh", + backface_culling = false, + mesh = "demoiselle_propeller.b3d", + --visual_size = {x = 3, y = 3, z = 3}, + textures = {"demoiselle_black.png","demoiselle_helice.png"}, + }, + + on_activate = function(self,std) + self.sdata = minetest.deserialize(std) or {} + if self.sdata.remove then self.object:remove() end + end, + + get_staticdata=function(self) + self.sdata.remove=true + return minetest.serialize(self.sdata) + end, + +}) + +-- +-- seat pivot +-- +minetest.register_entity('demoiselle:seat_base',{ +initial_properties = { + physical = false, + collide_with_objects=false, + pointable=false, + visual = "mesh", + mesh = "demoiselle_seat_base.b3d", + textures = {"demoiselle_black.png",}, + }, + + on_activate = function(self,std) + self.sdata = minetest.deserialize(std) or {} + if self.sdata.remove then self.object:remove() end + end, + + get_staticdata=function(self) + self.sdata.remove=true + return minetest.serialize(self.sdata) + end, + +}) + +minetest.register_entity("demoiselle:demoiselle", { + initial_properties = { + physical = true, + collide_with_objects = true, + collisionbox = {-1.2, 0, -1.2, 1.2, 1, 1.2}, --{-1,0,-1, 1,0.3,1}, + selectionbox = {-2, 0, -2, 2, 1, 2}, + visual = "mesh", + backface_culling = false, + mesh = "demoiselle.b3d", + stepheight = 0.5, + textures = {"demoiselle_bamboo.png", + "demoiselle_black.png", --cabos + "demoiselle_black.png", -- cabos empenagem + "demoiselle_canvas_structure.png", -- estrutura empenagem + "demoiselle_canvas.png", -- entelagem empenagem + "demoiselle_black.png", -- banco + "demoiselle_bamboo.png", -- estrutura fuselagem + "demoiselle_metal2.png", -- estrutura fuselagem + "demoiselle_metal.png", -- motor + "demoiselle_canvas_structure.png", --nervuras + "demoiselle_canvas.png", -- entelagem asas + "demoiselle_black.png", -- pneu + "demoiselle_metal.png", -- aro rodas + "demoiselle_wheel.png", -- raio rodas + "demoiselle_copper.png", -- tanque + "demoiselle_black.png", -- cabeçote motor + }, + }, + textures = {}, + driver_name = nil, + sound_handle = nil, + owner = "", + static_save = true, + infotext = "Demoiselle", + hp_max = 50, + buoyancy = 0.25, + springiness = 0.3, + physics = demoiselle.physics, + _rudder_angle = 0, + _acceleration = 0, + _engine_running = false, + _angle_of_attack = 2, + _elevator_angle = 0, + _power_lever = 0, + _energy = 0.001, + _last_vel = {x=0,y=0,z=0}, + _longit_speed = 0, + _show_hud = true, + _last_accell = {x=0,y=0,z=0}, + + get_staticdata = function(self) -- unloaded/unloads ... is now saved + return minetest.serialize({ + stored_energy = self._energy, + stored_owner = self.owner, + stored_hp = self.hp_max, + stored_power_lever = self._power_lever, + stored_driver_name = self.driver_name, + }) + end, + + on_activate = function(self, staticdata, dtime_s) + mobkit.actfunc(self, staticdata, dtime_s) + if staticdata ~= "" and staticdata ~= nil then + local data = minetest.deserialize(staticdata) or {} + self._energy = data.stored_energy + self.owner = data.stored_owner + self.hp_max = data.stored_hp + self._power_lever = data.stored_power_lever + self.driver_name = data.stored_driver_name + --minetest.debug("loaded: ", self._energy) + end + demoiselle.setText(self) + self.object:set_animation({x = 1, y = 12}, 0, 0, true) + + local pos = self.object:get_pos() + + local engine=minetest.add_entity(pos,'demoiselle:engine') + engine:set_attach(self.object,'',{x=0,y=24.5,z=0},{x=0,y=0,z=0}) + -- set the animation once and later only change the speed + engine:set_animation({x = 1, y = 12}, 0, 0, true) + self.engine = engine + + local pilot_seat_base=minetest.add_entity(pos,'demoiselle:seat_base') + pilot_seat_base:set_attach(self.object,'',{x=0,y=5,z=2},{x=0,y=0,z=0}) + self.pilot_seat_base = pilot_seat_base + + self.object:set_armor_groups({immortal=1}) + end, + + --on_step = mobkit.stepfunc, + on_step = function(self,dtime,colinfo) + self.dtime = math.min(dtime,0.2) + self.colinfo = colinfo + self.height = mobkit.get_box_height(self) + + -- physics comes first + local vel = self.object:get_velocity() + + if colinfo then + self.isonground = colinfo.touching_ground + else + if self.lastvelocity.y==0 and vel.y==0 then + self.isonground = true + else + self.isonground = false + end + end + + self:physics() + + if self.logic then + self:logic() + end + + self.lastvelocity = self.object:get_velocity() + self.time_total=self.time_total+self.dtime + end, + logic = demoiselle.flightstep, + + on_punch = function(self, puncher, ttime, toolcaps, dir, damage) + if not puncher or not puncher:is_player() then + return + end + + local is_admin = false + is_admin = minetest.check_player_privs(puncher, {server=true}) + local name = puncher:get_player_name() + if self.owner and self.owner ~= name and self.owner ~= "" then + if is_admin == false then return end + end + if self.owner == nil then + self.owner = name + end + + if self.driver_name and self.driver_name ~= name then + -- do not allow other players to remove the object while there is a driver + return + end + + local is_attached = false + if puncher:get_attach() == self.object then is_attached = true end + + local itmstck=puncher:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + if is_attached == false then + if demoiselle.loadFuel(self, puncher:get_player_name()) then + return + end + + --repair + if (item_name == "hidroplane:repair_tool" or item_name == "trike:repair_tool" or item_name == "airutils:repair_tool") + and self._engine_running == false then + if self.hp_max < 50 then + local inventory_item = "default:steel_ingot" + local inv = puncher:get_inventory() + if inv:contains_item("main", inventory_item) then + local stack = ItemStack(inventory_item .. " 1") + inv:remove_item("main", stack) + self.hp_max = self.hp_max + 10 + if self.hp_max > 50 then self.hp_max = 50 end + demoiselle.setText(self) + else + minetest.chat_send_player(puncher:get_player_name(), "You need steel ingots in your inventory to perform this repair.") + end + end + return + end + + -- deal with painting or destroying + if itmstck then + if not self.driver and toolcaps and toolcaps.damage_groups + and toolcaps.damage_groups.fleshy and item_name ~= demoiselle.fuel then + --mobkit.hurt(self,toolcaps.damage_groups.fleshy - 1) + --mobkit.make_sound(self,'hit') + self.hp_max = self.hp_max - 10 + minetest.sound_play("collision", { + object = self.object, + max_hear_distance = 5, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }) + demoiselle.setText(self) + end + end + + if self.hp_max <= 0 then + demoiselle.destroy(self) + end + + end + + end, + + on_rightclick = function(self, clicker) + if not clicker or not clicker:is_player() then + return + end + + local name = clicker:get_player_name() + + if self.owner == "" then + self.owner = name + end + + --check if is the owner + if self.owner == name then + -- pilot section + if name == self.driver_name then + --========================= + -- dettach player + --========================= + demoiselle.dettachPlayer(self, clicker) + --[[ sound and animation + if self.sound_handle then + minetest.sound_stop(self.sound_handle) + self.sound_handle = nil + end + self.engine:set_animation_frame_speed(0)]]-- + elseif not self.driver_name then + --========================= + -- attach player + --========================= + --attach player + local is_under_water = demoiselle.check_is_under_water(self.object) + if is_under_water then return end + + demoiselle.attach(self, clicker) + end + + -- end pilot section + else + local message = core.colorize('#ff0000', " >>> You aren't the owner of this demoiselle.") + minetest.chat_send_player(clicker:get_player_name(), message) + end + end, +}) diff --git a/demoiselle_fuel_management.lua b/demoiselle_fuel_management.lua new file mode 100644 index 0000000..651bf5a --- /dev/null +++ b/demoiselle_fuel_management.lua @@ -0,0 +1,49 @@ +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_global_definitions.lua") + +function demoiselle.contains(table, val) + for k,v in pairs(table) do + if k == val then + return v + end + end + return false +end + +function demoiselle.loadFuel(self, player_name) + local player = minetest.get_player_by_name(player_name) + local inv = player:get_inventory() + + local itmstck=player:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + local fuel = demoiselle.contains(demoiselle.fuel, item_name) + if fuel then + local stack = ItemStack(item_name .. " 1") + + if self._energy < DEMOISELLE_MAX_FUEL then + inv:remove_item("main", stack) + self._energy = self._energy + fuel + if self._energy > DEMOISELLE_MAX_FUEL then self._energy = DEMOISELLE_MAX_FUEL end + end + + return true + end + + return false +end + +function demoiselle.consumptionCalc(self, accel) + if accel == nil then return end + if self._energy > 0 and self._engine_running and accel ~= nil then + local consumed_power = self._power_lever/800000 + --minetest.chat_send_all('consumed: '.. consumed_power) + self._energy = self._energy - consumed_power; + end + if self._energy <= 0 and self._engine_running and accel ~= nil then + self._engine_running = false + self._autopilot = false + if self.sound_handle then minetest.sound_stop(self.sound_handle) end + self.engine:set_animation_frame_speed(0) + end +end diff --git a/demoiselle_global_definitions.lua b/demoiselle_global_definitions.lua new file mode 100644 index 0000000..3f5724a --- /dev/null +++ b/demoiselle_global_definitions.lua @@ -0,0 +1,11 @@ +-- +-- constants +-- +DEMOISELLE_LONGIT_DRAG_FACTOR = 0.13*0.13 +DEMOISELLE_LATER_DRAG_FACTOR = 2.0 +DEMOISELLE_MAX_FUEL = 5.0 + +deg = math.deg +abs = math.abs + +demoiselle.last_time_command = 0 diff --git a/demoiselle_hud.lua b/demoiselle_hud.lua new file mode 100755 index 0000000..0d0e4d9 --- /dev/null +++ b/demoiselle_hud.lua @@ -0,0 +1,354 @@ +demoiselle.hud_list = {} + +function demoiselle.animate_gauge(player, ids, prefix, x, y, angle) + local angle_in_rad = math.rad(angle + 180) + local dim = 10 + local pos_x = math.sin(angle_in_rad) * dim + local pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "2"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 20 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "3"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 30 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "4"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 40 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "5"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 50 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "6"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 60 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "7"], "offset", {x = pos_x + x, y = pos_y + y}) +end + +function demoiselle.update_hud(player, climb, speed, power, fuel) + local player_name = player:get_player_name() + + local screen_pos_y = -150 + local screen_pos_x = 10 + + local clb_gauge_x = screen_pos_x + 75 + local clb_gauge_y = screen_pos_y + 1 + local sp_gauge_x = screen_pos_x + 170 + local sp_gauge_y = clb_gauge_y + + local pwr_gauge_x = screen_pos_x + 330 + local pwr_gauge_y = clb_gauge_y + + local fu_gauge_x = screen_pos_x + 340 + local fu_gauge_y = clb_gauge_y + + local ids = demoiselle.hud_list[player_name] + if ids then + demoiselle.animate_gauge(player, ids, "clb_pt_", clb_gauge_x, clb_gauge_y, climb) + demoiselle.animate_gauge(player, ids, "sp_pt_", sp_gauge_x, sp_gauge_y, speed) + demoiselle.animate_gauge(player, ids, "pwr_pt_", pwr_gauge_x, pwr_gauge_y, power) + demoiselle.animate_gauge(player, ids, "fu_pt_", fu_gauge_x, fu_gauge_y, fuel) + else + ids = {} + + ids["title"] = player:hud_add({ + hud_elem_type = "text", + position = {x = 0, y = 1}, + offset = {x = screen_pos_x +140, y = screen_pos_y -100}, + text = "Flight Information", + alignment = 0, + scale = { x = 100, y = 30}, + number = 0xFFFFFF, + }) + + ids["bg"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = screen_pos_x, y = screen_pos_y}, + text = "demoiselle_hud_panel.png", + scale = { x = 0.5, y = 0.5}, + alignment = { x = 1, y = 0 }, + }) + + ids["clb_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["clb_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["sp_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["pwr_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["pwr_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["fu_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["fu_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "demoiselle_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + demoiselle.hud_list[player_name] = ids + end +end + + +function demoiselle.remove_hud(player) + if player then + local player_name = player:get_player_name() + --minetest.chat_send_all(player_name) + local ids = demoiselle.hud_list[player_name] + if ids then + --player:hud_remove(ids["altitude"]) + --player:hud_remove(ids["time"]) + player:hud_remove(ids["title"]) + player:hud_remove(ids["bg"]) + player:hud_remove(ids["clb_pt_7"]) + player:hud_remove(ids["clb_pt_6"]) + player:hud_remove(ids["clb_pt_5"]) + player:hud_remove(ids["clb_pt_4"]) + player:hud_remove(ids["clb_pt_3"]) + player:hud_remove(ids["clb_pt_2"]) + player:hud_remove(ids["clb_pt_1"]) + player:hud_remove(ids["sp_pt_7"]) + player:hud_remove(ids["sp_pt_6"]) + player:hud_remove(ids["sp_pt_5"]) + player:hud_remove(ids["sp_pt_4"]) + player:hud_remove(ids["sp_pt_3"]) + player:hud_remove(ids["sp_pt_2"]) + player:hud_remove(ids["sp_pt_1"]) + player:hud_remove(ids["pwr_pt_7"]) + player:hud_remove(ids["pwr_pt_6"]) + player:hud_remove(ids["pwr_pt_5"]) + player:hud_remove(ids["pwr_pt_4"]) + player:hud_remove(ids["pwr_pt_3"]) + player:hud_remove(ids["pwr_pt_2"]) + player:hud_remove(ids["pwr_pt_1"]) + player:hud_remove(ids["fu_pt_7"]) + player:hud_remove(ids["fu_pt_6"]) + player:hud_remove(ids["fu_pt_5"]) + player:hud_remove(ids["fu_pt_4"]) + player:hud_remove(ids["fu_pt_3"]) + player:hud_remove(ids["fu_pt_2"]) + player:hud_remove(ids["fu_pt_1"]) + end + demoiselle.hud_list[player_name] = nil + end + +end diff --git a/demoiselle_utilities.lua b/demoiselle_utilities.lua new file mode 100644 index 0000000..d0af8a1 --- /dev/null +++ b/demoiselle_utilities.lua @@ -0,0 +1,560 @@ +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_global_definitions.lua") +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_hud.lua") + +function demoiselle.get_hipotenuse_value(point1, point2) + return math.sqrt((point1.x - point2.x) ^ 2 + (point1.y - point2.y) ^ 2 + (point1.z - point2.z) ^ 2) +end + +function demoiselle.dot(v1,v2) + return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z +end + +function demoiselle.sign(n) + return n>=0 and 1 or -1 +end + +function demoiselle.minmax(v,m) + return math.min(math.abs(v),m)*demoiselle.sign(v) +end + +--lift +local function pitchroll2pitchyaw(aoa,roll) + if roll == 0.0 then return aoa,0 end + -- assumed vector x=0,y=0,z=1 + local p1 = math.tan(aoa) + local y = math.cos(roll)*p1 + local x = math.sqrt(p1^2-y^2) + local pitch = math.atan(y) + local yaw=math.atan(x)*math.sign(roll) + return pitch,yaw +end + +function demoiselle.getLiftAccel(self, velocity, accel, longit_speed, roll, curr_pos) + --lift calculations + ----------------------------------------------------------- + local max_height = 15000 + + local retval = accel + if longit_speed > 1 then + local angle_of_attack = math.rad(self._angle_of_attack + demoiselle.wing_angle_of_attack) + local lift = demoiselle.lift + --local acc = 0.8 + local daoa = deg(angle_of_attack) + + --to decrease the lift coefficient at hight altitudes + local curr_percent_height = (100 - ((curr_pos.y * 100) / max_height))/100 + + local rotation=self.object:get_rotation() + local vrot = mobkit.dir_to_rot(velocity,rotation) + + local hpitch,hyaw = pitchroll2pitchyaw(angle_of_attack,roll) + + local hrot = {x=vrot.x+hpitch,y=vrot.y-hyaw,z=roll} + local hdir = mobkit.rot_to_dir(hrot) --(hrot) + local cross = vector.cross(velocity,hdir) + local lift_dir = vector.normalize(vector.cross(cross,hdir)) + + local lift_coefficient = (0.24*abs(daoa)*(1/(0.025*daoa+3))^4*math.sign(angle_of_attack)) + local lift_val = math.abs((lift*(vector.length(velocity)^2)*lift_coefficient)*curr_percent_height) + --minetest.chat_send_all('lift: '.. lift_val) + + local lift_acc = vector.multiply(lift_dir,lift_val) + --lift_acc=vector.add(vector.multiply(minetest.yaw_to_dir(rotation.y),acc),lift_acc) + + retval = vector.add(retval,lift_acc) + end + ----------------------------------------------------------- + -- end lift + return retval +end + + +function demoiselle.get_gauge_angle(value, initial_angle) + initial_angle = initial_angle or 90 + local angle = value * 18 + angle = angle - initial_angle + angle = angle * -1 + return angle +end + +-- attach player +function demoiselle.attach(self, player, instructor_mode) + instructor_mode = instructor_mode or false + local name = player:get_player_name() + self.driver_name = name + + -- attach the driver + if instructor_mode == true then + player:set_attach(self.passenger_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + player:set_eye_offset({x = 0, y = -2.5, z = 2}, {x = 0, y = 1, z = -30}) + else + player:set_attach(self.pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + player:set_eye_offset({x = 0, y = -4, z = 2}, {x = 0, y = 1, z = -30}) + end + player:set_eye_offset({x = 0, y = -4, z = 2}, {x = 0, y = 1, z = -30}) + player_api.player_attached[name] = true + -- make the driver sit + minetest.after(0.2, function() + player = minetest.get_player_by_name(name) + if player then + player_api.set_animation(player, "sit") + --apply_physics_override(player, {speed=0,gravity=0,jump=0}) + end + end) +end + +function demoiselle.dettachPlayer(self, player) + local name = self.driver_name + demoiselle.setText(self) + + demoiselle.remove_hud(player) + + --self._engine_running = false + + -- driver clicked the object => driver gets off the vehicle + self.driver_name = nil + + -- detach the player + player:set_detach() + player_api.player_attached[name] = nil + player:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) + player_api.set_animation(player, "stand") + self.driver = nil + --remove_physics_override(player, {speed=1,gravity=1,jump=1}) +end + +function demoiselle.checkAttach(self, player) + if player then + local player_attach = player:get_attach() + if player_attach then + if player_attach == self.pilot_seat_base or player_attach == self.passenger_seat_base then + return true + end + end + end + return false +end + +-- destroy the boat +function demoiselle.destroy(self) + if self.sound_handle then + minetest.sound_stop(self.sound_handle) + self.sound_handle = nil + end + + if self.driver_name then + -- detach the driver + local player = minetest.get_player_by_name(self.driver_name) + demoiselle.dettachPlayer(self, player) + end + + local pos = self.object:get_pos() + if self.engine then self.engine:remove() end + if self.pilot_seat_base then self.pilot_seat_base:remove() end + + + if self.stick then self.stick:remove() end + + self.object:remove() + + pos.y=pos.y+2 + minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'demoiselle:wings') + + for i=1,5 do + minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:tin_ingot') + end + + for i=1,6 do + minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:steel_ingot') + end + + for i=1,2 do + minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'wool:white') + end + + for i=1,6 do + minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:mese_crystal') + minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:diamond') + end + + --minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'demoiselle:demoiselle') +end + +function demoiselle.check_node_below(obj) + local pos_below = obj:get_pos() + if pos_below then + pos_below.y = pos_below.y - 2.5 + local node_below = minetest.get_node(pos_below).name + local nodedef = minetest.registered_nodes[node_below] + local touching_ground = not nodedef or -- unknown nodes are solid + nodedef.walkable or false + local liquid_below = not touching_ground and nodedef.liquidtype ~= "none" + return touching_ground, liquid_below + end + return nil, nil +end + +function demoiselle.setText(self) + local properties = self.object:get_properties() + local formatted = string.format( + "%.2f", self.hp_max + ) + if properties then + properties.infotext = "Nice demoiselle of " .. self.owner .. ". Current hp: " .. formatted + self.object:set_properties(properties) + end +end + +function demoiselle.testImpact(self, velocity, position) + local p = position --self.object:get_pos() + local collision = false + if self._last_vel == nil then return end + --lets calculate the vertical speed, to avoid the bug on colliding on floor with hard lag + if abs(velocity.y - self._last_vel.y) > 2 then + local noded = mobkit.nodeatpos(mobkit.pos_shift(p,{y=-2.8})) + if (noded and noded.drawtype ~= 'airlike') then + collision = true + else + self.object:set_velocity(self._last_vel) + self.object:set_acceleration(self._last_accell) + end + end + local impact = abs(demoiselle.get_hipotenuse_value(velocity, self._last_vel)) + --minetest.chat_send_all('impact: '.. impact .. ' - hp: ' .. self.hp_max) + if impact > 2 then + --minetest.chat_send_all('impact: '.. impact .. ' - hp: ' .. self.hp_max) + local nodeu = mobkit.nodeatpos(mobkit.pos_shift(p,{y=1})) + local noded = mobkit.nodeatpos(mobkit.pos_shift(p,{y=-2.8})) + local nodel = mobkit.nodeatpos(mobkit.pos_shift(p,{x=-1})) + local noder = mobkit.nodeatpos(mobkit.pos_shift(p,{x=1})) + local nodef = mobkit.nodeatpos(mobkit.pos_shift(p,{z=1})) + local nodeb = mobkit.nodeatpos(mobkit.pos_shift(p,{z=-1})) + if (nodeu and nodeu.drawtype ~= 'airlike') or + (nodef and nodef.drawtype ~= 'airlike') or + (nodeb and nodeb.drawtype ~= 'airlike') or + (noder and noder.drawtype ~= 'airlike') or + (nodel and nodel.drawtype ~= 'airlike') then + collision = true + end + end + + if collision then + --self.object:set_velocity({x=0,y=0,z=0}) + local damage = impact / 2 + self.hp_max = self.hp_max - damage --subtract the impact value directly to hp meter + minetest.sound_play("demoiselle_collision", { + --to_player = self.driver_name, + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }, true) + + if self.driver_name then + local player_name = self.driver_name + demoiselle.setText(self) + + --minetest.chat_send_all('damage: '.. damage .. ' - hp: ' .. self.hp_max) + if self.hp_max < 0 then --if acumulated damage is greater than 50, adieu + demoiselle.destroy(self) + end + + local player = minetest.get_player_by_name(player_name) + if player then + if player:get_hp() > 0 then + player:set_hp(player:get_hp()-(damage/2)) + end + end + if self._passenger ~= nil then + local passenger = minetest.get_player_by_name(self._passenger) + if passenger then + if passenger:get_hp() > 0 then + passenger:set_hp(passenger:get_hp()-(damage/2)) + end + end + end + end + + end +end + +function demoiselle.checkattachBug(self) + -- for some engine error the player can be detached from the submarine, so lets set him attached again + if self.owner and self.driver_name then + -- attach the driver again + local player = minetest.get_player_by_name(self.owner) + if player then + if player:get_hp() > 0 then + demoiselle.attach(self, player, self._instruction_mode) + else + demoiselle.dettachPlayer(self, player) + end + end + end +end + +function demoiselle.check_is_under_water(obj) + local pos_up = obj:get_pos() + pos_up.y = pos_up.y + 0.1 + local node_up = minetest.get_node(pos_up).name + local nodedef = minetest.registered_nodes[node_up] + local liquid_up = nodedef.liquidtype ~= "none" + return liquid_up +end + +function demoiselle.flightstep(self) + local velocity = self.object:get_velocity() + --hack to avoid glitches + self.object:set_velocity(velocity) + local curr_pos = self.object:get_pos() + self.object:set_pos(curr_pos) + + + demoiselle.last_time_command = demoiselle.last_time_command + self.dtime + local player = nil + if self.driver_name then player = minetest.get_player_by_name(self.driver_name) end + local passenger = nil + if self._passenger then passenger = minetest.get_player_by_name(self._passenger) end + + if player then + local ctrl = player:get_player_control() + --------------------- + -- change the driver + --------------------- + if passenger and demoiselle.last_time_command >= 1 and self._instruction_mode == true then + if self._command_is_given == true then + if ctrl.sneak or ctrl.jump or ctrl.up or ctrl.down or ctrl.right or ctrl.left then + demoiselle.last_time_command = 0 + --take the control + demoiselle.transfer_control(self, false) + end + else + if ctrl.sneak == true and ctrl.jump == true then + demoiselle.last_time_command = 0 + --trasnfer the control to student + demoiselle.transfer_control(self, true) + end + end + end + ---------------------------------- + -- shows the hud for the player + ---------------------------------- + if ctrl.up == true and ctrl.down == true and demoiselle.last_time_command >= 1 then + demoiselle.last_time_command = 0 + if self._show_hud == true then + self._show_hud = false + else + self._show_hud = true + end + end + end + + local accel_y = self.object:get_acceleration().y + local rotation = self.object:get_rotation() + local yaw = rotation.y + local newyaw=yaw + local pitch = rotation.x + local roll = rotation.z + local newroll=roll + if newroll > 360 then newroll = newroll - 360 end + if newroll < -360 then newroll = newroll + 360 end + + local hull_direction = mobkit.rot_to_dir(rotation) --minetest.yaw_to_dir(yaw) + local nhdir = {x=hull_direction.z,y=0,z=-hull_direction.x} -- lateral unit vector + + local longit_speed = vector.dot(velocity,hull_direction) + self._longit_speed = longit_speed + local longit_drag = vector.multiply(hull_direction,longit_speed* + longit_speed*DEMOISELLE_LONGIT_DRAG_FACTOR*-1*demoiselle.sign(longit_speed)) + local later_speed = demoiselle.dot(velocity,nhdir) + --minetest.chat_send_all('later_speed: '.. later_speed) + local later_drag = vector.multiply(nhdir,later_speed*later_speed* + DEMOISELLE_LATER_DRAG_FACTOR*-1*demoiselle.sign(later_speed)) + local accel = vector.add(longit_drag,later_drag) + local stop = false + + local node_bellow = mobkit.nodeatpos(mobkit.pos_shift(curr_pos,{y=-0.1})) + local is_flying = true + if node_bellow and node_bellow.drawtype ~= 'airlike' then is_flying = false end + --if is_flying then minetest.chat_send_all('is flying') end + + local is_attached = demoiselle.checkAttach(self, player) + + --ajustar angulo de ataque + local percentage = math.abs(((longit_speed * 100)/(demoiselle.min_speed + 5))/100) + if percentage > 1.5 then percentage = 1.5 end + self._angle_of_attack = self._angle_of_attack - ((self._elevator_angle / 20)*percentage) + if self._angle_of_attack < -0.5 then + self._angle_of_attack = -0.1 + self._elevator_angle = self._elevator_angle - 0.1 + end --limiting the negative angle]]-- + if self._angle_of_attack > 20 then + self._angle_of_attack = 20 + self._elevator_angle = self._elevator_angle + 0.1 + end --limiting the very high climb angle due to strange behavior]]-- + + --minetest.chat_send_all(self._angle_of_attack) + + -- pitch + local speed_factor = 0 + if longit_speed > demoiselle.min_speed then speed_factor = (velocity.y * math.rad(2)) end + local newpitch = math.rad(self._angle_of_attack) + speed_factor + + -- adjust pitch at ground + if is_flying == false then --isn't flying? + if math.abs(longit_speed) < demoiselle.min_speed then + percentage = ((longit_speed * 100)/demoiselle.min_speed)/100 + if newpitch ~= 0 then + newpitch = newpitch * percentage + end + end + + --animate wheels + self.object:set_animation_frame_speed(longit_speed * 10) + else + --stop wheels + self.object:set_animation_frame_speed(0) + end + + -- new yaw + if math.abs(self._rudder_angle)>5 then + local turn_rate = math.rad(14) + local yaw_turn = self.dtime * math.rad(self._rudder_angle) * turn_rate * + demoiselle.sign(longit_speed) * math.abs(longit_speed/2) + newyaw = yaw + yaw_turn + end + + --roll adjust + --------------------------------- + local delta = 0.002 + if is_flying then + local roll_reference = newyaw + local sdir = minetest.yaw_to_dir(roll_reference) + local snormal = {x=sdir.z,y=0,z=-sdir.x} -- rightside, dot is negative + local prsr = demoiselle.dot(snormal,nhdir) + local rollfactor = -90 + local roll_rate = math.rad(25) + newroll = (prsr*math.rad(rollfactor)) * (later_speed * roll_rate) * demoiselle.sign(longit_speed) + --minetest.chat_send_all('newroll: '.. newroll) + else + delta = 0.2 + if roll > 0 then + newroll = roll - delta + if newroll < 0 then newroll = 0 end + end + if roll < 0 then + newroll = roll + delta + if newroll > 0 then newroll = 0 end + end + end + + --------------------------------- + -- end roll + + if not is_attached then + -- for some engine error the player can be detached from the machine, so lets set him attached again + demoiselle.checkattachBug(self) + end + + local pilot = player + if self._command_is_given and passenger then + pilot = passenger + else + self._command_is_given = false + end + + ------------------------------------------------------ + --accell calculation block + ------------------------------------------------------ + if is_attached or passenger then + accel, stop = demoiselle.control(self, self.dtime, hull_direction, + longit_speed, longit_drag, later_speed, later_drag, accel, pilot, is_flying) + end + + --end accell + + if accel == nil then accel = {x=0,y=0,z=0} end + + --lift calculation + accel.y = accel_y --accel.y + mobkit.gravity --accel_y + + --lets apply some bob in water + if self.isinliquid then + local bob = demoiselle.minmax(demoiselle.dot(accel,hull_direction),0.4) -- vertical bobbing + accel.y = accel.y + bob + local max_pitch = 6 + local h_vel_compensation = (((longit_speed * 4) * 100)/max_pitch)/100 + if h_vel_compensation < 0 then h_vel_compensation = 0 end + if h_vel_compensation > max_pitch then h_vel_compensation = max_pitch end + newpitch = newpitch + (velocity.y * math.rad(max_pitch - h_vel_compensation)) + end + + local new_accel = accel + if longit_speed > 1.5 then + new_accel = demoiselle.getLiftAccel(self, velocity, new_accel, longit_speed, roll, curr_pos) + end + -- end lift + + if stop ~= true then + self._last_accell = new_accel + self.object:set_acceleration(new_accel) + elseif stop == true then + self.object:set_velocity({x=0,y=0,z=0}) + end + ------------------------------------------------------ + -- end accell + ------------------------------------------------------ + + --self.object:get_luaentity() --hack way to fix jitter on climb + + --adjust climb indicator + local climb_rate = velocity.y * 1.5 + if climb_rate > 5 then climb_rate = 5 end + if climb_rate < -5 then + climb_rate = -5 + end + + --is an stall, force a recover + if self._angle_of_attack > 5 and climb_rate < -3 then + self._elevator_angle = 0 + self._angle_of_attack = -1 + newpitch = math.rad(self._angle_of_attack) + end + + --minetest.chat_send_all('rate '.. climb_rate) + local climb_angle = demoiselle.get_gauge_angle(climb_rate) + + local indicated_speed = longit_speed + if indicated_speed < 0 then indicated_speed = 0 end + local speed_angle = demoiselle.get_gauge_angle(indicated_speed, -45) + --adjust power indicator + local power_indicator_angle = demoiselle.get_gauge_angle(self._power_lever/10) + 90 + local energy_indicator_angle = (demoiselle.get_gauge_angle((DEMOISELLE_MAX_FUEL - self._energy)*2)) - 90 + + if is_attached then + if self._show_hud then + demoiselle.update_hud(player, climb_angle, speed_angle, power_indicator_angle, energy_indicator_angle) + else + demoiselle.remove_hud(player) + end + end + + --apply rotations + if newyaw~=yaw or newpitch~=pitch or newroll~=roll then + self.object:set_rotation({x=newpitch,y=newyaw,z=newroll}) + end + + --adjust elevator pitch (3d model) + self.object:set_bone_position("empenagem", {x=0, y=33.5, z=-0.5}, {x=-self._elevator_angle/2.5, y=0, z=self._rudder_angle/2.5}) + + -- calculate energy consumption -- + demoiselle.consumptionCalc(self, accel) + + --test collision + demoiselle.testImpact(self, velocity, curr_pos) + + --saves last velocity for collision detection (abrupt stop) + self._last_vel = self.object:get_velocity() +end + diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..59ac2cb --- /dev/null +++ b/depends.txt @@ -0,0 +1,3 @@ +mobkit +default +biofuel diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..5ef5ce7 --- /dev/null +++ b/init.lua @@ -0,0 +1,53 @@ +demoiselle={} +demoiselle.fuel = {['biofuel:biofuel'] = 1,['biofuel:bottle_fuel'] = 1, + ['biofuel:phial_fuel'] = 0.25, ['biofuel:fuel_can'] = 10} +demoiselle.gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.8 +demoiselle.wing_angle_of_attack = 3.5 +demoiselle.min_speed = 4 +demoiselle.max_engine_acc = 4 --5 +demoiselle.lift = 14 --12 + +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_global_definitions.lua") +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_crafts.lua") +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_control.lua") +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_fuel_management.lua") +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_custom_physics.lua") +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_utilities.lua") +dofile(minetest.get_modpath("demoiselle") .. DIR_DELIM .. "demoiselle_entities.lua") + +-- +-- helpers and co. +-- + +-- +-- items +-- + +-- add chatcommand to eject from demoiselle + +minetest.register_chatcommand("demoiselle_eject", { + params = "", + description = "Ejects from demoiselle", + privs = {interact = true}, + func = function(name, param) + local colorstring = core.colorize('#ff0000', " >>> you are not inside your demoiselle") + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local parent = attached_to:get_attach() + if parent ~= nil then + local entity = parent:get_luaentity() + if entity.driver_name == name and entity.name == "demoiselle:demoiselle" then + hidroplane.dettach(entity, player) + else + minetest.chat_send_player(name,colorstring) + end + end + else + minetest.chat_send_player(name,colorstring) + end + end +}) + + diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..5294987 --- /dev/null +++ b/mod.conf @@ -0,0 +1,5 @@ +name = demoiselle +depends = mobkit,default,biofuel +author = APercy +description = Adds a Demoiselle, an historical airplane +title = Demoiselle diff --git a/models/demoiselle.b3d b/models/demoiselle.b3d new file mode 100755 index 0000000..273fb6b Binary files /dev/null and b/models/demoiselle.b3d differ diff --git a/models/demoiselle_propeller.b3d b/models/demoiselle_propeller.b3d new file mode 100755 index 0000000..9788196 Binary files /dev/null and b/models/demoiselle_propeller.b3d differ diff --git a/models/demoiselle_seat_base.b3d b/models/demoiselle_seat_base.b3d new file mode 100644 index 0000000..ccf1db3 Binary files /dev/null and b/models/demoiselle_seat_base.b3d differ diff --git a/sounds/demoiselle_collision.ogg b/sounds/demoiselle_collision.ogg new file mode 100644 index 0000000..b15fbb1 Binary files /dev/null and b/sounds/demoiselle_collision.ogg differ diff --git a/sounds/demoiselle_engine.ogg b/sounds/demoiselle_engine.ogg new file mode 100644 index 0000000..2267db3 Binary files /dev/null and b/sounds/demoiselle_engine.ogg differ diff --git a/textures/demoiselle.png b/textures/demoiselle.png new file mode 100755 index 0000000..b337840 Binary files /dev/null and b/textures/demoiselle.png differ diff --git a/textures/demoiselle_bamboo.png b/textures/demoiselle_bamboo.png new file mode 100755 index 0000000..8b58453 Binary files /dev/null and b/textures/demoiselle_bamboo.png differ diff --git a/textures/demoiselle_black.png b/textures/demoiselle_black.png new file mode 100755 index 0000000..5d3d38c Binary files /dev/null and b/textures/demoiselle_black.png differ diff --git a/textures/demoiselle_body.png b/textures/demoiselle_body.png new file mode 100755 index 0000000..9dfaaba Binary files /dev/null and b/textures/demoiselle_body.png differ diff --git a/textures/demoiselle_canvas.png b/textures/demoiselle_canvas.png new file mode 100755 index 0000000..98443e1 Binary files /dev/null and b/textures/demoiselle_canvas.png differ diff --git a/textures/demoiselle_canvas_structure.png b/textures/demoiselle_canvas_structure.png new file mode 100755 index 0000000..b384af9 Binary files /dev/null and b/textures/demoiselle_canvas_structure.png differ diff --git a/textures/demoiselle_copper.png b/textures/demoiselle_copper.png new file mode 100755 index 0000000..f90530f Binary files /dev/null and b/textures/demoiselle_copper.png differ diff --git a/textures/demoiselle_grey.png b/textures/demoiselle_grey.png new file mode 100755 index 0000000..af9db27 Binary files /dev/null and b/textures/demoiselle_grey.png differ diff --git a/textures/demoiselle_helice.png b/textures/demoiselle_helice.png new file mode 100755 index 0000000..500fd16 Binary files /dev/null and b/textures/demoiselle_helice.png differ diff --git a/textures/demoiselle_hud_panel.png b/textures/demoiselle_hud_panel.png new file mode 100644 index 0000000..85d5cef Binary files /dev/null and b/textures/demoiselle_hud_panel.png differ diff --git a/textures/demoiselle_ind_box.png b/textures/demoiselle_ind_box.png new file mode 100644 index 0000000..dabada9 Binary files /dev/null and b/textures/demoiselle_ind_box.png differ diff --git a/textures/demoiselle_metal.png b/textures/demoiselle_metal.png new file mode 100755 index 0000000..336ba1c Binary files /dev/null and b/textures/demoiselle_metal.png differ diff --git a/textures/demoiselle_metal2.png b/textures/demoiselle_metal2.png new file mode 100755 index 0000000..35db717 Binary files /dev/null and b/textures/demoiselle_metal2.png differ diff --git a/textures/demoiselle_painting.png b/textures/demoiselle_painting.png new file mode 100755 index 0000000..769e09b Binary files /dev/null and b/textures/demoiselle_painting.png differ diff --git a/textures/demoiselle_wheel.png b/textures/demoiselle_wheel.png new file mode 100755 index 0000000..3b7c5b3 Binary files /dev/null and b/textures/demoiselle_wheel.png differ diff --git a/textures/demoiselle_white.png b/textures/demoiselle_white.png new file mode 100755 index 0000000..12a349f Binary files /dev/null and b/textures/demoiselle_white.png differ diff --git a/textures/demoiselle_wings.png b/textures/demoiselle_wings.png new file mode 100755 index 0000000..03c26ff Binary files /dev/null and b/textures/demoiselle_wings.png differ