frist commit

master
Alexsandro Percy 2021-03-09 19:23:16 -03:00
commit bca9243933
33 changed files with 1359 additions and 0 deletions

167
LICENSE Normal file
View File

@ -0,0 +1,167 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
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.

49
README.md Normal file
View File

@ -0,0 +1,49 @@
Minetest 5.4 mod: Ultralight Trike
========================================
As the title say, it's a flying machine
This one tries to implement some flight physics, using the mobkit mod.
In order to fly, it is necessary to first supply the aircraft with biofuel.
Then with a gallon selected, punch it against the trike.
You can use 10 of them to fill the tank. See the fuel gauge on the aircraft
panel (right below, with a green F). 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 move the wing, to increase and decrease the attack angle (it's important).
Right and Left (A and D) controls the yaw.
Then to fly, start the engine with the special key E. Press jump (space)
to increase the engine power (check the panel for the indicator marked with a yellow P).
Adjust to the maximum. Push the wing (W) and wait for speed to lift.
During the cruise flight, it is ideal to keep the power setting below the red range,
to control fuel consumption. Use the climb indicator to stabilize altitude,
as at high altitudes you lose support and you spend more fuel.
For landing, a traffic circuit can be made, lowering the power and increasing
the angle of attack of the wing to reduce speed and altitude (acting as a flap).
Or to force the descent by pulling the wing and pushing it forward when
approaching, regulating the speed.
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
To repair damages, you can use the repair tool. It subtracts steel ingots to increase
airplane hp.
It can be painted using dye of any color you want, you must punch the airplane with the dye.
Biofuel mod can be found here: https://github.com/APercy/minetest_biofuel
-----------------------
License of source code:
LGPL v3 (see file LICENSE)
License of media (textures and sounds):
---------------------------------------
engine.ogg and collision.ogg by APercy.
trike model and tectures by APercy.

3
depends.txt Normal file
View File

@ -0,0 +1,3 @@
mobkit
default
biofuel

52
init.lua Normal file
View File

@ -0,0 +1,52 @@
trike={}
trike.gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.8
trike.colors ={
black='#2b2b2b',
blue='#0063b0',
brown='#8c5922',
cyan='#07B6BC',
dark_green='#567a42',
dark_grey='#6d6d6d',
green='#4ee34c',
grey='#9f9f9f',
magenta='#ff0098',
orange='#ff8b0e',
pink='#ff62c6',
red='#dc1818',
violet='#a437ff',
white='#FFFFFF',
yellow='#ffe400',
}
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_crafts.lua")
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_control.lua")
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_fuel_management.lua")
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_custom_physics.lua")
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_utilities.lua")
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_entities.lua")
--
-- helpers and co.
--
local creative_exists = minetest.global_exists("creative")
--
-- items
--
settings = Settings(minetest.get_worldpath() .. "/trike_settings.conf")
local function fetch_setting(name)
local sname = name
return settings and settings:get(sname) or minetest.settings:get(sname)
end
trike.restricted = fetch_setting("restricted")
minetest.register_privilege("flight_licence", {
description = "Gives a flight licence to the player",
give_to_singleplayer = true
})

5
mod.conf Normal file
View File

@ -0,0 +1,5 @@
name = trike
depends = mobkit,default,biofuel
author = APercy
description = Adds a craftable trike
title = Trike

BIN
models/pointer.b3d Normal file

Binary file not shown.

BIN
models/src/trike_black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

BIN
models/src/trike_grey.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
models/src/trike_metal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
models/src/trike_panel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

BIN
models/src/trike_rotor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
models/src/trike_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
models/src/trike_wing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
models/trike_body.b3d Normal file

Binary file not shown.

Binary file not shown.

BIN
models/trike_propeller.b3d Normal file

Binary file not shown.

BIN
models/trike_wing.b3d Normal file

Binary file not shown.

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

BIN
sounds/collision.ogg Normal file

Binary file not shown.

BIN
sounds/engine.ogg Normal file

Binary file not shown.

BIN
textures/icon1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
textures/icon2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
textures/icon3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
textures/repair_tool.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

144
trike_control.lua Normal file
View File

@ -0,0 +1,144 @@
--global constants
trike.trike_last_time_command = 0
trike.vector_up = vector.new(0, 1, 0)
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_utilities.lua")
function trike.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 trike.control(self, dtime, hull_direction, longit_speed, longit_drag, later_speed, later_drag, accel)
trike.trike_last_time_command = trike.trike_last_time_command + dtime
if trike.trike_last_time_command > 1 then trike.trike_last_time_command = 1 end
if self.driver_name == nil then return end
local player = minetest.get_player_by_name(self.driver_name)
local retval_accel = accel
local rudder_limit = 30
-- player control
if player then
local ctrl = player:get_player_control()
if ctrl.aux1 and trike.trike_last_time_command > 0.3 then
trike.trike_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)
elseif self._engine_running == false and self._energy > 0 then
self._engine_running = true
-- sound and animation
self.sound_handle = minetest.sound_play({name = "engine"},
{object = self.object, gain = 2.0, pitch = 0.5 + ((self._power_lever/100)/2),max_hear_distance = 32, loop = true,})
self.engine:set_animation_frame_speed(60)
end
end
self._acceleration = 0
if self._engine_running then
local engineacc = 0
--compute the acceleration
local max_engine_acc = 3.5
--engine acceleration calc
engineacc = (self._power_lever * max_engine_acc) / 100;
self.engine:set_animation_frame_speed(60 + self._power_lever)
--increase power lever
if self._power_lever < 100 and ctrl.jump then
self._power_lever = self._power_lever + 1
if self._power_lever > 100 then
self._power_lever = 100
engineacc = max_engine_acc
else
--sound
minetest.sound_stop(self.sound_handle)
self.sound_handle = minetest.sound_play({name = "engine"},
{object = self.object, gain = 2.0, pitch = 0.5 + ((self._power_lever/100)/2),max_hear_distance = 32, loop = true,})
end
end
--decrease power lever
if ctrl.sneak then
if self._power_lever > 0 then
self._power_lever = self._power_lever - 1
if self._power_lever < 0 then self._power_lever = 0 end
end
if self._power_lever <= 0 then
--wheel break
if longit_speed >= 0.1 then
engineacc = -1
end
if longit_speed <= -0.1 then
engineacc = 1
end
if abs(longit_speed) < 0.1 then
self.object:set_velocity(vector.new())
end
else
--sound
minetest.sound_stop(self.sound_handle)
self.sound_handle = minetest.sound_play({name = "engine"},
{object = self.object, gain = 2.0, pitch = 0.5 + ((self._power_lever/100)/2),max_hear_distance = 32, loop = true,})
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
if longit_speed < 1.0 and ctrl.jump then
paddleacc = 0.5
elseif longit_speed > -1.0 and ctrl.sneak then
paddleacc = -0.5
end
self._acceleration = paddleacc
end
local hull_acc = vector.multiply(hull_direction,self._acceleration)
retval_accel=vector.add(accel,hull_acc)
--wing
local wing_limit = 10
if ctrl.down then
self._angle_of_attack = math.max(self._angle_of_attack-10*dtime,1)
elseif ctrl.up then
self._angle_of_attack = math.min(self._angle_of_attack+10*dtime,wing_limit)
end
-- yaw
if ctrl.right then
self._rudder_angle = math.max(self._rudder_angle-30*dtime,-rudder_limit)
elseif ctrl.left then
self._rudder_angle = math.min(self._rudder_angle+30*dtime,rudder_limit)
end
end
if longit_speed > 0 then
local factor = 1
if self._rudder_angle > 0 then factor = -1 end
local correction = (rudder_limit*(longit_speed/1000)) * factor
self._rudder_angle = self._rudder_angle + correction
end
return retval_accel
end

85
trike_crafts.lua Normal file
View File

@ -0,0 +1,85 @@
-- engine
minetest.register_craftitem("trike:wing",{
description = "Trike wing",
inventory_image = "icon3.png",
})
-- hull
minetest.register_craftitem("trike:fuselage",{
description = "Trike body",
inventory_image = "icon2.png",
})
-- trike
minetest.register_craftitem("trike:trike", {
description = "Ultralight Trike",
inventory_image = "icon1.png",
liquids_pointable = false,
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]
if nodedef.liquidtype == "none" then
pointed_pos.y=pointed_pos.y+1.5
local trike = minetest.add_entity(pointed_pos, "trike:trike")
if trike and placer then
local ent = trike:get_luaentity()
local owner = placer:get_player_name()
ent.owner = owner
trike:set_yaw(placer:get_look_horizontal())
itemstack:take_item()
end
end
return itemstack
end,
})
-- trike repair
minetest.register_craftitem("trike:repair_tool",{
description = "Trike repair tool",
inventory_image = "repair_tool.png",
})
--
-- crafting
--
if minetest.get_modpath("default") then
minetest.register_craft({
output = "trike:wing",
recipe = {
{"", "wool:white", "" },
{"wool:white", "default:steel_ingot", "wool:white"},
{"farming:string", "wool:white", "farming:string"},
}
})
minetest.register_craft({
output = "trike:fuselage",
recipe = {
{"", "default:diamondblock", ""},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "default:mese_block", "default:steel_ingot"},
}
})
minetest.register_craft({
output = "trike:trike",
recipe = {
{"", ""},
{"trike:fuselage", "trike:wing"},
}
})
minetest.register_craft({
output = "trike:repair_tool",
recipe = {
{"default:steel_ingot", "", "default:steel_ingot"},
{"", "default:steel_ingot", ""},
{"", "default:steel_ingot", ""},
},
})
end

66
trike_custom_physics.lua Normal file
View File

@ -0,0 +1,66 @@
local min = math.min
local abs = math.abs
local deg = math.deg
function trike.physics(self)
local vel=self.object:get_velocity()
-- dumb friction
--[[if self.isonground and not self.isinliquid then
self.object:set_velocity({x= vel.x> 0.2 and vel.x*mobkit.friction or 0,
y=vel.y,
z=vel.z > 0.2 and vel.z*mobkit.friction or 0})
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
-- buoyancy
local surface = nil
local surfnodename = nil
local spos = mobkit.get_stand_pos(self)
spos.y = spos.y+0.01
-- get surface height
local snodepos = mobkit.get_node_pos(spos)
local surfnode = mobkit.nodeatpos(spos)
while surfnode and (surfnode.drawtype == 'liquid' or surfnode.drawtype == 'flowingliquid') do
surfnodename = surfnode.name
surface = snodepos.y +0.5
if surface > spos.y+self.height then break end
snodepos.y = snodepos.y+1
surfnode = mobkit.nodeatpos(snodepos)
end
self.isinliquid = surfnodename
if surface then -- standing in liquid
-- self.isinliquid = true
local submergence = min(surface-spos.y,self.height)/self.height
-- local balance = self.buoyancy*self.height
local buoyacc = mobkit.gravity*(self.buoyancy-submergence)
mobkit.set_acceleration(self.object,
{x=-vel.x*self.water_drag,y=buoyacc-vel.y*abs(vel.y)*0.4,z=-vel.z*self.water_drag})
else
-- self.isinliquid = false
self.object:set_acceleration({x=0,y=mobkit.gravity,z=0})
end
end

459
trike_entities.lua Normal file
View File

@ -0,0 +1,459 @@
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_global_definitions.lua")
--
-- entity
--
trike.vector_up = vector.new(0, 1, 0)
minetest.register_entity('trike:engine',{
initial_properties = {
physical = false,
collide_with_objects=false,
pointable=false,
visual = "mesh",
mesh = "trike_propeller.b3d",
--visual_size = {x = 3, y = 3, z = 3},
textures = {"trike_rotor.png", "trike_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('trike:front_wheel',{
initial_properties = {
physical = false,
collide_with_objects=false,
pointable=false,
visual = "mesh",
mesh = "trike_front_wheel.b3d",
--visual_size = {x = 3, y = 3, z = 3},
textures = {"trike_metal.png", "trike_black.png", "trike_metal.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('trike:wing',{
initial_properties = {
physical = false,
collide_with_objects=true,
pointable=false,
visual = "mesh",
mesh = "trike_wing.b3d",
backface_culling = false,
textures = {"trike_black.png", "trike_black.png", "trike_metal.png", "trike_wing_color.png", "trike_wing.png"},
},
_color="",
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,
})
--
-- fuel
--
minetest.register_entity('trike:pointer',{
initial_properties = {
physical = false,
collide_with_objects=false,
pointable=false,
visual = "mesh",
mesh = "pointer.b3d",
visual_size = {x = 0.4, y = 0.4, z = 0.4},
textures = {"trike_grey.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("trike:trike", {
initial_properties = {
physical = true,
collide_with_objects = true,
collisionbox = {-1.2, 0.0, -1.2, 1.2, 3, 1.2}, --{-1,0,-1, 1,0.3,1},
selectionbox = {-1, 0, -1, 1, 1, 1},
visual = "mesh",
mesh = "trike_body.b3d",
textures = {"trike_black.png", "trike_metal.png", "trike_metal.png", "trike_metal.png",
"trike_metal.png", "trike_metal.png", "trike_painting.png", "trike_black.png",
"trike_white.png", "trike_black.png", "trike_black.png", "trike_black.png",
"trike_grey.png", "trike_panel.png", "trike_black.png", "trike_metal.png", "trike_black.png"},
},
textures = {},
driver_name = nil,
sound_handle = nil,
owner = "",
static_save = true,
infotext = "A nice ultralight",
hp_max = 50,
buoyancy = 2,
physics = trike.physics,
_color = "#0063b0",
_rudder_angle = 0,
_angle_of_attack = 0,
_acceleration = 0,
_engine_running = false,
_angle_of_attack = 2,
_power_lever = 0,
_energy = 0.001,
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_color = self._color,
stored_power_lever = self._power_lever,
})
end,
on_activate = function(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._color = data.stored_color
self._power_lever = data.stored_power_lever
--minetest.debug("loaded: ", self._energy)
end
trike.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,'trike:engine')
engine:set_attach(self.object,'',{x=0,y=0,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 wing=minetest.add_entity(pos,'trike:wing')
wing:set_attach(self.object,'',{x=0,y=29,z=0},{x=0,y=0,z=0})
-- set the animation once and later only change the speed
self.wing = wing
local wheel=minetest.add_entity(pos,'trike:front_wheel')
wheel:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
-- set the animation once and later only change the speed
wheel:set_animation({x = 1, y = 12}, 0, 0, true)
self.wheel = wheel
local fuel_gauge=minetest.add_entity(pos,'trike:pointer')
local energy_indicator_angle = trike.get_gauge_angle(self._energy)
fuel_gauge:set_attach(self.object,'',TRIKE_GAUGE_FUEL_POSITION,{x=0,y=0,z=energy_indicator_angle})
self.fuel_gauge = fuel_gauge
local power_gauge=minetest.add_entity(pos,'trike:pointer')
local power_indicator_angle = trike.get_gauge_angle(self._power_lever)
power_gauge:set_attach(self.object,'',TRIKE_GAUGE_POWER_POSITION,{x=0,y=0,z=power_indicator_angle})
self.power_gauge = power_gauge
local climb_gauge=minetest.add_entity(pos,'trike:pointer')
local climb_angle = trike.get_gauge_angle(0)
climb_gauge:set_attach(self.object,'',TRIKE_GAUGE_CLIMBER_POSITION,{x=0,y=0,z=climb_angle})
self.climb_gauge = climb_gauge
trike.paint(self, self.object, self._color, "trike_painting.png")
trike.paint(self, self.wing, self._color, "trike_wing_color.png")
self.object:set_armor_groups({immortal=1})
mobkit.actfunc(self, staticdata, dtime_s)
end,
on_step = function(self, dtime)
mobkit.stepfunc(self, dtime)
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 newpitch = pitch
local roll = rotation.z
local newroll=roll
local velocity = self.object:get_velocity()
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 = trike.dot(velocity,hull_direction)
local longit_drag = vector.multiply(hull_direction,longit_speed*longit_speed*LONGIT_DRAG_FACTOR*-1*trike.sign(longit_speed))
local later_speed = trike.dot(velocity,nhdir)
--minetest.chat_send_all('later_speed: '.. later_speed)
local later_drag = vector.multiply(nhdir,later_speed*later_speed*LATER_DRAG_FACTOR*-1*trike.sign(later_speed))
local accel = vector.add(longit_drag,later_drag)
local is_attached = trike.checkAttach(self)
if is_attached then
--control
accel = trike.control(self, dtime, hull_direction, longit_speed, longit_drag, later_speed, later_drag, accel) or vel
else
-- for some engine error the player can be detached from the machine, so lets set him attached again
trike.checkattachBug(self)
end
trike.testImpact(self, velocity)
-- new yaw
if math.abs(self._rudder_angle)>5 then
local turn_rate = math.rad(24)
newyaw = yaw + self.dtime*(1 - 1 / (math.abs(longit_speed) + 1)) * self._rudder_angle / 30 * turn_rate * trike.sign(longit_speed)
end
-- calculate energy consumption --
trike.consumptionCalc(self, accel)
--roll adjust
---------------------------------
local sdir = minetest.yaw_to_dir(newyaw)
local snormal = {x=sdir.z,y=0,z=-sdir.x} -- rightside, dot is negative
local prsr = trike.dot(snormal,nhdir)
local rollfactor = -20
newroll = (prsr*math.rad(rollfactor))*(later_speed)
--minetest.chat_send_all('newroll: '.. newroll)
---------------------------------
-- end roll
-- pitch
newpitch = self._angle_of_attack/200 --(velocity.y * math.rad(6))
-- adjust pitch by velocity
local touching_ground, liquid_below = trike.check_node_below(self.object)
--self.isinliquid or
if touching_ground then --isn't flying?
if newpitch < 0 then newpitch = 0 end
local min_speed = 4
if longit_speed < min_speed then
if newpitch > 0 then
local percentage = ((longit_speed * 100)/min_speed)/100
newpitch = newpitch * percentage
if newpitch < 0 then newpitch = 0 end
end
end
--animate wheels
self.object:set_animation_frame_speed(longit_speed * 10)
self.wheel:set_animation_frame_speed(longit_speed * 10)
else
--stop wheels
self.object:set_animation_frame_speed(0)
self.wheel:set_animation_frame_speed(0)
end
--adjust wing pitch (3d model)
self.wing:set_attach(self.object,'',{x=0,y=29,z=0},{x=-self._angle_of_attack,y=0,z=0})
--adjust power indicator
local power_indicator_angle = trike.get_gauge_angle(self._power_lever/10)
self.power_gauge:set_attach(self.object,'',TRIKE_GAUGE_POWER_POSITION,{x=0,y=0,z=power_indicator_angle})
if accel ~= nil then
--lift calculation
accel.y = accel_y
local wing_dir = mobkit.rot_to_dir({x=self._angle_of_attack/30,y=newyaw,z=newroll})
combined_acc = trike.getLiftAccel(self, velocity, accel, longit_speed, wing_dir)
-- end lift
self.object:set_acceleration(combined_acc)
end
if newyaw~=yaw or newpitch~=pitch or newroll~=roll then
self.object:set_rotation({x=newpitch,y=newyaw,z=newroll})
end
--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
--minetest.chat_send_all('rate '.. climb_rate)
local climb_angle = trike.get_gauge_angle(climb_rate)
self.climb_gauge:set_attach(self.object,'',TRIKE_GAUGE_CLIMBER_POSITION,{x=0,y=0,z=climb_angle})
--saves last velocy for collision detection (abrupt stop)
self.last_vel = self.object:get_velocity()
end,
on_punch = function(self, puncher, ttime, toolcaps, dir, damage)
if not puncher or not puncher:is_player() then
return
end
local name = puncher:get_player_name()
if self.owner and self.owner ~= name and self.owner ~= "" then return 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 touching_ground, liquid_below = trike.check_node_below(self.object)
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 item_name == "biofuel:biofuel" and self._engine_running == false and self.owner == name then
--refuel
trike.loadFuel(self, puncher:get_player_name())
return
end
--repair
if item_name == "trike: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")
local taken = inv:remove_item("main", stack)
self.hp_max = self.hp_max + 10
if self.hp_max > 50 then self.hp_max = 50 end
trike.setText(self)
end
end
return
end
-- deal with painting or destroying
if itmstck then
local _,indx = item_name:find('dye:')
if indx then
--lets paint!!!!
local color = item_name:sub(indx+1)
local colstr = trike.colors[color]
--minetest.chat_send_all(color ..' '.. dump(colstr))
if colstr then
trike.paint(self, self.object, colstr, "trike_painting.png")
trike.paint(self, self.wing, colstr, "trike_wing_color.png")
itmstck:set_count(itmstck:get_count()-1)
puncher:set_wielded_item(itmstck)
end
-- end painting
else -- deal damage
if not self.driver and toolcaps and toolcaps.damage_groups and toolcaps.damage_groups.fleshy and item_name ~= "biofuel:biofuel" 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,
})
trike.setText(self)
end
end
end
if self.hp_max <= 0 then
trike.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()
local can_access = true
if trike.restricted == "true" then
can_access = minetest.check_player_privs(clicker, {flight_licence=true})
end
if can_access then
if self.owner and self.owner ~= name and self.owner ~= "" then return end
if self.owner == "" then
self.owner = name
end
if name == self.driver_name then
trike.setText(self)
self._engine_running = false
-- driver clicked the object => driver gets off the vehicle
self.driver_name = nil
-- 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)
-- detach the player
clicker:set_detach()
player_api.player_attached[name] = nil
clicker:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
player_api.set_animation(clicker, "stand")
self.driver = nil
self.object:set_acceleration(vector.multiply(trike.vector_up, -trike.gravity))
elseif not self.driver_name then
-- no driver => clicker is new driver
trike.attach(self, clicker)
end
else
minetest.show_formspec(name, "trike:flightlicence",
"size[4,2]" ..
"label[0.0,0.0;Sorry ...]"..
"label[0.0,0.7;You need a flight licence to fly it.]" ..
"label[0.0,1.0;You must obtain it from server admin.]" ..
"button_exit[1.5,1.9;0.9,0.1;e;Exit]")
end
end,
})

45
trike_fuel_management.lua Normal file
View File

@ -0,0 +1,45 @@
function trike.loadFuel(self, player_name)
if self._energy < 9.5 then
local player = minetest.get_player_by_name(player_name)
local inv = player:get_inventory()
local inventory_fuel = "biofuel:biofuel"
if inv:contains_item("main", inventory_fuel) then
local stack = ItemStack(inventory_fuel .. " 1")
local taken = inv:remove_item("main", stack)
self._energy = self._energy + 1
if self._energy > 10 then self._energy = 10 end
local energy_indicator_angle = trike.get_gauge_angle(self._energy)
self.fuel_gauge:set_attach(self.object,'',TRIKE_GAUGE_FUEL_POSITION,{x=0,y=0,z=energy_indicator_angle})
end
else
print("Full tank.")
end
end
function trike.consumptionCalc(self, accel)
if accel == nil then return end
if self._energy > 0 and self._engine_running and accel ~= nil then
local zero_reference = vector.new()
local acceleration = trike.get_hipotenuse_value(accel, zero_reference)
local consumed_power = self._power_lever/500000
--minetest.chat_send_all('consumed: '.. consumed_power)
self._energy = self._energy - consumed_power;
local energy_indicator_angle = trike.get_gauge_angle(self._energy)
if self.fuel_gauge:get_luaentity() then
self.fuel_gauge:set_attach(self.object,'',TRIKE_GAUGE_FUEL_POSITION,{x=0,y=0,z=energy_indicator_angle})
else
--in case it have lost the entity by some conflict
--self.fuel_gauge=minetest.add_entity(TRIKE_GAUGE_POINTER_POSITION,'trike:pointer')
--self.fuel_gauge:set_attach(self.object,'',TRIKE_GAUGE_FUEL_POSITION,{x=0,y=0,z=energy_indicator_angle})
end
end
if self._energy <= 0 and self._engine_running and accel ~= nil then
self._engine_running = false
if self.sound_handle then minetest.sound_stop(self.sound_handle) end
self.engine:set_animation_frame_speed(0)
end
end

View File

@ -0,0 +1,14 @@
--
-- constants
--
LONGIT_DRAG_FACTOR = 0.13*0.13
LATER_DRAG_FACTOR = 2.0
deg = math.deg
abs = math.abs
min = math.min
TRIKE_GAUGE_FUEL_POSITION = {x=1.5,y=8.2,z=14.2}
TRIKE_GAUGE_POWER_POSITION = {x=1.5,y=9.7,z=14.2}
TRIKE_GAUGE_CLIMBER_POSITION = {x=-1.2,y=9.55,z=14.2}

270
trike_utilities.lua Normal file
View File

@ -0,0 +1,270 @@
dofile(minetest.get_modpath("trike") .. DIR_DELIM .. "trike_global_definitions.lua")
function trike.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 trike.dot(v1,v2)
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
end
function trike.sign(n)
return n>=0 and 1 or -1
end
function trike.minmax(v,m)
return math.min(math.abs(v),m)*trike.sign(v)
end
--lift
function trike.getLiftAccel(self, velocity, accel, longit_speed, hull_direction)
--lift calculations
-----------------------------------------------------------
local max_height = 1500
local retval = accel
if longit_speed > 1.0 then
local angle_of_attack = (self._angle_of_attack) / 10
local lift = 4.8 --lift 5
local daoa = deg(angle_of_attack)
local curr_pos = self.object:get_pos()
local curr_percent_height = (100 - ((curr_pos.y * 100) / max_height))/100 --to decrease the lift coefficient at hight altitudes
local cross = vector.cross(hull_direction,velocity)
local lift_dir = vector.normalize(vector.cross(hull_direction,cross))
local lift_coefficient = (0.24*abs(daoa)*(1/(0.025*daoa+3))^4*math.sign(angle_of_attack))*curr_percent_height
local lift_val = lift*(vector.length(velocity)^2)*lift_coefficient
--local lift_acc = vector.multiply(lift_dir,lift_val) --original, but with a lot of interferences in roll
local lift_acc = lift_dir
lift_acc.y = lift_acc.y * lift_val --multiply only the Y axis for lift
--gliding calcs (to increase speed)
if not self.isinliquid then --is flying?
if velocity.y < 0 then
local speed = math.abs(velocity.y/2) + 0.75
lift_acc=vector.add(lift_acc,vector.multiply(hull_direction,speed))
end
end
retval = vector.add(accel,lift_acc)
end
-----------------------------------------------------------
-- end lift
return retval
end
function trike.get_gauge_angle(value)
local angle = value * 18
angle = angle - 90
angle = angle * -1
return angle
end
-- attach player
function trike.attach(self, player)
local name = player:get_player_name()
self.driver_name = name
-- temporary------
self.hp = 50 -- why? cause I can desist from destroy
------------------
-- attach the driver
player:set_attach(self.object, "", {x = 0, y = 9, z = 2}, {x = 0, y = 0, z = 0})
player:set_eye_offset({x = 0, y = 3, z = 3}, {x = 0, y = 3, z = 10})
player_api.player_attached[name] = true
-- make the driver sit
minetest.after(0.2, function()
local player = minetest.get_player_by_name(name)
if player then
player_api.set_animation(player, "sit")
end
end)
-- disable gravity
self.object:set_acceleration(vector.new())
end
--painting
function trike.paint(self, object, colstr, search_string)
if colstr then
self._color = colstr
local entity = object:get_luaentity()
local l_textures = entity.initial_properties.textures
for _, texture in ipairs(l_textures) do
local i,indx = texture:find(search_string)
if indx then
l_textures[_] = search_string .."^[multiply:".. colstr
end
end
object:set_properties({textures=l_textures})
end
end
-- destroy the boat
function trike.destroy(self)
if self.sound_handle then
minetest.sound_stop(self.sound_handle)
self.sound_handle = nil
end
if self.driver_name then
local player = minetest.get_player_by_name(self.driver_name)
-- detach the driver first (puncher must be driver)
player:set_detach()
player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
player_api.player_attached[self.driver_name] = nil
-- player should stand again
player_api.set_animation(player, "stand")
self.driver_name = nil
end
local pos = self.object:get_pos()
if self.fuel_gauge then self.fuel_gauge:remove() end
if self.power_gauge then self.power_gauge:remove() end
if self.climb_gauge then self.climb_gauge:remove() end
if self.engine then self.engine:remove() end
if self.wing then self.wing:remove() end
if self.wheel then self.wheel:remove() end
self.object:remove()
pos.y=pos.y+2
for i=1,4 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')
end
for i=1,3 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
--minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'trike:trike')
local total_biofuel = math.floor(self._energy) - 1
for i=0,total_biofuel do
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'biofuel:biofuel')
end
end
function trike.check_node_below(obj)
local pos_below = obj:get_pos()
if pos_below then
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
return nil, nil
end
function trike.checkAttach(self)
if self.owner then
local player = minetest.get_player_by_name(self.owner)
if player then
local player_attach = player:get_attach()
if player_attach then
if player_attach == self.object then
return true
end
end
end
end
return false
end
function trike.setText(self)
local properties = self.object:get_properties()
local formatted = string.format(
"%.2f", self.hp_max
)
if properties then
properties.infotext = "Nice ultralight trike of " .. self.owner .. ". Current hp: " .. formatted
self.object:set_properties(properties)
end
end
function trike.testImpact(self, velocity)
collision = false
if self.last_vel == nil then return end
local impact = abs(trike.get_hipotenuse_value(velocity, self.last_vel))
if impact > 1 then
--minetest.chat_send_all('impact: '.. impact .. ' - hp: ' .. self.hp_max)
local p = self.object:get_pos()
local nodeu = mobkit.nodeatpos(mobkit.pos_shift(p,{y=1}))
local noded = mobkit.nodeatpos(mobkit.pos_shift(p,{y=-1}))
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
(noded and noded.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
local damage = impact
self.hp_max = self.hp_max - damage --subtract the impact value directly to hp meter
local curr_pos = self.object:get_pos()
if self.driver_name then
minetest.sound_play("collision", {
to_player = self.driver_name,
--pos = curr_pos,
--max_hear_distance = 5,
gain = 1.0,
fade = 0.0,
pitch = 1.0,
})
local player_name = self.driver_name
trike.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
trike.destroy(self)
end
local player = minetest.get_player_by_name(player_name)
if player:get_hp() > 0 then
player:set_hp(player:get_hp()-(damage/2))
end
end
end
end
function trike.checkattachBug(self)
-- for some engine error the player can be detached from the submarine, so lets set him attached again
local can_stop = true
if self.owner and self.driver_name then
-- attach the driver again
local player = minetest.get_player_by_name(self.owner)
if player then
trike.attach(self, player)
can_stop = false
end
end
if can_stop then
--detach player
if self.sound_handle ~= nil then
minetest.sound_stop(self.sound_handle)
self.sound_handle = nil
end
end
end