more mods
@@ -0,0 +1,9 @@
|
||||
This mod adds library for automobiles
|
||||
|
||||
licence of code
|
||||
MIT
|
||||
|
||||
physics_lib.lua - MIT by TheTermos
|
||||
|
||||
licence of the media:
|
||||
licence CC0
|
||||
@@ -0,0 +1,233 @@
|
||||
--global constants
|
||||
|
||||
automobiles_lib.vector_up = vector.new(0, 1, 0)
|
||||
|
||||
function automobiles_lib.check_road_is_ok(obj, max_acc_factor)
|
||||
local pos_below = obj:get_pos()
|
||||
pos_below.y = pos_below.y - 0.1
|
||||
local node_below = core.get_node(pos_below).name
|
||||
--core.chat_send_all(node_below)
|
||||
local nodedef = core.registered_nodes[node_below]
|
||||
if nodedef.liquidtype == "none" then
|
||||
local slow_nodes = {
|
||||
['default:ice '] = 0.01,
|
||||
['default:cave_ice'] = 0.01,
|
||||
}
|
||||
local acc = slow_nodes[node_below]
|
||||
if acc == nil then acc = max_acc_factor end
|
||||
return acc
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.set_yaw_by_mouse(self, dir, steering_limit)
|
||||
local rotation = self.object:get_rotation()
|
||||
local rot_y = math.deg(rotation.y)
|
||||
|
||||
local total = math.abs(math.floor(rot_y/360))
|
||||
|
||||
if rot_y < 0 then rot_y = rot_y + (360*total) end
|
||||
if rot_y > 360 then rot_y = rot_y - (360*total) end
|
||||
if rot_y >= 270 and dir <= 90 then dir = dir + 360 end
|
||||
if rot_y <= 90 and dir >= 270 then dir = dir - 360 end
|
||||
|
||||
local intensity = 2
|
||||
if self._intensity then intensity = self._intensity end
|
||||
local command = (rot_y - dir) * intensity
|
||||
if command < -90 then command = -90
|
||||
elseif command > 90 then command = 90 end
|
||||
--core.chat_send_all("rotation y: "..rot_y.." - dir: "..dir.." - command: "..(rot_y - dir))
|
||||
|
||||
--core.chat_send_all("rotation y: "..rot_y.." - dir: "..dir.." - command: "..command)
|
||||
|
||||
return (-command * steering_limit)/90
|
||||
end
|
||||
|
||||
function automobiles_lib.control(self, dtime, hull_direction, longit_speed, longit_drag, later_drag, accel, max_acc_factor, max_speed, steering_limit, steering_speed)
|
||||
self._last_time_command = self._last_time_command + dtime
|
||||
hull_direction = hull_direction or 0
|
||||
longit_speed = longit_speed or 0
|
||||
longit_drag = longit_drag or 0
|
||||
later_drag = later_drag or 0
|
||||
max_acc_factor = max_acc_factor or 0
|
||||
max_acc_factor = max_acc_factor
|
||||
|
||||
max_speed = max_speed or 0
|
||||
max_speed = math.min(max_speed,automobiles_lib.speed_limit)
|
||||
|
||||
steering_limit = steering_limit or 0
|
||||
steering_speed = steering_speed or 0
|
||||
|
||||
if self._last_time_command > 1 then self._last_time_command = 1 end
|
||||
|
||||
local player = core.get_player_by_name(self.driver_name)
|
||||
local retval_accel = accel;
|
||||
local stop = false
|
||||
|
||||
-- player control
|
||||
if player then
|
||||
local ctrl = player:get_player_control()
|
||||
local rot_x = player:get_look_vertical()
|
||||
local pi = math.pi
|
||||
local ang_min = -pi / 32
|
||||
local ang_max = pi / 64
|
||||
local gap = pi / 128
|
||||
|
||||
if self._one_hand == true then
|
||||
if rot_x < (ang_min - gap)then
|
||||
player:set_look_vertical(ang_min - gap)
|
||||
end
|
||||
if rot_x > (ang_max + gap) then
|
||||
player:set_look_vertical(ang_max + gap)
|
||||
end
|
||||
end
|
||||
|
||||
local acc = 0
|
||||
if self._energy > 0 then
|
||||
if self._one_hand == true then
|
||||
if rot_x <= ang_min then
|
||||
if longit_speed < max_speed then
|
||||
--get acceleration factor
|
||||
acc = automobiles_lib.check_road_is_ok(self.object, max_acc_factor)
|
||||
--core.chat_send_all('engineacc: '.. engineacc)
|
||||
if acc > 1 and acc < max_acc_factor and longit_speed > 0 then
|
||||
--improper road will reduce speed
|
||||
acc = -1
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if longit_speed < max_speed and ctrl.up then
|
||||
--get acceleration factor
|
||||
acc = automobiles_lib.check_road_is_ok(self.object, max_acc_factor)
|
||||
--core.chat_send_all('engineacc: '.. engineacc)
|
||||
if acc > 1 and acc < max_acc_factor and longit_speed > 0 then
|
||||
--improper road will reduce speed
|
||||
acc = -1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--reversing
|
||||
if self._one_hand == true then
|
||||
if rot_x >= ang_max then
|
||||
acc = -2
|
||||
end
|
||||
else
|
||||
if ctrl.sneak and longit_speed <= 1.0 and longit_speed > -1.0 then
|
||||
acc = -2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--break
|
||||
if self._one_hand == true then
|
||||
if rot_x >= ang_max and not ctrl.LMB then
|
||||
if longit_speed > 0 then
|
||||
acc = -5
|
||||
end
|
||||
if longit_speed < 0 then
|
||||
acc = 5
|
||||
if (longit_speed + acc) > 0 then
|
||||
acc = longit_speed * -1
|
||||
end
|
||||
end
|
||||
if math.abs(longit_speed) < 1 then
|
||||
stop = true
|
||||
end
|
||||
end
|
||||
else
|
||||
if ctrl.down or ctrl.jump then
|
||||
--[[if math.abs(longit_speed) > 0 then
|
||||
acc = -5 / (longit_speed / 2) -- lets set a brake efficience based on speed
|
||||
end]]--
|
||||
|
||||
--total stop
|
||||
--wheel break
|
||||
if longit_speed > 0 then
|
||||
acc = -5
|
||||
--[[if (longit_speed + acc) < 0 then
|
||||
acc = longit_speed * -1
|
||||
end]]--
|
||||
end
|
||||
if longit_speed < 0 then
|
||||
acc = 5
|
||||
if (longit_speed + acc) > 0 then
|
||||
acc = longit_speed * -1
|
||||
end
|
||||
end
|
||||
if math.abs(longit_speed) < 1 then
|
||||
stop = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if acc then retval_accel=vector.add(accel,vector.multiply(hull_direction,acc)) end
|
||||
|
||||
if ctrl.aux1 then
|
||||
if self._one_hand == true then
|
||||
local formspec_f = automobiles_lib.driver_formspec
|
||||
if self._formspec_function then formspec_f = self._formspec_function end
|
||||
formspec_f(self.driver_name)
|
||||
end
|
||||
--[[
|
||||
--sets the engine running - but sets a delay also, cause keypress
|
||||
if self._last_time_command > 0.3 then
|
||||
self._last_time_command = 0
|
||||
if self._engine_running then
|
||||
self._engine_running = false
|
||||
-- sound and animation
|
||||
if self.sound_handle then
|
||||
core.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 = core.sound_play({name = "engine"},
|
||||
{object = self.object, gain = 2.0, pitch = 1.0, max_hear_distance = 32, loop = true,})
|
||||
--self.engine:set_animation_frame_speed(30)
|
||||
end
|
||||
end]]--
|
||||
end
|
||||
|
||||
-- yaw
|
||||
local yaw_cmd = 0
|
||||
if self._yaw_by_mouse == true or self._one_hand == true then
|
||||
local rot_y = math.deg(player:get_look_horizontal())
|
||||
self._steering_angle = automobiles_lib.set_yaw_by_mouse(self, rot_y, steering_limit)
|
||||
else
|
||||
-- steering
|
||||
if ctrl.right then
|
||||
self._steering_angle = math.max(self._steering_angle-steering_speed*dtime,-steering_limit)
|
||||
elseif ctrl.left then
|
||||
self._steering_angle = math.min(self._steering_angle+steering_speed*dtime,steering_limit)
|
||||
else
|
||||
--center steering
|
||||
if longit_speed > 0 then
|
||||
local factor = 1
|
||||
if self._steering_angle > 0 then factor = -1 end
|
||||
local correction = (steering_limit*(longit_speed/75)) * factor
|
||||
local before_correction = self._steering_angle
|
||||
self._steering_angle = self._steering_angle + correction
|
||||
if math.sign(before_correction) ~= math.sign(self._steering_angle) then self._steering_angle = 0 end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local angle_factor = self._steering_angle / 60
|
||||
if angle_factor < 0 then angle_factor = angle_factor * -1 end
|
||||
local deacc_on_curve = longit_speed * angle_factor
|
||||
deacc_on_curve = deacc_on_curve * -1
|
||||
if deacc_on_curve then retval_accel=vector.add(retval_accel,vector.multiply(hull_direction,deacc_on_curve)) end
|
||||
|
||||
end
|
||||
|
||||
return retval_accel, stop
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
function automobiles_lib.physics(self)
|
||||
local friction = self._ground_friction or 0.99
|
||||
local vel=self.object:get_velocity()
|
||||
local new_velocity = vector.new()
|
||||
|
||||
--buoyancy
|
||||
local surface = nil
|
||||
local surfnodename = nil
|
||||
local spos = automobiles_lib.get_stand_pos(self)
|
||||
if not spos then return end
|
||||
spos.y = spos.y+0.01
|
||||
-- get surface height
|
||||
local snodepos = automobiles_lib.get_node_pos(spos)
|
||||
local surfnode = automobiles_lib.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 = automobiles_lib.nodeatpos(snodepos)
|
||||
end
|
||||
|
||||
self.isinliquid = surfnodename
|
||||
if surface then -- standing in liquid
|
||||
self.isinliquid = true
|
||||
end
|
||||
local last_accel = vector.new()
|
||||
if self._last_accel then
|
||||
last_accel = vector.new(self._last_accel)
|
||||
end
|
||||
|
||||
if self.isinliquid then
|
||||
self.water_drag = 0.2
|
||||
self.isinliquid = true
|
||||
local height = self.height
|
||||
local submergence = math.min(surface-spos.y,height)/height
|
||||
-- local balance = self.buoyancy*self.height
|
||||
local buoyacc = automobiles_lib.gravity*(-1)*(self.buoyancy-submergence)
|
||||
--local buoyacc = self._baloon_buoyancy*(self.buoyancy-submergence)
|
||||
local accell = {
|
||||
x=-vel.x*self.water_drag,
|
||||
y=buoyacc-(vel.y*math.abs(vel.y)*0.4),
|
||||
z=-vel.z*self.water_drag
|
||||
}
|
||||
if self.buoyancy >= 1 then self._engine_running = false end
|
||||
if last_accel then
|
||||
accell = vector.add(accell,last_accel)
|
||||
end
|
||||
new_velocity = vector.multiply(accell,self.dtime)
|
||||
else
|
||||
self.isinliquid = false
|
||||
new_velocity = vector.multiply(last_accel,self.dtime)
|
||||
end
|
||||
|
||||
if self.isonground and not self.isinliquid then
|
||||
--dumb friction
|
||||
new_velocity = {x=new_velocity.x*friction,
|
||||
y=new_velocity.y,
|
||||
z=new_velocity.z*friction}
|
||||
--core.chat_send_all(dump(friction))
|
||||
|
||||
-- bounciness
|
||||
if self.springiness and self.springiness > 0 and self.buoyancy >= 1 then
|
||||
local vnew = vector.new(new_velocity)
|
||||
|
||||
if not self.collided then -- ugly workaround for inconsistent collisions
|
||||
for _,k in ipairs({'y','z','x'}) do
|
||||
if new_velocity[k]==0 and math.abs(self.lastvelocity[k])> 0.1 then
|
||||
vnew[k]=-self.lastvelocity[k]*self.springiness
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not vector.equals(new_velocity,vnew) then
|
||||
self.collided = true
|
||||
else
|
||||
if self.collided then
|
||||
vnew = vector.new(self.lastvelocity)
|
||||
end
|
||||
self.collided = false
|
||||
end
|
||||
new_velocity = vnew
|
||||
end
|
||||
|
||||
--fix bug with unexpected moving
|
||||
if not self.driver_name and math.abs(vel.x) < 0.2 and math.abs(vel.z) < 0.2 then
|
||||
self.object:set_velocity({x=0,y=automobiles_lib.gravity*(-1)*self.dtime,z=0})
|
||||
if self.wheels then self.wheels:set_animation_frame_speed(0) end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
self.object:add_velocity(new_velocity)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,865 @@
|
||||
minetest.register_entity('automobiles_lib:pivot_mesh',{
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collide_with_objects=false,
|
||||
pointable=false,
|
||||
visual = "mesh",
|
||||
mesh = "automobiles_pivot_mesh.b3d",
|
||||
textures = {"automobiles_alpha.png",},
|
||||
},
|
||||
|
||||
on_activate = function(self,std)
|
||||
self.sdata = minetest.deserialize(std) or {}
|
||||
if self.sdata.remove then self.object:remove() end
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
end,
|
||||
|
||||
get_staticdata=function(self)
|
||||
self.sdata.remove=true
|
||||
return minetest.serialize(self.sdata)
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
minetest.register_entity('automobiles_lib:pointer',{
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collide_with_objects=false,
|
||||
pointable=false,
|
||||
visual = "mesh",
|
||||
mesh = "automobiles_pointer.b3d",
|
||||
visual_size = {x = 0.5, y = 0.5, z = 0.5},
|
||||
textures = {"automobiles_white.png"},
|
||||
},
|
||||
|
||||
on_activate = function(self,std)
|
||||
self.sdata = minetest.deserialize(std) or {}
|
||||
if self.sdata.remove then self.object:remove() end
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
end,
|
||||
|
||||
get_staticdata=function(self)
|
||||
self.sdata.remove=true
|
||||
return minetest.serialize(self.sdata)
|
||||
end,
|
||||
})
|
||||
|
||||
function automobiles_lib.on_rightclick (self, clicker)
|
||||
if not clicker or not clicker:is_player() then
|
||||
--return
|
||||
end
|
||||
|
||||
local name = clicker:get_player_name()
|
||||
--[[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
|
||||
local formspec_f = automobiles_lib.driver_formspec
|
||||
if self._formspec_function then formspec_f = self._formspec_function end
|
||||
formspec_f(name)
|
||||
else
|
||||
if name == self.owner or
|
||||
(self.driver_name == nil and
|
||||
( minetest.check_player_privs(clicker, "valet_parking") or minetest.check_player_privs(clicker, {server=true}) )
|
||||
) then
|
||||
if clicker:get_player_control().aux1 == true then
|
||||
automobiles_lib.show_vehicle_trunk_formspec(self, clicker, self._trunk_slots)
|
||||
else
|
||||
--is the owner, okay, lets attach
|
||||
local attach_driver_f = automobiles_lib.attach_driver
|
||||
if self._attach then attach_driver_f = self._attach end
|
||||
attach_driver_f(self, clicker)
|
||||
-- sound
|
||||
local base_pitch = 1
|
||||
if self._base_pitch then base_pitch = self._base_pitch end
|
||||
self.sound_handle = minetest.sound_play({name = self._engine_sound},
|
||||
{object = self.object, gain = 1, pitch = base_pitch, max_hear_distance = 30, loop = true,})
|
||||
end
|
||||
else
|
||||
--minetest.chat_send_all("clicou")
|
||||
--a passenger
|
||||
|
||||
if (automobiles_lib.is_minetest and not player_api.player_attached[name]) or
|
||||
(automobiles_lib.is_mcl and not mcl_player.player_attached[name]) then
|
||||
--there is no passenger, so lets attach
|
||||
if self.driver_name then
|
||||
local attach_pax_f = automobiles_lib.attach_pax
|
||||
if self._attach_pax then attach_pax_f = self._attach_pax end
|
||||
attach_pax_f(self, clicker, true)
|
||||
end
|
||||
else
|
||||
--there is a passenger
|
||||
--if you are the passenger, so deattach
|
||||
local dettach_pax_f = automobiles_lib.dettach_pax
|
||||
if self._dettach_pax then dettach_pax_f = self._dettach_pax end
|
||||
dettach_pax_f(self, clicker)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.on_punch (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 is_attached = false
|
||||
local obj_attach = puncher:get_attach()
|
||||
if obj_attach == self.driver_seat or obj_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
|
||||
|
||||
--refuel procedure
|
||||
--[[
|
||||
refuel works it car is stopped and engine is off
|
||||
]]--
|
||||
local velocity = self.object:get_velocity()
|
||||
local speed = automobiles_lib.get_hipotenuse_value(vector.new(), velocity)
|
||||
--if math.abs(speed) <= 0.1 then
|
||||
local was_refueled = automobiles_lib.loadFuel(self, puncher:get_player_name(), false, self._max_fuel)
|
||||
if was_refueled then
|
||||
return
|
||||
end
|
||||
--end
|
||||
-- end refuel
|
||||
|
||||
if is_attached == false then
|
||||
|
||||
-- deal with painting or destroying
|
||||
if itmstck then
|
||||
--race status restart
|
||||
if item_name == "checkpoints:status_restarter" and self._engine_running == false then
|
||||
--restart race current status
|
||||
self._last_checkpoint = ""
|
||||
self._total_laps = -1
|
||||
self._race_id = ""
|
||||
return
|
||||
end
|
||||
|
||||
local paint_f = automobiles_lib.set_paint
|
||||
if self._painting_function then paint_f = self._painting_function end
|
||||
if paint_f(self, puncher, itmstck) == false then
|
||||
local is_admin = false
|
||||
is_admin = minetest.check_player_privs(puncher, {server=true})
|
||||
--minetest.chat_send_all('owner '.. self.owner ..' - name '.. name)
|
||||
if not self.driver and (self.owner == name or is_admin == true) and toolcaps and
|
||||
toolcaps.damage_groups and toolcaps.damage_groups.fleshy then
|
||||
self.hp = self.hp - 10
|
||||
minetest.sound_play("automobiles_collision", {
|
||||
object = self.object,
|
||||
max_hear_distance = 5,
|
||||
gain = 1.0,
|
||||
fade = 0.0,
|
||||
pitch = 1.0,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.hp <= 0 then
|
||||
local destroy_f = automobiles_lib.destroy
|
||||
if self._destroy_function then destroy_f = self._destroy_function end
|
||||
destroy_f(self)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.get_staticdata(self)
|
||||
return minetest.serialize({
|
||||
stored_owner = self.owner,
|
||||
stored_hp = self.hp,
|
||||
stored_color = self._color,
|
||||
stored_det_color = self._det_color,
|
||||
stored_steering = self._steering_angle,
|
||||
stored_energy = self._energy,
|
||||
stored_rag = self._show_rag,
|
||||
stored_pitch = self._pitch,
|
||||
stored_light_old_pos = self._light_old_pos,
|
||||
stored_inv_id = self._inv_id,
|
||||
stored_car_type = self._car_type,
|
||||
stored_car_gravity = self._car_gravity,
|
||||
stored_scale = self._vehicle_scale or 1,
|
||||
stored_power_scale = self._vehicle_power_scale or 1,
|
||||
--race data
|
||||
stored_last_checkpoint = self._last_checkpoint,
|
||||
stored_total_laps = self._total_laps,
|
||||
stored_race_id = self._race_id,
|
||||
})
|
||||
end
|
||||
|
||||
local function scale_entity(self, scale)
|
||||
scale = scale or 1
|
||||
local initial_properties = automobiles_lib.properties_copy(self.initial_properties)
|
||||
local new_properties = automobiles_lib.properties_copy(initial_properties)
|
||||
|
||||
--[[if initial_properties.collisionbox then
|
||||
for i, value in ipairs(initial_properties.collisionbox) do
|
||||
new_properties.collisionbox[i] = value * scale
|
||||
--core.log("action", new_properties.collisionbox[i])
|
||||
end
|
||||
end
|
||||
|
||||
if initial_properties.selectionbox then
|
||||
for i, value in ipairs(initial_properties.selectionbox) do
|
||||
new_properties.selectionbox[i] = value * scale
|
||||
end
|
||||
end]]--
|
||||
|
||||
if initial_properties.stepheight then
|
||||
new_properties.stepheight = initial_properties.stepheight * scale
|
||||
end
|
||||
|
||||
new_properties.visual_size = {x=scale, y=scale}
|
||||
|
||||
self.object:set_properties(new_properties)
|
||||
end
|
||||
|
||||
function automobiles_lib.on_activate(self, staticdata, dtime_s)
|
||||
if staticdata ~= "" and staticdata ~= nil then
|
||||
local data = minetest.deserialize(staticdata) or {}
|
||||
self.owner = data.stored_owner
|
||||
self.hp = data.stored_hp
|
||||
self._color = data.stored_color
|
||||
self._det_color = data.stored_det_color
|
||||
self._steering_angle = data.stored_steering
|
||||
self._energy = data.stored_energy
|
||||
--minetest.debug("loaded: ", self.energy)
|
||||
--race data
|
||||
self._last_checkpoint = data.stored_last_checkpoint
|
||||
self._total_laps = data.stored_total_laps
|
||||
self._race_id = data.stored_race_id
|
||||
self._show_rag = data.stored_rag
|
||||
self._pitch = data.stored_pitch
|
||||
self._light_old_pos = data.stored_light_old_pos
|
||||
self._inv_id = data.stored_inv_id
|
||||
|
||||
self._car_type = data.stored_car_type
|
||||
self._car_gravity = data.stored_car_gravity or -automobiles_lib.gravity
|
||||
self._vehicle_scale = data.stored_scale or 1
|
||||
self._vehicle_power_scale = data.stored_power_scale or 1
|
||||
|
||||
automobiles_lib.setText(self, self._vehicle_name)
|
||||
if data.remove then
|
||||
automobiles_lib.destroy_inventory(self)
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
scale_entity(self, self._vehicle_scale)
|
||||
|
||||
if self._painting_load then
|
||||
self._painting_load(self, self._color)
|
||||
else
|
||||
automobiles_lib.paint(self, self._color)
|
||||
end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
local front_suspension=minetest.add_entity(self.object:get_pos(),self._front_suspension_ent)
|
||||
if front_suspension then
|
||||
front_suspension:set_attach(self.object,'',self._front_suspension_pos,{x=0,y=0,z=0})
|
||||
self.front_suspension = front_suspension
|
||||
end
|
||||
|
||||
if self._front_wheel_ent then
|
||||
local lf_wheel=minetest.add_entity(pos,self._front_wheel_ent)
|
||||
if lf_wheel then
|
||||
lf_wheel:set_attach(self.front_suspension,'',{x=-self._front_wheel_xpos,y=0,z=0},{x=0,y=0,z=0})
|
||||
-- set the animation once and later only change the speed
|
||||
lf_wheel:set_animation(self._front_wheel_frames, 0, 0, true)
|
||||
self.lf_wheel = lf_wheel
|
||||
end
|
||||
|
||||
local rf_wheel=minetest.add_entity(pos,self._front_wheel_ent)
|
||||
if rf_wheel then
|
||||
rf_wheel:set_attach(self.front_suspension,'',{x=self._front_wheel_xpos,y=0,z=0},{x=0,y=180,z=0})
|
||||
-- set the animation once and later only change the speed
|
||||
rf_wheel:set_animation(self._front_wheel_frames, 0, 0, true)
|
||||
self.rf_wheel = rf_wheel
|
||||
end
|
||||
end
|
||||
|
||||
local rear_suspension=minetest.add_entity(self.object:get_pos(),self._rear_suspension_ent)
|
||||
if rear_suspension then
|
||||
rear_suspension:set_attach(self.object,'',self._rear_suspension_pos,{x=0,y=0,z=0})
|
||||
self.rear_suspension = rear_suspension
|
||||
end
|
||||
|
||||
if self._rear_wheel_ent then
|
||||
local lr_wheel=minetest.add_entity(pos,self._rear_wheel_ent)
|
||||
if lr_wheel then
|
||||
lr_wheel:set_attach(self.rear_suspension,'',{x=-self._rear_wheel_xpos,y=0,z=0},{x=0,y=0,z=0})
|
||||
-- set the animation once and later only change the speed
|
||||
lr_wheel:set_animation(self._rear_wheel_frames, 0, 0, true)
|
||||
self.lr_wheel = lr_wheel
|
||||
end
|
||||
|
||||
local rr_wheel=minetest.add_entity(pos,self._rear_wheel_ent)
|
||||
if rr_wheel then
|
||||
rr_wheel:set_attach(self.rear_suspension,'',{x=self._rear_wheel_xpos,y=0,z=0},{x=0,y=180,z=0})
|
||||
-- set the animation once and later only change the speed
|
||||
rr_wheel:set_animation(self._rear_wheel_frames, 0, 0, true)
|
||||
self.rr_wheel = rr_wheel
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if self._steering_ent then
|
||||
local steering_axis=minetest.add_entity(pos,'automobiles_lib:pivot_mesh')
|
||||
if steering_axis then
|
||||
steering_axis:set_attach(self.object,'',self._drive_wheel_pos,{x=self._drive_wheel_angle,y=0,z=0})
|
||||
if steering_axis then
|
||||
self.steering_axis = steering_axis
|
||||
|
||||
local steering=minetest.add_entity(self.steering_axis:get_pos(), self._steering_ent)
|
||||
steering:set_attach(self.steering_axis,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.steering = steering
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self._rag_retracted_ent then
|
||||
local rag_rect=minetest.add_entity(self.object:get_pos(),self._rag_retracted_ent)
|
||||
if rag_rect then
|
||||
rag_rect:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.rag_rect = rag_rect
|
||||
self.rag_rect:set_properties({is_visible=false})
|
||||
end
|
||||
end
|
||||
|
||||
if self._rag_extended_ent then
|
||||
local rag=minetest.add_entity(self.object:get_pos(),self._rag_extended_ent)
|
||||
if rag then
|
||||
rag:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.rag = rag
|
||||
end
|
||||
end
|
||||
|
||||
automobiles_lib.seats_create(self)
|
||||
|
||||
local pointer_entity = 'automobiles_lib:pointer'
|
||||
if self._gauge_pointer_ent then pointer_entity = self._gauge_pointer_ent end
|
||||
if self._fuel_gauge_pos then
|
||||
local fuel_gauge=minetest.add_entity(pos, pointer_entity)
|
||||
if fuel_gauge then
|
||||
fuel_gauge:set_attach(self.object,'',self._fuel_gauge_pos,{x=0,y=0,z=0})
|
||||
self.fuel_gauge = fuel_gauge
|
||||
end
|
||||
end
|
||||
|
||||
if self._front_lights then
|
||||
local lights = minetest.add_entity(pos,self._front_lights)
|
||||
if lights then
|
||||
lights:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.lights = lights
|
||||
self.lights:set_properties({is_visible=true})
|
||||
end
|
||||
end
|
||||
|
||||
if self._rear_lights then
|
||||
local r_lights = minetest.add_entity(pos,self._rear_lights)
|
||||
if r_lights then
|
||||
r_lights:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.r_lights = r_lights
|
||||
self.r_lights:set_properties({is_visible=true})
|
||||
end
|
||||
end
|
||||
|
||||
if self._reverse_lights then
|
||||
local reverse_lights = minetest.add_entity(pos,self._reverse_lights)
|
||||
if reverse_lights then
|
||||
reverse_lights:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.reverse_lights = reverse_lights
|
||||
self.reverse_lights:set_properties({is_visible=true})
|
||||
end
|
||||
end
|
||||
|
||||
if self._turn_left_lights then
|
||||
local turn_l_light = minetest.add_entity(pos,self._turn_left_lights)
|
||||
if turn_l_light then
|
||||
turn_l_light:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.turn_l_light = turn_l_light
|
||||
self.turn_l_light:set_properties({is_visible=true})
|
||||
end
|
||||
end
|
||||
|
||||
if self._turn_right_lights then
|
||||
local turn_r_light = minetest.add_entity(pos,self._turn_right_lights)
|
||||
if turn_r_light then
|
||||
turn_r_light:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.turn_r_light = turn_r_light
|
||||
self.turn_r_light:set_properties({is_visible=true})
|
||||
end
|
||||
end
|
||||
|
||||
if self._extra_items_function then
|
||||
self._extra_items_function(self)
|
||||
end
|
||||
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
|
||||
local inv = minetest.get_inventory({type = "detached", name = self._inv_id})
|
||||
-- if the game was closed the inventories have to be made anew, instead of just reattached
|
||||
if not inv then
|
||||
automobiles_lib.create_inventory(self, self._trunk_slots)
|
||||
else
|
||||
self.inv = inv
|
||||
end
|
||||
|
||||
automobiles_lib.actfunc(self, staticdata, dtime_s)
|
||||
end
|
||||
|
||||
function checkIsFinite(mynumber)
|
||||
if math.isfinite(mynumber) then
|
||||
return mynumber
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function automobiles_lib.on_step(self, dtime)
|
||||
automobiles_lib.stepfunc(self, dtime)
|
||||
|
||||
--[[sound play control]]--
|
||||
self._last_time_collision_snd = self._last_time_collision_snd + dtime
|
||||
if self._last_time_collision_snd > 1 then self._last_time_collision_snd = 1 end
|
||||
self._last_time_drift_snd = self._last_time_drift_snd + dtime
|
||||
if self._last_time_drift_snd > 2.0 then self._last_time_drift_snd = 2.0 end
|
||||
--[[end sound control]]--
|
||||
|
||||
--in case it's not declared
|
||||
self._max_acc_factor = self._max_acc_factor or 1
|
||||
self._vehicle_scale = self._vehicle_scale or 1
|
||||
self._vehicle_power_scale = self._vehicle_power_scale or self._vehicle_scale
|
||||
|
||||
local rotation = self.object:get_rotation()
|
||||
local yaw = rotation.y
|
||||
local newyaw=yaw
|
||||
local pitch = rotation.x
|
||||
|
||||
local hull_direction = minetest.yaw_to_dir(yaw)
|
||||
local nhdir = {x=hull_direction.z,y=0,z=-hull_direction.x} -- lateral unit vector
|
||||
local velocity = self.object:get_velocity()
|
||||
|
||||
local longit_speed = automobiles_lib.dot(velocity,hull_direction)
|
||||
local fuel_weight_factor = (5 - self._energy)/5000
|
||||
local longit_drag = vector.multiply(hull_direction,(longit_speed*longit_speed) *
|
||||
(self._LONGIT_DRAG_FACTOR - fuel_weight_factor) * -1 * automobiles_lib.sign(longit_speed))
|
||||
|
||||
local later_speed = automobiles_lib.dot(velocity,nhdir)
|
||||
local dynamic_later_drag = self._LATER_DRAG_FACTOR
|
||||
if longit_speed > 2 then dynamic_later_drag = 2.0 end
|
||||
if longit_speed > 8 then dynamic_later_drag = 0.5 end
|
||||
--core.chat_send_all(dump(longit_speed))
|
||||
|
||||
if automobiles_lib.extra_drift and longit_speed > (4*self._vehicle_power_scale) then
|
||||
dynamic_later_drag = dynamic_later_drag/(longit_speed*2)
|
||||
end
|
||||
|
||||
local later_drag = vector.new()
|
||||
if self._is_motorcycle == true then
|
||||
later_drag = vector.multiply(nhdir,later_speed*
|
||||
later_speed*self._LATER_DRAG_FACTOR*-1*automobiles_lib.sign(later_speed))
|
||||
else
|
||||
later_drag = vector.multiply(nhdir,later_speed*
|
||||
later_speed*dynamic_later_drag*-1*automobiles_lib.sign(later_speed))
|
||||
end
|
||||
|
||||
local accel = vector.add(longit_drag,later_drag)
|
||||
local stop = nil
|
||||
local curr_pos = self.object:get_pos()
|
||||
|
||||
if self._show_rag == true then
|
||||
if self._windshield_pos and self._windshield_ext_rotation then
|
||||
self.object:set_bone_position("windshield", self._windshield_pos, self._windshield_ext_rotation) --extended
|
||||
end
|
||||
if self.rag_rect then self.rag_rect:set_properties({is_visible=true}) end
|
||||
if self.rag then self.rag:set_properties({is_visible=false}) end
|
||||
else
|
||||
if self._windshield_pos then
|
||||
self.object:set_bone_position("windshield", self._windshield_pos, {x=0, y=0, z=0}) --retracted
|
||||
end
|
||||
if self.rag_rect then self.rag_rect:set_properties({is_visible=false}) end
|
||||
if self.rag then self.rag:set_properties({is_visible=true}) end
|
||||
end
|
||||
|
||||
local player = nil
|
||||
local is_attached = false
|
||||
if self.driver_name then
|
||||
player = minetest.get_player_by_name(self.driver_name)
|
||||
|
||||
if player then
|
||||
local player_attach = player:get_attach()
|
||||
if player_attach then
|
||||
if self.driver_seat then
|
||||
if player_attach == self.driver_seat or player_attach == self.object then is_attached = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--for flying
|
||||
if self._setmode then
|
||||
self._setmode(self, is_attached, curr_pos, velocity, player, dtime)
|
||||
end
|
||||
|
||||
local is_braking = false
|
||||
if is_attached then
|
||||
local ctrl = player:get_player_control()
|
||||
if ctrl.jump then
|
||||
dynamic_later_drag = 0.2
|
||||
end
|
||||
if ctrl.aux1 then
|
||||
--sets the engine running - but sets a delay also, cause keypress
|
||||
if self._last_time_command > 0.8 then
|
||||
self._last_time_command = 0
|
||||
local horn_sound = "automobiles_horn"
|
||||
if self._horn_sound then horn_sound = self._horn_sound end
|
||||
minetest.sound_play({name = horn_sound},
|
||||
{object = self.object, gain = 0.6, pitch = 1.0, max_hear_distance = 32, loop = false,})
|
||||
end
|
||||
end
|
||||
if ctrl.down then
|
||||
is_braking = true
|
||||
if self.r_lights then self.r_lights:set_properties({textures={"automobiles_rear_lights_full.png"}, glow=15}) end
|
||||
end
|
||||
if self.reverse_lights then
|
||||
if ctrl.sneak then
|
||||
self.reverse_lights:set_properties({textures={"automobiles_white.png"}, glow=15})
|
||||
else
|
||||
self.reverse_lights:set_properties({textures={"automobiles_grey.png"}, glow=0})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self._last_light_move = self._last_light_move + dtime
|
||||
if self._last_light_move > 0.15 then
|
||||
self._last_light_move = 0
|
||||
if self.lights then
|
||||
if self._show_lights == true then
|
||||
--self.lights:set_properties({is_visible=true})
|
||||
self.lights:set_properties({textures={"automobiles_front_lights.png"}, glow=15})
|
||||
if is_braking == false then
|
||||
if self.r_lights then self.r_lights:set_properties({textures={"automobiles_rear_lights.png"}, glow=10}) end
|
||||
end
|
||||
automobiles_lib.put_light(self)
|
||||
else
|
||||
--self.lights:set_properties({is_visible=false})
|
||||
self.lights:set_properties({textures={"automobiles_grey.png"}, glow=0})
|
||||
if is_braking == false then
|
||||
if self.r_lights then self.r_lights:set_properties({textures={"automobiles_rear_lights_off.png"}, glow=0}) end
|
||||
end
|
||||
automobiles_lib.remove_light(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- impacts and control
|
||||
if is_attached then --and self.driver_name == self.owner then
|
||||
local impact = automobiles_lib.get_hipotenuse_value(velocity, self.lastvelocity)
|
||||
if impact > 1 then
|
||||
--self.damage = self.damage + impact --sum the impact value directly to damage meter
|
||||
if self._last_time_collision_snd > 0.3 then
|
||||
self._last_time_collision_snd = 0
|
||||
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,
|
||||
})
|
||||
end
|
||||
--[[if self.damage > 100 then --if acumulated damage is greater than 100, adieu
|
||||
automobiles_lib.destroy(self)
|
||||
end]]--
|
||||
end
|
||||
|
||||
--control
|
||||
local steering_angle_max = 40
|
||||
local steering_speed = 40
|
||||
if math.abs(longit_speed) > 3*self._vehicle_scale then
|
||||
local mid_speed = (steering_speed/2)
|
||||
steering_speed = (mid_speed + (mid_speed / math.abs(longit_speed*0.25))*self._vehicle_scale)
|
||||
end
|
||||
|
||||
--adjust engine parameter (transmission emulation)
|
||||
local acc_factor = self._max_acc_factor
|
||||
local transmission_state = automobiles_lib.get_transmission_state(self, longit_speed, self._max_speed)
|
||||
|
||||
local target_acc_factor = acc_factor
|
||||
|
||||
if self._have_transmission ~= false then
|
||||
if transmission_state == 1 then
|
||||
target_acc_factor = (acc_factor/3)
|
||||
end
|
||||
if transmission_state == 2 then
|
||||
target_acc_factor = (acc_factor/2)
|
||||
end
|
||||
self._transmission_state = transmission_state
|
||||
end
|
||||
--core.chat_send_all(transmission_state)
|
||||
|
||||
--control
|
||||
local control = automobiles_lib.control
|
||||
if self._control_function then
|
||||
control = self._control_function
|
||||
end
|
||||
accel, stop = control(self, dtime, hull_direction, longit_speed, longit_drag, later_drag, accel, target_acc_factor, self._max_speed, steering_angle_max, steering_speed)
|
||||
--accel = vector.multiply(accel, self._vehicle_power_scale)
|
||||
else
|
||||
self._show_lights = false
|
||||
if self.sound_handle ~= nil then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
end
|
||||
end
|
||||
|
||||
local angle_factor = self._steering_angle / 10
|
||||
|
||||
--whell turn
|
||||
if self.lf_wheel and self.rf_wheel and self.lr_wheel and self.rr_wheel and self._is_flying ~= 1 then
|
||||
self.lf_wheel:set_attach(self.front_suspension,'',{x=-self._front_wheel_xpos,y=0,z=0},{x=0,y=-self._steering_angle-angle_factor,z=0})
|
||||
self.rf_wheel:set_attach(self.front_suspension,'',{x=self._front_wheel_xpos,y=0,z=0},{x=0,y=(-self._steering_angle+angle_factor)+180,z=0})
|
||||
self.lr_wheel:set_attach(self.rear_suspension,'',{x=-self._rear_wheel_xpos,y=0,z=0},{x=0,y=0,z=0})
|
||||
self.rr_wheel:set_attach(self.rear_suspension,'',{x=self._rear_wheel_xpos,y=0,z=0},{x=0,y=180,z=0})
|
||||
end
|
||||
|
||||
--check if the tyres is touching the pavement
|
||||
local noded = automobiles_lib.nodeatpos(automobiles_lib.pos_shift(curr_pos,{y=self.initial_properties.collisionbox[2]-0.5}))
|
||||
if (noded and noded.drawtype ~= 'airlike') then
|
||||
if noded.drawtype ~= 'liquid' then
|
||||
local wheel_compensation = longit_speed * (self._wheel_compensation or 1)
|
||||
if self.lf_wheel then self.lf_wheel:set_animation_frame_speed( wheel_compensation * (12 - angle_factor)) end
|
||||
if self.rf_wheel then self.rf_wheel:set_animation_frame_speed(-wheel_compensation * (12 + angle_factor)) end
|
||||
if self.lr_wheel then self.lr_wheel:set_animation_frame_speed( wheel_compensation * (12 - angle_factor)) end
|
||||
if self.rr_wheel then self.rr_wheel:set_animation_frame_speed(-wheel_compensation * (12 + angle_factor)) end
|
||||
end
|
||||
else
|
||||
--is flying
|
||||
if self.lf_wheel then self.lf_wheel:set_animation_frame_speed(0) end
|
||||
if self.rf_wheel then self.rf_wheel:set_animation_frame_speed(0) end
|
||||
if self.lr_wheel then self.lr_wheel:set_animation_frame_speed(0) end
|
||||
if self.rr_wheel then self.rr_wheel:set_animation_frame_speed(0) end
|
||||
end
|
||||
|
||||
--drive wheel turn
|
||||
if self._steering_ent then
|
||||
self.steering:set_attach(self.steering_axis,'',{x=0,y=0,z=0},{x=0,y=0,z=self._steering_angle*2})
|
||||
else
|
||||
self.object:set_bone_position("drive_wheel", {x=-0, y=0, z=0}, {x=0, y=0, z=-self._steering_angle*2})
|
||||
end
|
||||
|
||||
|
||||
if math.abs(self._steering_angle)>5 then
|
||||
local turn_rate = math.rad(40)
|
||||
newyaw = yaw + dtime*(1 - 1 / (math.abs(longit_speed) + 1)) *
|
||||
self._steering_angle / 30 * turn_rate * automobiles_lib.sign(longit_speed)
|
||||
end
|
||||
|
||||
--turn light
|
||||
if self.turn_l_light and self.turn_r_light then
|
||||
self._turn_light_timer = self._turn_light_timer + dtime
|
||||
if math.abs(self._steering_angle) > 15 and self._turn_light_timer >= 1 then
|
||||
self._turn_light_timer = 0
|
||||
--set turn light
|
||||
--minetest.chat_send_all(self._steering_angle)
|
||||
local textures_l = self._textures_turn_lights_on or {"automobiles_rear_lights_full.png"}
|
||||
if self._steering_angle < 0 then
|
||||
--minetest.chat_send_all("direita")
|
||||
self.turn_r_light:set_properties({textures=textures_l, glow=20})
|
||||
end
|
||||
if self._steering_angle > 0 then
|
||||
--minetest.chat_send_all("esquerda")
|
||||
self.turn_l_light:set_properties({textures=textures_l, glow=20})
|
||||
end
|
||||
end
|
||||
if self._turn_light_timer > 0.5 then
|
||||
local textures_l = self._textures_turn_lights_off or {"automobiles_rear_lights_off.png"}
|
||||
self.turn_l_light:set_properties({textures=textures_l, glow=0})
|
||||
self.turn_r_light:set_properties({textures=textures_l, glow=0})
|
||||
end
|
||||
if self._turn_light_timer > 1 then
|
||||
self._turn_light_timer = 1
|
||||
end
|
||||
end
|
||||
|
||||
--if it's not NaN, add accel
|
||||
accel.x = checkIsFinite(accel.x)
|
||||
accel.y = checkIsFinite(accel.y)
|
||||
accel.z = checkIsFinite(accel.z)
|
||||
|
||||
--[[
|
||||
accell correction
|
||||
under some circunstances the acceleration exceeds the max value accepted by set_acceleration and
|
||||
the game crashes with an overflow, so limiting the max acceleration in each axis prevents the crash
|
||||
]]--
|
||||
local max_factor = 25
|
||||
local acc_adjusted = 0
|
||||
if accel.x > max_factor then accel.x = acc_adjusted end
|
||||
if accel.x < -max_factor then accel.x = -acc_adjusted end
|
||||
if accel.z > max_factor then accel.z = acc_adjusted end
|
||||
if accel.z < -max_factor then accel.z = -acc_adjusted end
|
||||
-- end correction
|
||||
|
||||
-- calculate energy consumption --
|
||||
----------------------------------
|
||||
if self._energy > 0 then
|
||||
local zero_reference = vector.new()
|
||||
local acceleration = automobiles_lib.get_hipotenuse_value(accel, zero_reference)
|
||||
--minetest.chat_send_all(acceleration)
|
||||
if automobiles_lib.is_drift_game == false then
|
||||
local consumption = 40000
|
||||
if self._consumption_divisor then
|
||||
consumption = self._consumption_divisor
|
||||
end
|
||||
local consumed_power = acceleration/consumption
|
||||
self._energy = self._energy - consumed_power;
|
||||
else
|
||||
self._energy = 5
|
||||
end
|
||||
end
|
||||
if self._energy <= 0 then
|
||||
self._engine_running = false
|
||||
self._is_flying = 0
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
minetest.chat_send_player(self.driver_name, "Out of fuel")
|
||||
end
|
||||
else
|
||||
self._last_engine_sound_update = self._last_engine_sound_update + dtime
|
||||
if self._last_engine_sound_update > 0.300 then
|
||||
self._last_engine_sound_update = 0
|
||||
automobiles_lib.engine_set_sound_and_animation(self, longit_speed)
|
||||
end
|
||||
end
|
||||
|
||||
local energy_indicator_angle = automobiles_lib.get_gauge_angle(self._energy)
|
||||
if self.fuel_gauge then
|
||||
self.fuel_gauge:set_attach(self.object,'',self._fuel_gauge_pos,{x=0,y=0,z=energy_indicator_angle})
|
||||
end
|
||||
----------------------------
|
||||
-- end energy consumption --
|
||||
|
||||
--gravity works
|
||||
if not self._is_flying or self._is_flying == 0 then
|
||||
accel.y = -automobiles_lib.gravity
|
||||
else
|
||||
local time_correction = (self.dtime/automobiles_lib.ideal_step)
|
||||
local y_accel = self._car_gravity*time_correction
|
||||
accel.y = y_accel --sets the anti gravity
|
||||
end
|
||||
|
||||
if stop == true then
|
||||
self._last_accel = vector.new() --self.object:get_acceleration()
|
||||
self.object:set_acceleration({x=0,y=0,z=0})
|
||||
self.object:set_velocity({x=0,y=0,z=0})
|
||||
else
|
||||
self.object:move_to(curr_pos)
|
||||
--automobiles_lib.set_acceleration(self.object, new_accel)
|
||||
local limit = (self._max_speed/self.dtime)
|
||||
if accel.y > limit then accel.y = limit end --it isn't a rocket :/
|
||||
self._last_accel = accel
|
||||
end
|
||||
|
||||
self._last_ground_check = self._last_ground_check + dtime
|
||||
if self._last_ground_check > 0.18 then
|
||||
self._last_ground_check = 0
|
||||
automobiles_lib.ground_get_distances(self, 0.372*self._vehicle_scale, (self._front_suspension_pos.z*self._vehicle_scale)/10)
|
||||
end
|
||||
local newpitch = self._pitch --velocity.y * math.rad(6)
|
||||
|
||||
local newroll = self._roll or 0
|
||||
if self._is_flying == 1 then
|
||||
newpitch = 0
|
||||
local turn_effect_speed = longit_speed
|
||||
if turn_effect_speed > 10 then turn_effect_speed = 10 end
|
||||
newroll = (-self._steering_angle/100)*(turn_effect_speed/10)
|
||||
if math.abs(self._steering_angle) < 1 then newroll = 0 end
|
||||
|
||||
--pitch
|
||||
local max_pitch = 6
|
||||
local h_vel_compensation = (((longit_speed * 2) * 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))
|
||||
else
|
||||
local turn_effect_speed = longit_speed
|
||||
if self._is_motorcycle then
|
||||
if turn_effect_speed > 20 then turn_effect_speed = 20 end
|
||||
newroll = (-self._steering_angle/100)*(turn_effect_speed/10)
|
||||
|
||||
if is_attached == false and stop == true then
|
||||
newroll = self._stopped_roll
|
||||
end
|
||||
else
|
||||
if turn_effect_speed > 10 then turn_effect_speed = 10 end
|
||||
if math.abs(longit_speed) > 0 then
|
||||
--local tilt_effect = (-self._steering_angle/100)*(turn_effect_speed/30)
|
||||
local max_tilt = 10
|
||||
local tilt_effect = (-later_speed/12)*(turn_effect_speed/30)
|
||||
local tire_burn = false
|
||||
if tilt_effect > max_tilt then
|
||||
tilt_effect = max_tilt
|
||||
end
|
||||
if tilt_effect < -max_tilt then
|
||||
tilt_effect = -max_tilt
|
||||
end
|
||||
newroll = tilt_effect * -1
|
||||
self.front_suspension:set_rotation({x=0,y=0,z=tilt_effect})
|
||||
self.rear_suspension:set_rotation({x=0,y=0,z=tilt_effect})
|
||||
newroll = newroll + (self._roll or 0)
|
||||
|
||||
|
||||
if (noded and noded.drawtype ~= 'airlike') then
|
||||
if noded.drawtype ~= 'liquid' then
|
||||
local min_later_speed = self._min_later_speed or 3
|
||||
local speed_for_smoke = min_later_speed / 2
|
||||
if (later_speed > speed_for_smoke or later_speed < -speed_for_smoke) and not self._is_motorcycle then
|
||||
automobiles_lib.add_smoke(self, curr_pos, yaw, self._rear_wheel_xpos*self._vehicle_scale)
|
||||
if automobiles_lib.extra_drift == false then --disables the sound when playing drift game.. it's annoying
|
||||
if self._last_time_drift_snd >= 2.0 and (later_speed > min_later_speed or later_speed < -min_later_speed) then
|
||||
self._last_time_drift_snd = 0
|
||||
minetest.sound_play("automobiles_drifting", {
|
||||
pos = curr_pos,
|
||||
max_hear_distance = 20,
|
||||
gain = 3.0,
|
||||
fade = 0.0,
|
||||
pitch = 1.0,
|
||||
ephemeral = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
if self.front_suspension then self.front_suspension:set_rotation({x=0,y=0,z=0}) end
|
||||
if self.rear_suspension then self.rear_suspension:set_rotation({x=0,y=0,z=0}) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.object:set_rotation({x=newpitch,y=newyaw,z=newroll})
|
||||
|
||||
self._longit_speed = longit_speed
|
||||
end
|
||||
@@ -0,0 +1,93 @@
|
||||
local S = automobiles_lib.S
|
||||
|
||||
function automobiles_lib.getCarFromPlayer(player, self)
|
||||
local seat = player:get_attach()
|
||||
if seat then
|
||||
local car = seat:get_attach()
|
||||
if car then
|
||||
return car
|
||||
else
|
||||
return seat
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function automobiles_lib.driver_formspec(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
local vehicle_obj = automobiles_lib.getCarFromPlayer(player)
|
||||
if vehicle_obj == nil then
|
||||
return
|
||||
end
|
||||
local ent = vehicle_obj:get_luaentity()
|
||||
|
||||
if ent then
|
||||
local yaw = "false"
|
||||
local one_hand = "false"
|
||||
if ent._yaw_by_mouse then yaw = "true" end
|
||||
if ent._one_hand then one_hand = "true" end
|
||||
|
||||
local basic_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[6,7]",
|
||||
}, "")
|
||||
|
||||
basic_form = basic_form.."button[1,1.0;4,1;go_out;" .. S("Go Offboard") .. "]"
|
||||
basic_form = basic_form.."button[1,2.5;4,1;lights;" .. S("Lights") .. "]"
|
||||
basic_form = basic_form.."checkbox[1,5.5;yaw;" .. S("Direction by mouse") .. ";"..yaw.."]"
|
||||
basic_form = basic_form.."checkbox[1,6.2;one_hand;" .. S("One Hand Driving\n(open menu using AUX)") .. ";"..one_hand.."]"
|
||||
|
||||
minetest.show_formspec(name, "automobiles_lib:driver_main", basic_form)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "automobiles_lib:driver_main" then
|
||||
local name = player:get_player_name()
|
||||
local car_obj = automobiles_lib.getCarFromPlayer(player)
|
||||
if car_obj then
|
||||
local ent = car_obj:get_luaentity()
|
||||
if ent then
|
||||
if fields.go_out then
|
||||
if ent._passenger then --any pax?
|
||||
local pax_obj = minetest.get_player_by_name(ent._passenger)
|
||||
|
||||
local dettach_pax_f = automobiles_lib.dettach_pax
|
||||
if ent._dettach_pax then dettach_pax_f = ent._dettach_pax end
|
||||
dettach_pax_f(ent, pax_obj)
|
||||
end
|
||||
ent._is_flying = 0
|
||||
|
||||
local dettach_f = automobiles_lib.dettach_driver
|
||||
if ent._dettach then dettach_f = ent._dettach end
|
||||
dettach_f(ent, player)
|
||||
end
|
||||
if fields.lights then
|
||||
if ent._show_lights == true then
|
||||
ent._show_lights = false
|
||||
else
|
||||
ent._show_lights = true
|
||||
end
|
||||
end
|
||||
if fields.yaw then
|
||||
if ent._yaw_by_mouse == true then
|
||||
ent._yaw_by_mouse = false
|
||||
else
|
||||
ent._yaw_by_mouse = true
|
||||
end
|
||||
end
|
||||
if fields.one_hand then
|
||||
if ent._one_hand == true then
|
||||
ent._one_hand = false
|
||||
else
|
||||
ent._one_hand = true
|
||||
core.chat_send_player(name,core.colorize('#00ff00', S(" >>> Use AUX or key \"E\" to open the menu")))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.close_formspec(name, "automobiles_lib:driver_main")
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,44 @@
|
||||
--
|
||||
-- fuel
|
||||
--
|
||||
|
||||
function automobiles_lib.contains(table, val)
|
||||
for k,v in pairs(table) do
|
||||
if k == val then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function automobiles_lib.loadFuel(self, player_name, free, max_fuel)
|
||||
free = free or false
|
||||
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 = automobiles_lib.contains(automobiles_lib.fuel, item_name)
|
||||
--minetest.chat_send_all(dump(fuel))
|
||||
if fuel or free == true then
|
||||
local stack = ItemStack(item_name .. " 1")
|
||||
if self._energy < max_fuel then
|
||||
itmstck:set_count(1)
|
||||
|
||||
if free == false then inv:remove_item("main", itmstck) end
|
||||
if fuel then
|
||||
self._energy = self._energy + fuel
|
||||
end
|
||||
if self._energy > max_fuel then self._energy = max_fuel end
|
||||
automobiles_lib.last_fuel_display = 0
|
||||
if self._energy == max_fuel then minetest.chat_send_player(player_name, "Full tank!") end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
function automobiles_lib.putAngleOnRange(angle)
|
||||
local n_angle = angle/360
|
||||
n_angle = (n_angle - math.floor(n_angle))*360
|
||||
if n_angle < -180 then n_angle = n_angle + 360 end
|
||||
if n_angle > 180 then n_angle = n_angle - 360 end
|
||||
return n_angle
|
||||
end
|
||||
|
||||
function automobiles_lib.pid_controller(current_value, setpoint, last_error, delta_t, kp, ki, kd)
|
||||
kp = kp or 0
|
||||
ki = ki or 0.00000000000001
|
||||
kd = kd or 0.05
|
||||
delta_t = delta_t or 0.100;
|
||||
|
||||
ti = kp/ki
|
||||
td = kd/kp
|
||||
|
||||
local _error = setpoint - current_value
|
||||
local derivative = _error - last_error
|
||||
--local output = kpv*erro + (kpv/Tiv)*I + kpv*Tdv*((erro - erro_passado)/delta_t);
|
||||
if integrative == nil then integrative = 0 end
|
||||
integrative = integrative + (((_error + last_error)/delta_t)/2);
|
||||
local output = kp*_error + (kp/ti)*integrative + kp * td*((_error - last_error)/delta_t)
|
||||
last_error = _error
|
||||
return output, last_error
|
||||
|
||||
end
|
||||
|
||||
-- Função para calcular ângulos de pitch e roll
|
||||
local function get_vehicle_pitch_roll(pos, wheels, yaw)
|
||||
local world = vector.new()
|
||||
--core.chat_send_all(dump(wheels))
|
||||
|
||||
--rotacionando as posições das rodas
|
||||
for i = 1,4,1 do
|
||||
local orig_x = wheels[i].x
|
||||
local orig_z = wheels[i].z
|
||||
wheels[i].x = (orig_x * math.cos(yaw)) - (orig_z * math.sin(yaw))
|
||||
wheels[i].z = (orig_x * math.sin(yaw)) + (orig_z * math.cos(yaw))
|
||||
end
|
||||
--core.chat_send_all(dump(wheels))
|
||||
|
||||
local points = {}
|
||||
|
||||
-- Obter altura do terreno para cada roda
|
||||
for i, wheel in ipairs(wheels) do
|
||||
world.x = pos.x + wheel.x
|
||||
world.y = pos.y + wheel.y
|
||||
world.z = pos.z + wheel.z
|
||||
local height = automobiles_lib.get_obstacle({x=world.x,y=world.y,z=world.z}).y or 0
|
||||
table.insert(points, {x = world.x, y = height, z = world.z})
|
||||
end
|
||||
|
||||
-- Calcular os coeficientes do plano Ax + By + Cz + D = 0
|
||||
local x1, y1, z1 = points[1].x, points[1].y, points[1].z
|
||||
local x2, y2, z2 = points[2].x, points[2].y, points[2].z
|
||||
local x3, y3, z3 = points[3].x, points[3].y, points[3].z
|
||||
local x4, y4, z4 = points[4].x, points[4].y, points[4].z
|
||||
|
||||
-- Criamos dois vetores a partir das quatro rodas
|
||||
local v1 = {x = x2 - x1, y = y2 - y1, z = z2 - z1}
|
||||
local v2 = {x = x3 - x1, y = y3 - y1, z = z3 - z1}
|
||||
local v3 = {x = x4 - x1, y = y4 - y1, z = z4 - z1}
|
||||
|
||||
-- Tiramos a média do produto vetorial entre os três vetores
|
||||
local normal1 = {
|
||||
x = v1.y * v2.z - v1.z * v2.y,
|
||||
y = v1.z * v2.x - v1.x * v2.z,
|
||||
z = v1.x * v2.y - v1.y * v2.x
|
||||
}
|
||||
|
||||
local normal2 = {
|
||||
x = v1.y * v3.z - v1.z * v3.y,
|
||||
y = v1.z * v3.x - v1.x * v3.z,
|
||||
z = v1.x * v3.y - v1.y * v3.x
|
||||
}
|
||||
|
||||
-- Média das duas normais para melhor precisão
|
||||
local normal = {
|
||||
x = (normal1.x + normal2.x) / 2,
|
||||
y = (normal1.y + normal2.y) / 2,
|
||||
z = (normal1.z + normal2.z) / 2
|
||||
}
|
||||
|
||||
-- Normalizar o vetor normal
|
||||
local magnitude = math.sqrt(normal.x^2 + normal.y^2 + normal.z^2)
|
||||
if magnitude == 0 then
|
||||
return 0, 0 -- Retorna ângulos neutros se o cálculo falhar
|
||||
end
|
||||
|
||||
normal.x = normal.x / magnitude
|
||||
normal.y = normal.y / magnitude
|
||||
normal.z = normal.z / magnitude
|
||||
--core.chat_send_all("normal2 "..dump(normal).."\nmagnitude: "..magnitude)
|
||||
|
||||
-- Criar um vetor de direção do veículo (da traseira para a frente)
|
||||
local forward = {
|
||||
x = (wheels[1].x + wheels[2].x) / 2 - (wheels[3].x + wheels[4].x) / 2,
|
||||
z = (wheels[1].z + wheels[2].z) / 2 - (wheels[3].z + wheels[4].z) / 2
|
||||
}
|
||||
|
||||
-- Normalizar o vetor de direção
|
||||
local magnitude_forward = math.sqrt(forward.x^2 + forward.z^2)
|
||||
forward.x = forward.x / magnitude_forward
|
||||
forward.z = forward.z / magnitude_forward
|
||||
|
||||
-- Descobrir se o veículo está apontando para frente ou para trás no eixo Z
|
||||
local direction_z = forward.z >= 0 and -1 or 1
|
||||
-- Descobrir se o veículo está apontando para direita/esquerda no eixo X
|
||||
local direction_x = forward.x >= 0 and -1 or 1
|
||||
|
||||
-- Verificar qual eixo está "apontado para frente"
|
||||
local dominantX = math.abs(forward.x) > math.abs(forward.z)
|
||||
|
||||
local pitch, roll
|
||||
local m, n
|
||||
|
||||
if dominantX then
|
||||
-- Calcular ângulos de pitch e roll
|
||||
m = normal.x/normal.y
|
||||
pitch = math.atan(m) * direction_x
|
||||
if pitch ~= pitch then pitch = 0 end
|
||||
n = normal.z/normal.y
|
||||
roll = math.atan(n) * direction_x
|
||||
if roll ~= roll then roll = 0 end
|
||||
else
|
||||
-- Calcular ângulos de pitch e roll
|
||||
m = normal.z/normal.y
|
||||
pitch = math.atan(m) * direction_z
|
||||
if pitch ~= pitch then pitch = 0 end
|
||||
n = normal.x/normal.y
|
||||
roll = math.atan(n) * direction_z * -1
|
||||
if roll ~= roll then roll = 0 end
|
||||
end
|
||||
|
||||
--core.chat_send_all(dump("pitch "..math.deg(pitch).." - roll "..math.deg(roll)))
|
||||
|
||||
return pitch, roll
|
||||
end
|
||||
|
||||
local function get_nodedef_field(nodename, fieldname)
|
||||
if not minetest.registered_nodes[nodename] then
|
||||
return nil
|
||||
end
|
||||
return minetest.registered_nodes[nodename][fieldname]
|
||||
end
|
||||
|
||||
--lets assume that the rear axis is at object center, so we will use the distance only for front wheels
|
||||
function automobiles_lib.ground_get_distances(self, radius, axis_distance)
|
||||
--local mid_axis = (axis_length / 2)/10
|
||||
local hip = axis_distance
|
||||
--minetest.chat_send_all("entre-eixo "..hip)
|
||||
local pitch = self._pitch --+90 for the calculations
|
||||
|
||||
local yaw = self.object:get_yaw()
|
||||
local deg_yaw = math.deg(yaw)
|
||||
local yaw_turns = math.floor(deg_yaw / 360)
|
||||
deg_yaw = deg_yaw - (yaw_turns * 360)
|
||||
yaw = math.rad(deg_yaw)
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
local front_wheel_x = self._front_wheel_xpos / 10
|
||||
if math.abs(front_wheel_x) < 0.01 then front_wheel_x = 0.01 end --pra não anular as normals
|
||||
local rear_wheel_x = self._rear_wheel_xpos / 10
|
||||
if math.abs(rear_wheel_x) < 0.01 then rear_wheel_x = 0.01 end --pra não anular as normals
|
||||
|
||||
local wheels = {
|
||||
{x = -front_wheel_x, y = 0, z = axis_distance}, -- Roda frontal esquerda
|
||||
{x = front_wheel_x, y = 0, z = axis_distance}, -- Roda frontal direita
|
||||
{x = -rear_wheel_x, y = 0, z = -0}, -- Roda traseira esquerda
|
||||
{x = rear_wheel_x, y = 0, z = -0} -- Roda traseira direita
|
||||
}
|
||||
|
||||
--retornando pitch e roll
|
||||
local pitch, roll = get_vehicle_pitch_roll(pos, wheels, yaw)
|
||||
|
||||
if not self._last_pitch then self._last_pitch = pitch end
|
||||
|
||||
--[[
|
||||
* `minetest.raycast(pos1, pos2, objects, liquids)`: returns `Raycast`
|
||||
* Creates a `Raycast` object.
|
||||
* `pos1`: start of the ray
|
||||
* `pos2`: end of the ray
|
||||
* `objects`: if false, only nodes will be returned. Default is `true`.
|
||||
* `liquids`: if false, liquid nodes won't be returned. Default is `false`.
|
||||
]]--
|
||||
|
||||
|
||||
if 1 then
|
||||
local pos_y_elevation = 1.6
|
||||
local pos_elevation = (math.tan(pitch)*axis_distance) + pos_y_elevation
|
||||
--core.chat_send_all(dump(pos_elevation))
|
||||
local f_x, f_z = automobiles_lib.get_xz_from_hipotenuse(pos.x, pos.z, yaw, axis_distance)
|
||||
local f_y = pos.y + pos_elevation
|
||||
local end_pos = {x=f_x, y=f_y, z=f_z}
|
||||
local initial_pos = vector.new(pos)
|
||||
initial_pos.y = initial_pos.y + pos_elevation
|
||||
|
||||
local cast = minetest.raycast(initial_pos, end_pos, true, false)
|
||||
local thing = cast:next()
|
||||
while thing do
|
||||
if thing.type == "node" then
|
||||
local pos = thing.intersection_point
|
||||
if pos then
|
||||
local nodename = minetest.get_node(thing.under).name
|
||||
local drawtype = get_nodedef_field(nodename, "drawtype")
|
||||
|
||||
if drawtype ~= "plantlike" then
|
||||
self.object:set_acceleration({x=0,y=0,z=0})
|
||||
local oldvel = self.object:get_velocity()
|
||||
self.object:add_velocity(vector.subtract(vector.new(), oldvel))
|
||||
pitch = math.rad(0)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
thing = cast:next()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self._pitch = pitch -- math.rad(90)
|
||||
self._roll = roll -- math.rad(90)
|
||||
--core.chat_send_all(dump("pitch "..pitch.." - roll "..roll))
|
||||
|
||||
end
|
||||
|
||||
function automobiles_lib.get_obstacle(ref_pos, ammount)
|
||||
ammount = ammount or -3
|
||||
--lets clone the table
|
||||
local retval = {x=ref_pos.x, y=ref_pos.y, z=ref_pos.z}
|
||||
--minetest.chat_send_all("aa y: " .. dump(retval.y))
|
||||
local i_pos = {x=ref_pos.x, y=ref_pos.y + 1, z=ref_pos.z}
|
||||
--minetest.chat_send_all("bb y: " .. dump(i_pos.y))
|
||||
|
||||
local y = automobiles_lib.eval_interception(i_pos, {x=i_pos.x, y=ref_pos.y + ammount, z=i_pos.z})
|
||||
if y then
|
||||
retval.y = y
|
||||
end
|
||||
|
||||
--minetest.chat_send_all("y: " .. dump(ref_pos.y) .. " ye: ".. dump(retval.y))
|
||||
return retval
|
||||
end
|
||||
|
||||
function automobiles_lib.eval_interception(initial_pos, end_pos)
|
||||
local ret_y = nil
|
||||
local cast = minetest.raycast(initial_pos, end_pos, true, false)
|
||||
local thing = cast:next()
|
||||
while thing do
|
||||
if thing.type == "node" then
|
||||
local pos = thing.intersection_point
|
||||
if pos then
|
||||
local nodename = minetest.get_node(thing.under).name
|
||||
local drawtype = get_nodedef_field(nodename, "drawtype")
|
||||
|
||||
if drawtype ~= "plantlike" and drawtype ~= nil then
|
||||
if initial_pos.y >= pos.y then
|
||||
ret_y = pos.y
|
||||
--minetest.chat_send_all("ray intercection: " .. dump(pos.y) .. " -- " .. nodename)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
thing = cast:next()
|
||||
end
|
||||
return ret_y
|
||||
end
|
||||
|
||||
function automobiles_lib.get_node_below(pos, dist)
|
||||
local node = minetest.get_node(pos)
|
||||
local pos_below = pos
|
||||
pos_below.y = pos_below.y - (dist + 0.1)
|
||||
local node_below = minetest.get_node(pos_below)
|
||||
return node_below
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,937 @@
|
||||
-- Minetest 5.4.1 : automobiles
|
||||
|
||||
automobiles_lib = {
|
||||
storage = minetest.get_mod_storage()
|
||||
}
|
||||
|
||||
automobiles_lib.S = nil
|
||||
|
||||
if(minetest.get_translator ~= nil) then
|
||||
automobiles_lib.S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
else
|
||||
automobiles_lib.S = function ( s ) return s end
|
||||
|
||||
end
|
||||
|
||||
local S = automobiles_lib.S
|
||||
local storage = automobiles_lib.storage
|
||||
|
||||
automobiles_lib.is_minetest = core.get_modpath("player_api")
|
||||
automobiles_lib.is_mcl = core.get_modpath("mcl_player")
|
||||
automobiles_lib.is_repixture = core.get_modpath("rp_player")
|
||||
|
||||
automobiles_lib.fuel = {['biofuel:biofuel'] = 1,['biofuel:bottle_fuel'] = 1,
|
||||
['biofuel:phial_fuel'] = 0.25, ['biofuel:fuel_can'] = 10,
|
||||
['airutils:biofuel'] = 1,}
|
||||
|
||||
automobiles_lib.gravity = 9.8
|
||||
automobiles_lib.ideal_step = 0.2
|
||||
automobiles_lib.is_creative = minetest.settings:get_bool("creative_mode", false)
|
||||
automobiles_lib.can_collect_car = minetest.settings:get_bool("collect_automobiles", true)
|
||||
|
||||
automobiles_lib.is_drift_game = false
|
||||
automobiles_lib.extra_drift = false
|
||||
if minetest.registered_nodes["dg_mapgen:stone"] then
|
||||
automobiles_lib.is_drift_game = true
|
||||
automobiles_lib.extra_drift = true
|
||||
end
|
||||
|
||||
local load_noob_mode = core.settings:get_bool("noob_mode", false) -- or storage:get_int("noob_mode")
|
||||
automobiles_lib.noob_mode = false
|
||||
automobiles_lib.extra_stepheight = 0
|
||||
-- 1 == true ---- 2 == false
|
||||
if load_noob_mode == true then
|
||||
automobiles_lib.load_noob_mode = true
|
||||
automobiles_lib.extra_stepheight = 1
|
||||
end
|
||||
|
||||
local new_speed_limit = core.settings:get("speed_limit")
|
||||
automobiles_lib.speed_limit = tonumber(new_speed_limit) or 500
|
||||
automobiles_lib.speed_limit = math.floor(automobiles_lib.speed_limit)
|
||||
|
||||
local motorcycle_anim_mode = minetest.settings:get_bool("motorcycle_anim_mode", true)
|
||||
automobiles_lib.mot_anim_mode = true
|
||||
if motorcycle_anim_mode == false or automobiles_lib.is_mcl then --disable for mcl too -- TODO
|
||||
automobiles_lib.mot_anim_mode = false
|
||||
end
|
||||
|
||||
|
||||
--cars colors
|
||||
automobiles_lib.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',
|
||||
-- NEW: Mineclonia colors that were missing (thanks PsyMops)
|
||||
light_blue = "#258ec9", -- Light blue dye
|
||||
lime = "#60ac19", -- Lime dye (bright green)
|
||||
purple = "#6821a0", -- Purple dye (similar to violet)
|
||||
silver = "#818177", -- Silver/light grey dye
|
||||
}
|
||||
|
||||
local eye_height_plus_value = 6.5
|
||||
local base_eie_height = -4
|
||||
if automobiles_lib.is_mcl then
|
||||
base_eie_height = -5.5
|
||||
end
|
||||
|
||||
--
|
||||
-- helpers and co.
|
||||
--
|
||||
|
||||
function automobiles_lib.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 automobiles_lib.dot(v1,v2)
|
||||
return (v1.x*v2.x)+(v1.y*v2.y)+(v1.z*v2.z)
|
||||
end
|
||||
|
||||
function automobiles_lib.sign(n)
|
||||
return n>=0 and 1 or -1
|
||||
end
|
||||
|
||||
function automobiles_lib.minmax(v,m)
|
||||
return math.min(math.abs(v),m)*minekart.sign(v)
|
||||
end
|
||||
|
||||
function automobiles_lib.properties_copy(origin_table)
|
||||
local tablecopy = {}
|
||||
for k, v in pairs(origin_table) do
|
||||
tablecopy[k] = v
|
||||
end
|
||||
return tablecopy
|
||||
end
|
||||
|
||||
local function smoke_particle(self, pos)
|
||||
minetest.add_particle({
|
||||
pos = pos,
|
||||
velocity = {x = 0, y = 0, z = 0},
|
||||
acceleration = {x = 0, y = 0, z = 0},
|
||||
expirationtime = 0.25,
|
||||
size = 2.8*(self._vehicle_scale or 1),
|
||||
collisiondetection = false,
|
||||
collision_removal = false,
|
||||
vertical = false,
|
||||
texture = "automobiles_smoke.png",
|
||||
})
|
||||
end
|
||||
|
||||
function automobiles_lib.add_smoke(self, pos, yaw, rear_wheel_xpos)
|
||||
local direction = yaw
|
||||
|
||||
--right
|
||||
local move = rear_wheel_xpos/10
|
||||
local smk_pos = vector.new(pos)
|
||||
smk_pos.x = smk_pos.x + move * math.cos(direction)
|
||||
smk_pos.z = smk_pos.z + move * math.sin(direction)
|
||||
|
||||
smoke_particle(self, smk_pos)
|
||||
|
||||
--left
|
||||
direction = direction - math.rad(180)
|
||||
smk_pos = vector.new(pos)
|
||||
smk_pos.x = smk_pos.x + move * math.cos(direction)
|
||||
smk_pos.z = smk_pos.z + move * math.sin(direction)
|
||||
|
||||
smoke_particle(self, smk_pos)
|
||||
end
|
||||
|
||||
--returns 0 for old, 1 for new
|
||||
function automobiles_lib.detect_player_api(player)
|
||||
local player_proterties = player:get_properties()
|
||||
--local mesh = "character.b3d"
|
||||
--if player_proterties.mesh == mesh then
|
||||
if core.get_modpath("player_api") then
|
||||
local models = player_api.registered_models
|
||||
local character = models[player_proterties.mesh]
|
||||
--core.chat_send_all(dump(character));
|
||||
if character and character.animations and character.animations.sit then
|
||||
if character.animations.sit.eye_height then
|
||||
--core.chat_send_all(dump(character.animations.sit.eye_height));
|
||||
if character.animations.sit.eye_height == 0.8 then
|
||||
--core.chat_send_all("new model");
|
||||
return 1
|
||||
else
|
||||
--core.chat_send_all("new height");
|
||||
return 2 --strange bug with armor ands skins returning 1.47
|
||||
end
|
||||
else
|
||||
--core.chat_send_all("old model");
|
||||
return 0
|
||||
end
|
||||
end
|
||||
elseif core.get_modpath("mcl_player") then
|
||||
return 1
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function automobiles_lib.seats_create(self)
|
||||
if self.object then
|
||||
local pos = self.object:get_pos()
|
||||
self._passengers_base = {}
|
||||
self._passengers = {}
|
||||
if self._seat_pos then
|
||||
local max_seats = table.getn(self._seat_pos)
|
||||
for i=1, max_seats do
|
||||
self._passengers_base[i] = minetest.add_entity(pos,'automobiles_lib:pivot_mesh')
|
||||
if self._passengers_base[i] then
|
||||
if not self._seats_rot then
|
||||
self._passengers_base[i]:set_attach(self.object,'',self._seat_pos[i],{x=0,y=0,z=0})
|
||||
else
|
||||
self._passengers_base[i]:set_attach(self.object,'',self._seat_pos[i],{x=0,y=self._seats_rot[i],z=0})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.driver_seat = self._passengers_base[1] --sets pilot seat reference
|
||||
self.passenger_seat = self._passengers_base[2] --sets copilot seat reference
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.sit(player)
|
||||
--set_animation(frame_range, frame_speed, frame_blend, frame_loop)
|
||||
player:set_animation({x = 81, y = 160},30, 0, true)
|
||||
if core.get_modpath("emote") then emote.start(player:get_player_name(), "sit") end
|
||||
end
|
||||
|
||||
-- attach player
|
||||
function automobiles_lib.attach_driver(self, player)
|
||||
local name = player:get_player_name()
|
||||
self.driver_name = name
|
||||
if not self.driver_seat then return end
|
||||
|
||||
-- attach the driver
|
||||
player:set_attach(self.driver_seat, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
local eye_y = base_eie_height
|
||||
if automobiles_lib.detect_player_api(player) == 1 then
|
||||
eye_y = eye_y + eye_height_plus_value
|
||||
end
|
||||
eye_y = eye_y*self._vehicle_scale
|
||||
|
||||
player:set_eye_offset({x = 0, y = eye_y, z = 0}, {x = 0, y = eye_y, z = -30})
|
||||
|
||||
if automobiles_lib.is_minetest then
|
||||
player_api.player_attached[name] = true
|
||||
player_api.set_animation(player, "sit")
|
||||
elseif automobiles_lib.is_mcl then
|
||||
mcl_player.player_attached[name] = true
|
||||
mcl_player.player_set_animation(player, "sit" , 30)
|
||||
automobiles_lib.sit(player)
|
||||
end
|
||||
|
||||
-- make the driver sit
|
||||
minetest.after(1, function()
|
||||
if player then
|
||||
--minetest.chat_send_all("okay")
|
||||
automobiles_lib.sit(player)
|
||||
--apply_physics_override(player, {speed=0,gravity=0,jump=0})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function automobiles_lib.dettach_driver(self, player)
|
||||
local name = self.driver_name
|
||||
|
||||
--self._engine_running = false
|
||||
|
||||
-- driver clicked the object => driver gets off the vehicle
|
||||
self.driver_name = nil
|
||||
|
||||
if self._engine_running then
|
||||
self._engine_running = false
|
||||
end
|
||||
-- sound and animation
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
end
|
||||
|
||||
-- detach the player
|
||||
if player.set_detach then
|
||||
--automobiles_lib.remove_hud(player)
|
||||
|
||||
--player:set_properties({physical=true})
|
||||
player:set_detach()
|
||||
player:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
|
||||
if automobiles_lib.is_minetest then
|
||||
if player_api.player_attached[name] then
|
||||
player_api.player_attached[name] = nil
|
||||
end
|
||||
player_api.set_animation(player, "stand")
|
||||
elseif automobiles_lib.is_mcl then
|
||||
if mcl_player.player_attached[name] then
|
||||
mcl_player.player_attached[name] = nil
|
||||
end
|
||||
mcl_player.player_set_animation(player, "stand")
|
||||
end
|
||||
|
||||
end
|
||||
self.driver = nil
|
||||
end
|
||||
|
||||
-- attach passenger
|
||||
function automobiles_lib.attach_pax(self, player, onside)
|
||||
local onside = onside or false
|
||||
local name = player:get_player_name()
|
||||
|
||||
local eye_y = base_eie_height
|
||||
if automobiles_lib.detect_player_api(player) == 1 then
|
||||
eye_y = eye_y + eye_height_plus_value
|
||||
end
|
||||
eye_y = eye_y*self._vehicle_scale
|
||||
|
||||
if self._passenger == nil then
|
||||
self._passenger = name
|
||||
|
||||
-- attach the pax
|
||||
player:set_attach(self.passenger_seat, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
player:set_eye_offset({x = 0, y = eye_y, z = 0}, {x = 0, y = eye_y, z = -30})
|
||||
if automobiles_lib.is_minetest then
|
||||
player_api.player_attached[name] = true
|
||||
player_api.set_animation(player, "sit")
|
||||
elseif automobiles_lib.is_mcl then
|
||||
mcl_player.player_attached[name] = true
|
||||
mcl_player.player_set_animation(player, "sit" , 30)
|
||||
automobiles_lib.sit(player)
|
||||
end
|
||||
|
||||
-- make the pax sit
|
||||
minetest.after(0.2, function()
|
||||
if player then
|
||||
--minetest.chat_send_all("okay")
|
||||
automobiles_lib.sit(player)
|
||||
--apply_physics_override(player, {speed=0,gravity=0,jump=0})
|
||||
end
|
||||
end)
|
||||
else
|
||||
--randomize the seat
|
||||
local max_seats = table.getn(self._seat_pos) --driver and front passenger
|
||||
|
||||
t = {} -- new array
|
||||
for i=1, max_seats do --(the first are for the driver
|
||||
t[i] = i
|
||||
end
|
||||
|
||||
for i = 1, #t*2 do
|
||||
local a = math.random(#t)
|
||||
local b = math.random(#t)
|
||||
t[a],t[b] = t[b],t[a]
|
||||
end
|
||||
|
||||
for k,v in ipairs(t) do
|
||||
i = t[k]
|
||||
if self._passengers[i] == nil and i > 2 then
|
||||
--core.chat_send_all(self.driver_name)
|
||||
self._passengers[i] = name
|
||||
player:set_attach(self._passengers_base[i], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
player:set_eye_offset({x = 0, y = eye_y, z = 0}, {x = 0, y = 3, z = -30})
|
||||
if automobiles_lib.is_minetest then
|
||||
player_api.player_attached[name] = true
|
||||
player_api.set_animation(player, "sit")
|
||||
elseif automobiles_lib.is_mcl then
|
||||
mcl_player.player_attached[name] = true
|
||||
mcl_player.player_set_animation(player, "sit" , 30)
|
||||
automobiles_lib.sit(player)
|
||||
end
|
||||
|
||||
-- make the pax sit
|
||||
minetest.after(0.2, function()
|
||||
if player then
|
||||
--minetest.chat_send_all("okay")
|
||||
automobiles_lib.sit(player)
|
||||
--apply_physics_override(player, {speed=0,gravity=0,jump=0})
|
||||
end
|
||||
end)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.dettach_pax(self, player)
|
||||
if not player then return end
|
||||
local name = player:get_player_name() --self._passenger
|
||||
|
||||
-- passenger clicked the object => driver gets off the vehicle
|
||||
if self._passenger == name then
|
||||
self._passenger = nil
|
||||
self._passengers[2] = nil
|
||||
else
|
||||
local max_seats = table.getn(self._seat_pos)
|
||||
for i = max_seats,1,-1
|
||||
do
|
||||
if self._passengers[i] == name then
|
||||
self._passengers[i] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- detach the player
|
||||
if player then
|
||||
local pos = player:get_pos()
|
||||
player:set_detach()
|
||||
|
||||
if automobiles_lib.is_minetest then
|
||||
if player_api.player_attached[name] then
|
||||
player_api.player_attached[name] = nil
|
||||
end
|
||||
player_api.set_animation(player, "stand")
|
||||
elseif automobiles_lib.is_mcl then
|
||||
if mcl_player.player_attached[name] then
|
||||
mcl_player.player_attached[name] = nil
|
||||
end
|
||||
mcl_player.player_set_animation(player, "stand")
|
||||
end
|
||||
|
||||
player:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
--remove_physics_override(player, {speed=1,gravity=1,jump=1})
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.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
|
||||
|
||||
function automobiles_lib.setText(self, vehicle_name)
|
||||
local properties = self.object:get_properties()
|
||||
local formatted = ""
|
||||
if self.hp_max then
|
||||
formatted = S(" Current hp: ") .. string.format(
|
||||
"%.2f", self.hp_max
|
||||
)
|
||||
end
|
||||
if properties then
|
||||
properties.infotext = S("Nice @1 of @2.@3", vehicle_name, self.owner, formatted)
|
||||
self.object:set_properties(properties)
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.get_xz_from_hipotenuse(orig_x, orig_z, yaw, distance)
|
||||
--cara, o minetest é bizarro, ele considera o eixo no sentido ANTI-HORÁRIO... Então pra equação funcionar, subtrair o angulo de 360 antes
|
||||
yaw = math.rad(360) - yaw
|
||||
local z = (math.cos(yaw)*distance) + orig_z
|
||||
local x = (math.sin(yaw)*distance) + orig_x
|
||||
return x, z
|
||||
end
|
||||
|
||||
function automobiles_lib.remove_light(self)
|
||||
if self._light_old_pos then
|
||||
--force the remotion of the last light
|
||||
minetest.add_node(self._light_old_pos, {name="air"})
|
||||
self._light_old_pos = nil
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.swap_node(self, pos)
|
||||
local target_pos = pos
|
||||
local have_air = false
|
||||
local node = nil
|
||||
local count = 0
|
||||
while have_air == false and count <= 3 do
|
||||
node = minetest.get_node(target_pos)
|
||||
if node.name == "air" then
|
||||
have_air = true
|
||||
break
|
||||
end
|
||||
count = count + 1
|
||||
target_pos.y = target_pos.y + 1
|
||||
end
|
||||
|
||||
if have_air then
|
||||
minetest.set_node(target_pos, {name='automobiles_lib:light'})
|
||||
automobiles_lib.remove_light(self)
|
||||
self._light_old_pos = target_pos
|
||||
--remove after one second
|
||||
--[[minetest.after(1,function(target_pos)
|
||||
local node = minetest.get_node_or_nil(target_pos)
|
||||
if node and node.name == "automobiles_lib:light" then
|
||||
minetest.swap_node(target_pos, {name="air"})
|
||||
end
|
||||
end, target_pos)]]--
|
||||
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function automobiles_lib.put_light(self)
|
||||
local pos = self.object:get_pos()
|
||||
pos.y = pos.y + 1
|
||||
local yaw = self.object:get_yaw()
|
||||
local lx, lz = automobiles_lib.get_xz_from_hipotenuse(pos.x, pos.z, yaw, 10)
|
||||
local light_pos = {x=lx, y=pos.y, z=lz}
|
||||
|
||||
local cast = minetest.raycast(pos, light_pos, false, false)
|
||||
local thing = cast:next()
|
||||
local was_set = false
|
||||
while thing do
|
||||
if thing.type == "node" then
|
||||
local ipos = thing.intersection_point
|
||||
if ipos then
|
||||
was_set = automobiles_lib.swap_node(self, ipos)
|
||||
end
|
||||
end
|
||||
thing = cast:next()
|
||||
end
|
||||
if was_set == false then
|
||||
local n = minetest.get_node_or_nil(light_pos)
|
||||
if n and n.name == 'air' then
|
||||
automobiles_lib.swap_node(self, light_pos)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[local n = minetest.get_node_or_nil(light_pos)
|
||||
--minetest.chat_send_player(name, n.name)
|
||||
if n and n.name == 'air' then
|
||||
minetest.set_node(pos, {name='automobiles_lib:light'})
|
||||
--local timer = minetest.get_node_timer(pos)
|
||||
--timer:set(10, 0)
|
||||
minetest.after(0.3,function(pos)
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node and node.name == "automobiles_lib:light" then
|
||||
minetest.swap_node(pos, {name="air"})
|
||||
end
|
||||
end, pos)
|
||||
end]]--
|
||||
|
||||
end
|
||||
|
||||
function automobiles_lib.seats_destroy(self)
|
||||
local max_seats = table.getn(self._passengers_base)
|
||||
for i=1, max_seats do
|
||||
if self._passengers_base[i] then self._passengers_base[i]:remove() end
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.destroy(self, puncher)
|
||||
automobiles_lib.remove_light(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 first (puncher must be driver)
|
||||
if puncher then
|
||||
puncher:set_detach()
|
||||
puncher:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
|
||||
if automobiles_lib.is_minetest then
|
||||
if player_api.player_attached[self.driver_name] then
|
||||
player_api.player_attached[self.driver_name] = nil
|
||||
end
|
||||
player_api.set_animation(player, "stand")
|
||||
elseif automobiles_lib.is_mcl then
|
||||
if mcl_player.player_attached[self.driver_name] then
|
||||
mcl_player.player_attached[self.driver_name] = nil
|
||||
end
|
||||
mcl_player.player_set_animation(player, "stand")
|
||||
end
|
||||
end
|
||||
self.driver_name = nil
|
||||
end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
if self.front_suspension then self.front_suspension:remove() end
|
||||
if self.lf_wheel then self.lf_wheel:remove() end
|
||||
if self.rf_wheel then self.rf_wheel:remove() end
|
||||
if self.rear_suspension then self.rear_suspension:remove() end
|
||||
if self.lr_wheel then self.lr_wheel:remove() end
|
||||
if self.rr_wheel then self.rr_wheel:remove() end
|
||||
if self.steering then self.steering:remove() end
|
||||
if self.steering_axis then self.steering_axis:remove() end
|
||||
if self.driver_seat then self.driver_seat:remove() end
|
||||
if self.passenger_seat then self.passenger_seat:remove() end
|
||||
if self.fuel_gauge then self.fuel_gauge:remove() end
|
||||
if self.lights then self.lights:remove() end
|
||||
if self.r_lights then self.r_lights:remove() end
|
||||
if self.reverse_lights then self.reverse_lights:remove() end
|
||||
if self.turn_l_light then self.turn_l_light:remove() end
|
||||
if self.turn_r_light then self.turn_r_light:remove() end
|
||||
if self.rag then self.rag:remove() end --for buggy
|
||||
if self.back_seat then self.back_seat:remove() end --for catrelle
|
||||
if self.instruments then self.instruments:remove() end --for delorean
|
||||
if self.normal_kit then self.normal_kit:remove() end
|
||||
if self.rag_rect then self.rag_rect:remove() end --for roadster
|
||||
|
||||
|
||||
automobiles_lib.seats_destroy(self)
|
||||
automobiles_lib.destroy_inventory(self)
|
||||
|
||||
pos.y=pos.y+2
|
||||
|
||||
if automobiles_lib.can_collect_car == false then
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'automobiles_lib:engine')
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'automobiles_lib:wheel')
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'automobiles_lib:wheel')
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'automobiles_lib:wheel')
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'automobiles_lib:wheel')
|
||||
else
|
||||
local lua_ent = self.object:get_luaentity()
|
||||
local staticdata = lua_ent:get_staticdata(self)
|
||||
local obj_name = lua_ent.name
|
||||
local player = puncher
|
||||
|
||||
local stack = ItemStack(obj_name)
|
||||
local stack_meta = stack:get_meta()
|
||||
stack_meta:set_string("staticdata", staticdata)
|
||||
|
||||
if player then
|
||||
local inv = player:get_inventory()
|
||||
if inv then
|
||||
if inv:room_for_item("main", stack) then
|
||||
inv:add_item("main", stack)
|
||||
else
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack)
|
||||
end
|
||||
end
|
||||
else
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack)
|
||||
end
|
||||
end
|
||||
|
||||
self.object:remove()
|
||||
end
|
||||
|
||||
function automobiles_lib.engine_set_sound_and_animation(self, _longit_speed)
|
||||
--minetest.chat_send_all('test1 ' .. dump(self._engine_running) )
|
||||
local abs_curr_long_speed = math.abs(self._longit_speed)
|
||||
local abs_long_speed = math.abs(_longit_speed)
|
||||
local scale = self._vehicle_power_scale
|
||||
local range_spacing = 0.01
|
||||
if self.sound_handle then
|
||||
if (abs_curr_long_speed*scale > (abs_long_speed + range_spacing)*scale)
|
||||
or ((abs_curr_long_speed + range_spacing)*scale < abs_long_speed*scale) then
|
||||
--minetest.chat_send_all('test2')
|
||||
automobiles_lib.engineSoundPlay(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.engineSoundPlay(self)
|
||||
--sound
|
||||
if self.sound_handle then minetest.sound_stop(self.sound_handle) end
|
||||
if self.object then
|
||||
local base_pitch = 1
|
||||
if self._base_pitch then base_pitch = self._base_pitch end
|
||||
|
||||
local divisor = 6 --3 states, so 6 to make it more smooth
|
||||
local multiplier = self._transmission_state or 1
|
||||
local snd_pitch = base_pitch + ((base_pitch/divisor)*multiplier) + ((self._longit_speed/10)/2)
|
||||
if self._transmission_state == 1 then
|
||||
snd_pitch = base_pitch + (self._longit_speed/10)
|
||||
end
|
||||
|
||||
self.sound_handle = minetest.sound_play({name = self._engine_sound},
|
||||
{object = self.object, gain = 4,
|
||||
pitch = snd_pitch,
|
||||
max_hear_distance = 15,
|
||||
loop = true,})
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("automobiles_lib:light", {
|
||||
drawtype = "airlike",
|
||||
--tile_images = {"automobiles_light.png"},
|
||||
inventory_image = minetest.inventorycube("automobiles_light.png"),
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
is_ground_content = true,
|
||||
light_propagates = true,
|
||||
sunlight_propagates = true,
|
||||
light_source = 14,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
})
|
||||
|
||||
function automobiles_lib.set_paint(self, puncher, itmstck)
|
||||
local is_admin = false
|
||||
is_admin = minetest.check_player_privs(puncher, {server=true})
|
||||
if not (self.owner == puncher:get_player_name() or is_admin == true) then
|
||||
return false
|
||||
end
|
||||
|
||||
local item_name = ""
|
||||
if itmstck then item_name = itmstck:get_name() end
|
||||
|
||||
if item_name == "bike:painter" then
|
||||
--painting with bike painter
|
||||
local meta = itmstck:get_meta()
|
||||
local colstr = meta:get_string("paint_color")
|
||||
automobiles_lib.paint(self, colstr)
|
||||
return true
|
||||
else
|
||||
--painting with dyes
|
||||
local split = string.split(item_name, ":")
|
||||
local color, indx, _
|
||||
if split[1] then _,indx = split[1]:find('dye') end
|
||||
--minetest.chat_send_all("item " .. dump(item_name))
|
||||
if indx then
|
||||
--[[for clr,_ in pairs(automobiles_lib.colors) do
|
||||
local _,x = split[2]:find(clr)
|
||||
if x then color = clr end
|
||||
end]]--
|
||||
--lets paint!!!!
|
||||
local color = split[2] --(item_name:sub(indx+1)):gsub(":", "")
|
||||
local colstr = automobiles_lib.colors[color]
|
||||
--minetest.chat_send_all(color ..' '.. dump(colstr))
|
||||
if colstr then
|
||||
automobiles_lib.paint(self, colstr)
|
||||
itmstck:set_count(itmstck:get_count()-1)
|
||||
puncher:set_wielded_item(itmstck)
|
||||
return true
|
||||
end
|
||||
-- end painting
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--painting
|
||||
function automobiles_lib.paint(self, colstr, target_texture, second_painting_texture)
|
||||
target_texture = target_texture or "automobiles_painting.png"
|
||||
second_painting_texture = second_painting_texture or "automobiles_painting2.png"
|
||||
if colstr then
|
||||
self._color = colstr
|
||||
local l_textures = self.initial_properties.textures
|
||||
for _, texture in ipairs(l_textures) do
|
||||
local indx = texture:find(target_texture)
|
||||
if indx then
|
||||
l_textures[_] = target_texture.."^[multiply:".. colstr
|
||||
end
|
||||
indx = texture:find(second_painting_texture)
|
||||
if indx then
|
||||
l_textures[_] = second_painting_texture.."^[multiply:".. colstr
|
||||
end
|
||||
end
|
||||
self.object:set_properties({textures=l_textures})
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.paint_with_mask(self, colstr, mask_colstr, target_texture, mask_texture)
|
||||
--"("..steampunk_blimp.canvas_texture.."^[mask:steampunk_blimp_rotor_mask2.png)^(default_wood.png^[mask:steampunk_blimp_rotor_mask.png)"
|
||||
|
||||
target_texture = target_texture or "automobiles_painting.png"
|
||||
if colstr then
|
||||
self._color = colstr
|
||||
self._det_color = mask_colstr
|
||||
local l_textures = self.initial_properties.textures
|
||||
for _, texture in ipairs(l_textures) do
|
||||
local indx = texture:find(target_texture)
|
||||
if indx then
|
||||
--"("..target_texture.."^[mask:"..mask_texture..")"
|
||||
l_textures[_] = "("..target_texture.."^[multiply:".. colstr..")^("..target_texture.."^[multiply:".. mask_colstr.."^[mask:"..mask_texture..")"
|
||||
end
|
||||
end
|
||||
self.object:set_properties({textures=l_textures})
|
||||
end
|
||||
end
|
||||
|
||||
-- very basic transmission emulation for the car
|
||||
function automobiles_lib.get_transmission_state(self, curr_speed, max_speed)
|
||||
local retVal = 1
|
||||
max_speed = max_speed or 100
|
||||
max_speed = max_speed*self._vehicle_scale
|
||||
curr_speed = curr_speed*self._vehicle_scale
|
||||
if curr_speed >= (max_speed/4) then retVal = 2 end
|
||||
if curr_speed >= (max_speed/2) then retVal = 3 end
|
||||
return retVal
|
||||
end
|
||||
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "physics_lib.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "custom_physics.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "control.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "fuel_management.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "ground_detection.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "painter.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "inventory_management.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "formspecs.lua")
|
||||
dofile(minetest.get_modpath("automobiles_lib") .. DIR_DELIM .. "entities.lua")
|
||||
|
||||
-- engine
|
||||
minetest.register_craftitem("automobiles_lib:engine",{
|
||||
description = S("Car Engine"),
|
||||
inventory_image = "automobiles_engine.png",
|
||||
})
|
||||
|
||||
-- engine
|
||||
minetest.register_craftitem("automobiles_lib:wheel",{
|
||||
description = S("Car Wheel"),
|
||||
inventory_image = "automobiles_wheel_icon.png",
|
||||
})
|
||||
|
||||
if minetest.get_modpath("default") then
|
||||
minetest.register_craft({
|
||||
output = "automobiles_lib:engine",
|
||||
recipe = {
|
||||
{"default:steel_ingot","default:steel_ingot","default:steel_ingot"},
|
||||
{"default:steelblock","default:mese_block", "default:steelblock"},
|
||||
}
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "automobiles_lib:wheel",
|
||||
recipe = {
|
||||
{"default:tin_ingot", "default:steel_ingot", "default:tin_ingot"},
|
||||
{"default:steel_ingot","default:steelblock", "default:steel_ingot"},
|
||||
{"default:tin_ingot", "default:steel_ingot", "default:tin_ingot"},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_entity('automobiles_lib:wheel',{
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collide_with_objects=false,
|
||||
pointable=false,
|
||||
visual = "mesh",
|
||||
mesh = "automobiles_wheel.b3d",
|
||||
backface_culling = false,
|
||||
textures = {"automobiles_black.png", "automobiles_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_privilege("valet_parking", {
|
||||
description = S("Gives a valet parking priv for a player"),
|
||||
give_to_singleplayer = true
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("transfer_vehicle", {
|
||||
params = "<new_owner>",
|
||||
description = S("Transfer the property of a vehicle to another player"),
|
||||
privs = {interact=true},
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local target_player = minetest.get_player_by_name(param)
|
||||
local attached_to = player:get_attach()
|
||||
|
||||
if attached_to ~= nil then
|
||||
if target_player ~= nil then
|
||||
local seat = attached_to:get_attach()
|
||||
if seat ~= nil then
|
||||
local entity = seat:get_luaentity()
|
||||
if entity then
|
||||
if entity.owner == name or minetest.check_player_privs(name, {protection_bypass=true}) then
|
||||
entity.owner = param
|
||||
minetest.chat_send_player(name,core.colorize('#00ff00', S(" >>> This vehicle now is property of: ")..param))
|
||||
automobiles_lib.setText(entity, "vehicle")
|
||||
else
|
||||
minetest.chat_send_player(name,core.colorize('#ff0000', S(" >>> only the owner or moderators can transfer this vehicle")))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name,core.colorize('#ff0000', S(" >>> the target player must be logged in")))
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name,core.colorize('#ff0000', S(" >>> you are not inside a vehicle to perform the command")))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
--[[minetest.register_chatcommand("noobfy_the_vehicles", {
|
||||
params = "<true/false>",
|
||||
description = S("Enable/disable the NOOB mode for the vehicles"),
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
local command = param
|
||||
|
||||
if command == "false" then
|
||||
automobiles_lib.noob_mode = false
|
||||
automobiles_lib.extra_stepheight = 0
|
||||
minetest.chat_send_player(name, S(">>> Noob mode is disabled - A restart is required to changes take full effect"))
|
||||
else
|
||||
automobiles_lib.noob_mode = true
|
||||
automobiles_lib.extra_stepheight = 1
|
||||
minetest.chat_send_player(name, S(">>> Noob mode is enabled - A restart is required to changes take full effect"))
|
||||
end
|
||||
local save = 2
|
||||
if automobiles_lib.noob_mode == true then save = 1 end
|
||||
storage:set_int("noob_mode", save)
|
||||
end,
|
||||
})]]--
|
||||
|
||||
-- Give to new player
|
||||
if automobiles_lib.is_drift_game == true then
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local inv = player:get_inventory()
|
||||
local car = "automobiles_beetle:beetle"
|
||||
if core.get_modpath("automobiles_beetle") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
car = "automobiles_buggy:buggy"
|
||||
if core.get_modpath("automobiles_buggy") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
car = "automobiles_catrelle:catrelle_4f"
|
||||
if core.get_modpath("automobiles_catrelle") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
car = "automobiles_coupe:coupe"
|
||||
if core.get_modpath("automobiles_coupe") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
car = "automobiles_delorean:delorean"
|
||||
if core.get_modpath("automobiles_delorean") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
car = "automobiles_delorean:time_machine"
|
||||
if core.get_modpath("automobiles_delorean") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
car = "automobiles_trans_am:trans_am"
|
||||
if core.get_modpath("automobiles_trans_am") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
car = "automobiles_beetle:beetle_conv"
|
||||
if core.get_modpath("automobiles_beetle") and not inv:contains_item("main", car) then inv:add_item("main", car) end
|
||||
end)
|
||||
end
|
||||
|
||||
local old_entities = {
|
||||
"automobiles_buggy:pivot_mesh",
|
||||
"automobiles_buggy:pointer",
|
||||
"automobiles_catrelle:pivot_mesh",
|
||||
"automobiles_catrelle:pointer",
|
||||
"automobiles_catrelle:catrelle_tl",
|
||||
"automobiles_coupe:pivot_mesh",
|
||||
"automobiles_coupe:pointer",
|
||||
"automobiles_delorean:pivot_mesh",
|
||||
"automobiles_delorean:pointer",
|
||||
"automobiles_roadster:pivot_mesh",
|
||||
"automobiles_trans_am:pivot_mesh",
|
||||
"automobiles_trans_am:pointer",
|
||||
"automobiles_buggy:steering",
|
||||
"automobiles_vespa:pivot_mesh",
|
||||
"automobiles_motorcycle:pivot_mesh",
|
||||
}
|
||||
for _,entity_name in ipairs(old_entities) do
|
||||
minetest.register_entity(":"..entity_name, {
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:remove()
|
||||
end,
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,208 @@
|
||||
local storage = automobiles_lib.storage
|
||||
automobiles_lib.modname = core.get_current_modname()
|
||||
|
||||
--function to format formspec for mineclone. In case of minetest, just returns an empty string
|
||||
local function get_itemslot_bg(a, b, c, d)
|
||||
if automobiles_lib.is_mcl then
|
||||
if mcl_formspec then
|
||||
return mcl_formspec.get_itemslot_bg(a,b,c,d)
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function get_formspec_by_size(self, size)
|
||||
local background = ""
|
||||
local hotbar = ""
|
||||
local is_minetest_game = automobiles_lib.is_minetest or false
|
||||
if is_minetest_game then
|
||||
background = background .. default.gui_bg .. default.gui_bg_img .. default.gui_slots
|
||||
hotbar = default.get_hotbar_bg(0,4.85)
|
||||
end
|
||||
local default_inventory_formspecs = {
|
||||
["2"]="size[8,6]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;3.0,0;3,1;]" .. get_itemslot_bg(3.0, 0, 2, 1) ..
|
||||
"list[current_player;main;0,2;8,4;]" .. get_itemslot_bg(0, 2, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["3"]="size[8,6]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;2.5,0;3,1;]" .. get_itemslot_bg(2.5, 0, 3, 1) ..
|
||||
"list[current_player;main;0,2;8,4;]" .. get_itemslot_bg(0, 2, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["4"]="size[8,6]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;2,0;4,1;]" .. get_itemslot_bg(2.0, 0, 4, 1) ..
|
||||
"list[current_player;main;0,2;8,4;]" .. get_itemslot_bg(0, 2, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["6"]="size[8,6]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;1,0;6,1;]".. get_itemslot_bg(1.0, 0, 6, 1) ..
|
||||
"list[current_player;main;0,2;8,4;]" .. get_itemslot_bg(0, 2, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["8"]="size[8,6]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;0,0;8,1;]".. get_itemslot_bg(0, 0, 8, 1) ..
|
||||
"list[current_player;main;0,2;8,4;]" .. get_itemslot_bg(0, 2, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["12"]="size[8,7]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;1,0;6,2;]".. get_itemslot_bg(1, 0, 6, 2) ..
|
||||
"list[current_player;main;0,3;8,4;]" .. get_itemslot_bg(0, 3, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["16"]="size[8,7]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;0,0;8,2;]".. get_itemslot_bg(0, 0, 8, 2) ..
|
||||
"list[current_player;main;0,3;8,4;]" .. get_itemslot_bg(0, 3, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["24"]="size[8,8]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;0,0;8,3;]".. get_itemslot_bg(0, 0, 8, 3) ..
|
||||
"list[current_player;main;0,4;8,4;]" .. get_itemslot_bg(0, 4, 8, 4) ..
|
||||
"listring[]",
|
||||
|
||||
["32"]="size[8,9]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;0,0.3;8,4;]".. get_itemslot_bg(0, 0.3, 8, 4) ..
|
||||
"list[current_player;main;0,5;8,4;]".. get_itemslot_bg(0, 5, 8, 4) ..
|
||||
"listring[]" ..
|
||||
hotbar,
|
||||
|
||||
["50"]="size[10,10]".. background ..
|
||||
"list[detached:" .. self._inv_id .. ";main;0,0;10,5;]".. get_itemslot_bg(0, 0, 10, 5) ..
|
||||
"list[current_player;main;1,6;8,4;]" .. get_itemslot_bg(1, 6, 8, 4) ..
|
||||
"listring[]",
|
||||
}
|
||||
|
||||
local formspec = default_inventory_formspecs[tostring(size)]
|
||||
return formspec
|
||||
end
|
||||
|
||||
local function inventory_id(maker_name)
|
||||
local id= automobiles_lib.modname .. "_" .. maker_name .. "_"
|
||||
for i=0,5 do
|
||||
id=id..(math.random(0,9))
|
||||
end
|
||||
return id
|
||||
end
|
||||
|
||||
function automobiles_lib.load_inventory(self)
|
||||
if self._inv then
|
||||
local inv_content = minetest.deserialize(storage:get_string(self._inv_id))
|
||||
if inv_content then
|
||||
self._inv:set_list("main", inv_content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.save_inventory(self)
|
||||
if self._inv then
|
||||
local inv_content = self._inv:get_list("main")
|
||||
if inv_content then
|
||||
for k, v in pairs(inv_content) do
|
||||
inv_content[k] = v:to_string()
|
||||
end
|
||||
|
||||
local inv_content = minetest.serialize(inv_content)
|
||||
storage:set_string(self._inv_id, inv_content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.remove_inventory(self)
|
||||
local inventory = automobiles_lib.get_inventory(self)
|
||||
if inventory then
|
||||
if inventory:is_empty("main") then
|
||||
return minetest.remove_detached_inventory(self._inv_id)
|
||||
else
|
||||
local inv_content = inventory:get_list("main")
|
||||
if inv_content then
|
||||
local pos = self.object:get_pos()
|
||||
for k, v in pairs(inv_content) do
|
||||
local count = 0
|
||||
for i = 0,v:get_count()-1,1
|
||||
do
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},v:get_name())
|
||||
count = count + 1
|
||||
if count >= 5 then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
return minetest.remove_detached_inventory(self._inv_id)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function automobiles_lib.destroy_inventory(self)
|
||||
automobiles_lib.remove_inventory(self)
|
||||
storage:set_string(self._inv_id, nil)
|
||||
end
|
||||
|
||||
--show inventory form to user
|
||||
function automobiles_lib.show_vehicle_trunk_formspec(self, player, size)
|
||||
if size then
|
||||
local form = get_formspec_by_size(self, size)
|
||||
if form then
|
||||
minetest.show_formspec(player:get_player_name(), automobiles_lib.modname .. ":inventory",
|
||||
form
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.create_inventory(self, size, owner)
|
||||
owner = owner or ""
|
||||
if owner == "" then owner = self.owner end
|
||||
if owner ~= nil and owner ~= "" then
|
||||
if self._inv_id == "" then
|
||||
self._inv_id = inventory_id(owner)
|
||||
end
|
||||
local vehicle_inv = minetest.create_detached_inventory(self._inv_id, {
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
return count -- allow moving
|
||||
end,
|
||||
|
||||
allow_put = function(inv, listname, index, stack, player)
|
||||
return stack:get_count() -- allow putting
|
||||
end,
|
||||
|
||||
allow_take = function(inv, listname, index, stack, player)
|
||||
return stack:get_count() -- allow taking
|
||||
end,
|
||||
on_put = function(inv, toList, toIndex, stack, player)
|
||||
automobiles_lib.save_inventory(self)
|
||||
end,
|
||||
on_take = function(inv, toList, toIndex, stack, player)
|
||||
automobiles_lib.save_inventory(self)
|
||||
end,
|
||||
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
automobiles_lib.save_inventory(self)
|
||||
end,
|
||||
})
|
||||
if size >= 8 then
|
||||
if vehicle_inv:set_size("main", size) then
|
||||
vehicle_inv:set_width("main", 8)
|
||||
end
|
||||
else
|
||||
vehicle_inv:set_size("main", size)
|
||||
end
|
||||
self._inv = vehicle_inv
|
||||
automobiles_lib.load_inventory(self)
|
||||
end
|
||||
end
|
||||
|
||||
function automobiles_lib.get_inventory(self)
|
||||
if self._inv then
|
||||
return minetest.get_inventory({type="detached", name=self._inv_id})
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function automobiles_lib.list_inventory(self)
|
||||
local inventory = automobiles_lib.get_inventory(self)
|
||||
if inventory then
|
||||
local list = inventory.get_list("main")
|
||||
|
||||
minetest.chat_send_all(dump(list))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# textdomain: automobiles_lib
|
||||
### init.lua ###
|
||||
Go Offboard=Aussteigen
|
||||
Lights=Lichter
|
||||
Direction by mouse=Lenken mit Maus
|
||||
Current hp: = Aktuelle Lp:
|
||||
Nice @1 of @2.@3=Schöne(r) @1 von @2.@3
|
||||
Car Engine=Motor
|
||||
Car Wheel=Autoreifen
|
||||
Gives a valet parking priv for a player=Gibt einen Parkservice für einen Spieler
|
||||
Transfer the property of a vehicle to another player=Überträgt das Eigentum des Fahrzeuges an einem anderen Spieler
|
||||
>>> This vehicle now is property of: = >>> Dieses Fahrzeug ist nun Eigentum von:
|
||||
>>> only the owner or moderators can transfer this vehicle= >>> Nur der Eigentümer oder Moderator kann das Eigentum des Fahrzeuges übertragen
|
||||
>>> the target player must be logged in= >>> der Zielspieler muß dafür eingeloggt sein.
|
||||
>>> you are not inside a vehicle to perform the command= >>> du sitzt in keinem Fahrzeug um das Kommando auszuführen
|
||||
Enable/disable the NOOB mode for the vehicles=Ein/Ausschalten des NOOB Moduses für die Fahrzeuge
|
||||
>>> Noob mode is disabled - A restart is required to changes take full effect= >>> Noob Modus ist ausgeschalten - Ein Neustart für die Änderungen ist erforderlich
|
||||
>>> Noob mode is enabled - A restart is required to changes take full effect= >>> Noob Modus ist eingeschalten - Ein Neustart für die Änderungen ist erforderlich
|
||||
Preview=Vorschau
|
||||
Set Hex=Gib Hexzahl
|
||||
Set Color=Gib Farbe
|
||||
Cancel=Abbrechen
|
||||
### painter.lua ###
|
||||
Automobiles Painter= Automobil Pinsel
|
||||
Only the owner can paint this entity.=Nur der Eigentümer kann dieses Objekt einfärben.
|
||||
@@ -0,0 +1,25 @@
|
||||
# textdomain: automobiles_lib
|
||||
### init.lua ###
|
||||
Go Offboard=
|
||||
Lights=
|
||||
Direction by mouse=
|
||||
Current hp: =
|
||||
Nice @1 of @2.@3=
|
||||
Car Engine=Aŭta Motoro
|
||||
Car Wheel=Aŭta Rado
|
||||
Gives a valet parking priv for a player=
|
||||
Transfer the property of a vehicle to another player=
|
||||
>>> This vehicle now is property of: =
|
||||
>>> only the owner or moderators can transfer this vehicle=
|
||||
>>> the target player must be logged in=
|
||||
>>> you are not inside a vehicle to perform the command=
|
||||
Enable/disable the NOOB mode for the vehicles=
|
||||
>>> Noob mode is disabled - A restart is required to changes take full effect=
|
||||
>>> Noob mode is enabled - A restart is required to changes take full effect=
|
||||
Preview=
|
||||
Set Hex=
|
||||
Set Color=
|
||||
Cancel=
|
||||
### painter.lua ###
|
||||
Automobiles Painter=Aŭta Pentrilo
|
||||
Only the owner can paint this entity.=
|
||||
@@ -0,0 +1,25 @@
|
||||
# textdomain: automobiles_lib
|
||||
### init.lua ###
|
||||
Go Offboard=
|
||||
Lights=
|
||||
Direction by mouse=
|
||||
Current hp: =
|
||||
Nice @1 of @2.@3=
|
||||
Car Engine=
|
||||
Car Wheel=
|
||||
Gives a valet parking priv for a player=
|
||||
Transfer the property of a vehicle to another player=
|
||||
>>> This vehicle now is property of: =
|
||||
>>> only the owner or moderators can transfer this vehicle=
|
||||
>>> the target player must be logged in=
|
||||
>>> you are not inside a vehicle to perform the command=
|
||||
Enable/disable the NOOB mode for the vehicles=
|
||||
>>> Noob mode is disabled - A restart is required to changes take full effect=
|
||||
>>> Noob mode is enabled - A restart is required to changes take full effect=
|
||||
Preview=
|
||||
Set Hex=
|
||||
Set Color=
|
||||
Cancel=
|
||||
### painter.lua ###
|
||||
Automobiles Painter=
|
||||
Only the owner can paint this entity.=
|
||||
@@ -0,0 +1,3 @@
|
||||
name = automobiles_lib
|
||||
depends=
|
||||
optional_depends=player_api, mcl_formspec, mcl_player, emote, biofuel, dg_mapgen
|
||||
@@ -0,0 +1,181 @@
|
||||
--[[
|
||||
Copyright (C) 2022 Alexsandro Percy
|
||||
Copyright (C) 2018 Hume2
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
]]--
|
||||
|
||||
local S = automobiles_lib.S
|
||||
|
||||
local function is_hex(color)
|
||||
if not color or color:len() ~= 7 then return end
|
||||
return color:match("#%x%x%x%x%x%x")
|
||||
end
|
||||
|
||||
-- hex translation
|
||||
local function hex_to_rgb(hex_value)
|
||||
hex_value = hex_value:gsub("#","")
|
||||
local rgb = {
|
||||
r = tonumber("0x"..hex_value:sub(1,2)),
|
||||
g = tonumber("0x"..hex_value:sub(3,4)),
|
||||
b = tonumber("0x"..hex_value:sub(5,6)),
|
||||
}
|
||||
return rgb
|
||||
end
|
||||
|
||||
local function rgb_to_hex(rgb)
|
||||
return string.format("#%02X%02X%02X", rgb.r, rgb.g, rgb.b)
|
||||
end
|
||||
|
||||
-- Painter formspec
|
||||
local function painter_form(player, rgb)
|
||||
local color = rgb_to_hex(rgb)
|
||||
minetest.show_formspec(player:get_player_name(), "automobiles_lib:painter",
|
||||
-- Init formspec
|
||||
"formspec_version[3]" .. -- Minetest 5.2+
|
||||
"size[5.6,5.2;true]" ..
|
||||
"position[0.5,0.45]" ..
|
||||
|
||||
-- Color preview
|
||||
"image[0.2,0.2;2,2;automobiles_painting.png^[colorize:" .. color .. ":255]" ..
|
||||
"label[0.3,1.2;" .. S("Preview") .. "]" ..
|
||||
|
||||
-- Hex field
|
||||
"field_close_on_enter[hex;false]" ..
|
||||
"field[2.4,0.2;3,0.8;hex;;" .. color .. "]" ..
|
||||
"button[2.4,1;3,0.8;set_hex;" .. S("Set Hex") .. "]" ..
|
||||
|
||||
-- RGB sliders
|
||||
"container[0,2.4]" ..
|
||||
"scrollbaroptions[min=0;max=255;smallstep=20]" ..
|
||||
|
||||
"box[0.4,0;4.77,0.38;red]" ..
|
||||
"label[0.2,0.2;-]" ..
|
||||
"scrollbar[0.4,0;4.8,0.4;horizontal;r;" .. rgb.r .."]" ..
|
||||
"label[5.2,0.2;+]" ..
|
||||
|
||||
"box[0.4,0.6;4.77,0.38;green]" ..
|
||||
"label[0.2,0.8;-]" ..
|
||||
"scrollbar[0.4,0.6;4.8,0.4;horizontal;g;" .. rgb.g .."]" ..
|
||||
"label[5.2,0.8;+]" ..
|
||||
|
||||
"box[0.4,1.2;4.77,0.38;blue]" ..
|
||||
"label[0.2,1.4;-]" ..
|
||||
"scrollbar[0.4,1.2;4.8,0.4;horizontal;b;" .. rgb.b .. "]" ..
|
||||
"label[5.2,1.4;+]" ..
|
||||
|
||||
"container_end[]" ..
|
||||
|
||||
-- Bottom buttons
|
||||
"button_exit[0.2,4.2;2.8,0.8;set_color;" .. S("Set Color") .. "]" ..
|
||||
"button_exit[3.2,4.2;2.2,0.8;quit;" .. S("Cancel") .. "]"
|
||||
)
|
||||
end
|
||||
|
||||
local automobiles_being_painted = {}
|
||||
local formspec_timers = {}
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "automobiles_lib:painter" then
|
||||
if formspec_timers[player] then
|
||||
formspec_timers[player]:cancel()
|
||||
formspec_timers[player] = nil
|
||||
end
|
||||
|
||||
local function parse_scrollbar_field(value)
|
||||
value = value or ""
|
||||
local num = math.floor(tonumber((value:gsub(".*:", ""))) or 0)
|
||||
if num > 255 or num < 0 then num = 0 end
|
||||
return num
|
||||
end
|
||||
|
||||
local rgb = {
|
||||
r = parse_scrollbar_field(fields.r),
|
||||
g = parse_scrollbar_field(fields.g),
|
||||
b = parse_scrollbar_field(fields.b),
|
||||
}
|
||||
|
||||
if fields.set_hex or fields.key_enter then
|
||||
if is_hex(fields.hex) then
|
||||
painter_form(player, hex_to_rgb(fields.hex))
|
||||
else
|
||||
painter_form(player, rgb)
|
||||
end
|
||||
elseif fields.set_color then
|
||||
local object_ref = automobiles_being_painted[player]
|
||||
if object_ref and object_ref:get_pos() then
|
||||
local luaentity = object_ref:get_luaentity()
|
||||
luaentity:_change_color(rgb_to_hex(rgb))
|
||||
end
|
||||
automobiles_being_painted[player] = nil
|
||||
elseif fields.r and fields.r:find("^CHG") or
|
||||
fields.g and fields.g:find("^CHG") or
|
||||
fields.b and fields.b:find("^CHG") then -- Has a scrollbar changed?
|
||||
formspec_timers[player] = minetest.after(0.2, function(itemstack, name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
painter_form(player, rgb)
|
||||
end
|
||||
formspec_timers[player] = nil
|
||||
end, itemstack, player:get_player_name())
|
||||
end
|
||||
|
||||
if fields.quit then
|
||||
automobiles_being_painted[player] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- Make the actual thingy
|
||||
minetest.register_tool("automobiles_lib:painter", {
|
||||
description = S("Automobiles Painter"),
|
||||
inventory_image = "automobiles_painter.png",
|
||||
wield_scale = {x = 2, y = 2, z = 1},
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type == "object" and not pointed_thing.ref:is_player() then
|
||||
local luaentity = pointed_thing.ref:get_luaentity()
|
||||
if luaentity and type(luaentity._change_color) == "function" then
|
||||
local player_name = user:get_player_name()
|
||||
if not luaentity.owner or luaentity.owner == player_name then
|
||||
automobiles_being_painted[user] = pointed_thing.ref
|
||||
local color = luaentity._color
|
||||
local rgb = is_hex(color) and hex_to_rgb(color) or {r = 0, g = 0, b = 0}
|
||||
painter_form(user, rgb)
|
||||
else
|
||||
minetest.chat_send_player(player_name, S("Only the owner can paint this entity."))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_on_leaveplayer(function(player, timed_out)
|
||||
automobiles_being_painted[player] = nil
|
||||
end)
|
||||
|
||||
minetest.register_craft({
|
||||
output = "automobiles_lib:painter",
|
||||
recipe = {
|
||||
{"", "default:steel_ingot", ""},
|
||||
{"dye:red", "dye:green", "dye:blue"},
|
||||
{"", "default:steel_ingot", ""},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,175 @@
|
||||
local abs = math.abs
|
||||
local pi = math.pi
|
||||
local floor = math.floor
|
||||
local ceil = math.ceil
|
||||
local random = math.random
|
||||
local sqrt = math.sqrt
|
||||
local max = math.max
|
||||
local min = math.min
|
||||
local tan = math.tan
|
||||
local pow = math.pow
|
||||
|
||||
local sign = function(x)
|
||||
return (x<0) and -1 or 1
|
||||
end
|
||||
|
||||
function automobiles_lib.rot_to_dir(rot) -- keep rot within <-pi/2,pi/2>
|
||||
local dir = minetest.yaw_to_dir(rot.y)
|
||||
dir.y = dir.y+tan(rot.x)*vector.length(dir)
|
||||
return vector.normalize(dir)
|
||||
end
|
||||
|
||||
function automobiles_lib.dir_to_rot(v,rot)
|
||||
rot = rot or {x=0,y=0,z=0}
|
||||
return {x = (v.x==0 and v.y==0 and v.z==0) and rot.x or math.atan2(v.y,vector.length({x=v.x,y=0,z=v.z})),
|
||||
y = (v.x==0 and v.z==0) and rot.y or minetest.dir_to_yaw(v),
|
||||
z=rot.z}
|
||||
end
|
||||
|
||||
function automobiles_lib.pos_shift(pos,vec) -- vec components can be omitted e.g. vec={y=1}
|
||||
vec.x=vec.x or 0
|
||||
vec.y=vec.y or 0
|
||||
vec.z=vec.z or 0
|
||||
return {x=pos.x+vec.x,
|
||||
y=pos.y+vec.y,
|
||||
z=pos.z+vec.z}
|
||||
end
|
||||
|
||||
function automobiles_lib.get_stand_pos(thing) -- thing can be luaentity or objectref.
|
||||
local pos = {}
|
||||
local colbox = {}
|
||||
if type(thing) == 'table' then
|
||||
pos = thing.object:get_pos()
|
||||
colbox = thing.object:get_properties().collisionbox
|
||||
elseif type(thing) == 'userdata' then
|
||||
pos = thing:get_pos()
|
||||
colbox = thing:get_properties().collisionbox
|
||||
else
|
||||
return false
|
||||
end
|
||||
return automobiles_lib.pos_shift(pos,{y=colbox[2]+0.01}), pos
|
||||
end
|
||||
|
||||
function automobiles_lib.get_node_pos(pos)
|
||||
return {
|
||||
x=floor(pos.x+0.5),
|
||||
y=floor(pos.y+0.5),
|
||||
z=floor(pos.z+0.5),
|
||||
}
|
||||
end
|
||||
|
||||
function automobiles_lib.nodeatpos(pos)
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node then return minetest.registered_nodes[node.name] end
|
||||
end
|
||||
|
||||
function automobiles_lib.minmax(v,m)
|
||||
return min(abs(v),m)*sign(v)
|
||||
end
|
||||
|
||||
function automobiles_lib.set_acceleration(thing,vec,limit)
|
||||
limit = limit or 100
|
||||
if type(thing) == 'table' then thing=thing.object end
|
||||
vec.x=automobiles_lib.minmax(vec.x,limit)
|
||||
vec.y=automobiles_lib.minmax(vec.y,limit)
|
||||
vec.z=automobiles_lib.minmax(vec.z,limit)
|
||||
|
||||
thing:set_acceleration(vec)
|
||||
end
|
||||
|
||||
function automobiles_lib.actfunc(self, staticdata, dtime_s)
|
||||
|
||||
self.logic = self.logic or self.brainfunc
|
||||
self.physics = self.physics or automobiles_lib.physics
|
||||
|
||||
self.lqueue = {}
|
||||
self.hqueue = {}
|
||||
self.nearby_objects = {}
|
||||
self.nearby_players = {}
|
||||
self.pos_history = {}
|
||||
self.path_dir = 1
|
||||
self.time_total = 0
|
||||
self.water_drag = self.water_drag or 1
|
||||
|
||||
local sdata = minetest.deserialize(staticdata)
|
||||
if sdata then
|
||||
for k,v in pairs(sdata) do
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if self.textures==nil then
|
||||
local prop_tex = self.object:get_properties().textures
|
||||
if prop_tex then self.textures=prop_tex end
|
||||
end
|
||||
|
||||
if not self.memory then -- this is the initial activation
|
||||
self.memory = {}
|
||||
|
||||
-- texture variation
|
||||
if #self.textures > 1 then self.texture_no = random(#self.textures) end
|
||||
end
|
||||
|
||||
if self.timeout and ((self.timeout>0 and dtime_s > self.timeout and next(self.memory)==nil) or
|
||||
(self.timeout<0 and dtime_s > abs(self.timeout))) then
|
||||
self.object:remove()
|
||||
end
|
||||
|
||||
-- apply texture
|
||||
if self.textures and self.texture_no then
|
||||
local props = {}
|
||||
props.textures = {self.textures[self.texture_no]}
|
||||
self.object:set_properties(props)
|
||||
end
|
||||
|
||||
--hp
|
||||
self.max_hp = self.max_hp or 10
|
||||
self.hp = self.hp or self.max_hp
|
||||
--armor
|
||||
if type(self.armor_groups) ~= 'table' then
|
||||
self.armor_groups={}
|
||||
end
|
||||
self.armor_groups.immortal = 1
|
||||
self.object:set_armor_groups(self.armor_groups)
|
||||
|
||||
self.buoyancy = self.buoyancy or 0
|
||||
self.oxygen = self.oxygen or self.lung_capacity
|
||||
self.lastvelocity = {x=0,y=0,z=0}
|
||||
end
|
||||
|
||||
function automobiles_lib.get_box_height(self)
|
||||
if type(self) == 'table' then self = self.object end
|
||||
local colbox = self:get_properties().collisionbox
|
||||
local height = 0.1
|
||||
if colbox then height = colbox[5]-colbox[2] end
|
||||
|
||||
return height > 0 and height or 0.1
|
||||
end
|
||||
|
||||
function automobiles_lib.stepfunc(self,dtime,colinfo)
|
||||
self.dtime = math.min(dtime,0.2)
|
||||
self.colinfo = colinfo
|
||||
self.height = automobiles_lib.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
|
||||
@@ -0,0 +1,8 @@
|
||||
# all settings for client menu or in minetest.conf on servers
|
||||
|
||||
|
||||
# make cars collectable
|
||||
collect_automobiles (Collect vehicles to inventory) bool true
|
||||
noob_mode (make automobiles climb entire blocks) bool false
|
||||
motorcycle_anim_mode (enable motorcycle pilot animations) bool true
|
||||
speed_limit (limit all vehicles to a speed - useful for lag servers) int 500
|
||||
@@ -0,0 +1 @@
|
||||
horn - SoundEffects by : https://youtu.be/FQc5zRy6wBU
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 382 B |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 246 B |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 609 B |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.8 KiB |