From 125809583a4999ce7d9f0fdd3529ccfed3cf8f5d Mon Sep 17 00:00:00 2001 From: Alexsandro Percy Date: Sat, 15 Feb 2025 09:12:56 -0300 Subject: [PATCH] improved autopilot --- lib_planes/control.lua | 32 +++++++++---- lib_planes/entities.lua | 96 +++++++++++++++++++++------------------ lib_planes/forms.lua | 2 + sounds/airutils_beep.ogg | Bin 0 -> 4733 bytes 4 files changed, 76 insertions(+), 54 deletions(-) create mode 100644 sounds/airutils_beep.ogg diff --git a/lib_planes/control.lua b/lib_planes/control.lua index 0ef58da..f9815d1 100755 --- a/lib_planes/control.lua +++ b/lib_planes/control.lua @@ -243,7 +243,9 @@ function airutils.autopilot(self, dtime, hull_direction, longit_speed, accel, cu if not self._have_auto_pilot then return end - local max_attack_angle = 1.8 + local min_attack_angle = self._wing_angle_of_attack or 1.0 + local flap = self._wing_angle_extra_flaps or 2 + local max_attack_angle = min_attack_angle + flap --1.8 --climb local velocity = self.object:get_velocity() @@ -254,14 +256,15 @@ function airutils.autopilot(self, dtime, hull_direction, longit_speed, accel, cu end self._acceleration = 0 + local climb_rate_min = 0.2 + local factor = math.abs(climb_rate * 0.1) if self._engine_running then --engine acceleration calc local engineacc = (self._power_lever * self._max_engine_acc) / 100; --self.engine:set_animation_frame_speed(60 + self._power_lever) - local factor = math.abs(climb_rate * 0.1) --increase power lever - if climb_rate > 0.2 then + if climb_rate > climb_rate_min then airutils.powerAdjust(self, dtime, factor, -1) end --decrease power lever @@ -271,7 +274,7 @@ function airutils.autopilot(self, dtime, hull_direction, longit_speed, accel, cu --do not exceed local max_speed = self._max_speed if longit_speed > max_speed then - engineacc = engineacc - (longit_speed-max_speed) + engineacc = 0 if engineacc < 0 then engineacc = 0 end end self._acceleration = engineacc @@ -280,20 +283,29 @@ function airutils.autopilot(self, dtime, hull_direction, longit_speed, accel, cu local hull_acc = vector.multiply(hull_direction,self._acceleration) retval_accel=vector.add(retval_accel,hull_acc) - --pitch - if self._angle_of_attack > max_attack_angle then - airutils.set_autopilot_pitch(self, 1, dtime) - elseif self._angle_of_attack < max_attack_angle then + --decrease power lever + if climb_rate < 0 then airutils.set_autopilot_pitch(self, -1, dtime) + --core.chat_send_all("cabrando: "..dump(climb_rate)) + elseif climb_rate > climb_rate_min then + airutils.set_autopilot_pitch(self, 1, dtime) + --core.chat_send_all("picando: "..dump(climb_rate)) end + --pitch + --[[if self._angle_of_attack > max_attack_angle then + airutils.set_autopilot_pitch(self, -1, dtime) + elseif self._angle_of_attack < min_attack_angle then + airutils.set_autopilot_pitch(self, 1, dtime) + end]]-- + -- yaw airutils.set_yaw(self, 0, dtime) - if longit_speed > 0 then + if longit_speed > (self._min_speed or 0) then airutils.rudder_auto_correction(self, longit_speed, dtime) if airutils.elevator_auto_correction then - self._elevator_angle = airutils.elevator_auto_correction(self, longit_speed, self.dtime, self._max_speed, self._elevator_angle, self._elevator_limit, airutils.ideal_step, 500) + --self._elevator_angle = airutils.elevator_auto_correction(self, longit_speed, self.dtime, self._max_speed, self._elevator_angle, self._elevator_limit, airutils.ideal_step, 500) end end diff --git a/lib_planes/entities.lua b/lib_planes/entities.lua index cb1a70d..b7d7703 100644 --- a/lib_planes/entities.lua +++ b/lib_planes/entities.lua @@ -1,4 +1,4 @@ -dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "global_definitions.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "global_definitions.lua") local S = airutils.S @@ -7,7 +7,7 @@ local function lib_change_color(self, colstr) end function airutils.get_staticdata(self) -- unloaded/unloads ... is now saved - return minetest.serialize({ + return core.serialize({ --stored_sound_handle = self.sound_handle, stored_energy = self._energy or 0, stored_owner = self.owner or "", @@ -32,7 +32,7 @@ function airutils.on_deactivate(self) airutils.save_inventory(self) local pos = self.object:get_pos() if airutils.debug_log then - minetest.log("action","deactivating: "..self._vehicle_name.." from "..self.owner.." at position "..math.floor(pos.x)..","..math.floor(pos.y)..","..math.floor(pos.z)) + core.log("action","deactivating: "..self._vehicle_name.." from "..self.owner.." at position "..math.floor(pos.x)..","..math.floor(pos.y)..","..math.floor(pos.z)) end end @@ -43,7 +43,7 @@ function airutils.on_activate(self, staticdata, dtime_s) self._flap = false if staticdata ~= "" and staticdata ~= nil then - local data = minetest.deserialize(staticdata) or {} + local data = core.deserialize(staticdata) or {} self._energy = data.stored_energy or 0 self.owner = data.stored_owner or "" self.hp_max = data.stored_hp or 10 @@ -66,7 +66,7 @@ function airutils.on_activate(self, staticdata, dtime_s) -- o macete aqui eh inicializar mesmo que não exista no escopo da entity self._vehicle_custom_data = {} --initialize it end - --minetest.debug("loaded: ", self._energy) + --core.debug("loaded: ", self._energy) if self._engine_running then self._last_applied_power = -1 --signal to start end @@ -84,7 +84,7 @@ function airutils.on_activate(self, staticdata, dtime_s) self._pitch = 0 if airutils.debug_log then - minetest.log("action","activating: "..self._vehicle_name.." from "..self.owner.." at position "..math.floor(pos.x)..","..math.floor(pos.y)..","..math.floor(pos.z)) + core.log("action","activating: "..self._vehicle_name.." from "..self.owner.." at position "..math.floor(pos.x)..","..math.floor(pos.y)..","..math.floor(pos.z)) end if self._register_parts_method then @@ -109,7 +109,7 @@ function airutils.on_activate(self, staticdata, dtime_s) local inv = nil if self._inv_id then - inv = minetest.get_inventory({type = "detached", name = self._inv_id}) + inv = core.get_inventory({type = "detached", name = self._inv_id}) end -- if the game was closed the inventories have to be made anew, instead of just reattached if not inv then @@ -230,9 +230,9 @@ function airutils.logic(self) if self._last_time_command > 1 then self._last_time_command = 1 end local player = nil - if self.driver_name then player = minetest.get_player_by_name(self.driver_name) end + if self.driver_name then player = core.get_player_by_name(self.driver_name) end local co_pilot = nil - if self.co_pilot and self._have_copilot then co_pilot = minetest.get_player_by_name(self.co_pilot) end + if self.co_pilot and self._have_copilot then co_pilot = core.get_player_by_name(self.co_pilot) end --test collision airutils.testImpact(self, velocity, curr_pos) @@ -267,14 +267,22 @@ function airutils.logic(self) if ctrl.sneak or ctrl.jump or ctrl.up or ctrl.down or ctrl.right or ctrl.left then self._last_time_command = 0 self._autopilot = false - minetest.chat_send_player(self.driver_name,S(" >>> Autopilot deactivated")) + core.chat_send_player(self.driver_name,S(" >>> Autopilot deactivated")) end else if ctrl.sneak == true and ctrl.jump == true and self._have_auto_pilot then self._last_time_command = 0 self._autopilot = true self._auto_pilot_altitude = curr_pos.y - minetest.chat_send_player(self.driver_name,core.colorize('#00ff00', S(" >>> Autopilot on"))) + core.chat_send_player(self.driver_name,core.colorize('#00ff00', S(" >>> Autopilot activated"))) + if self.driver_name then + core.sound_play("airutils_beep", { + to_player = self.driver_name, + gain = 1.0, + fade = 0.0, + pitch = 0.7, + }, true) + end end end end @@ -290,21 +298,21 @@ function airutils.logic(self) newroll = math.floor(newroll/360) newroll = newroll * 360 - local hull_direction = airutils.rot_to_dir(rotation) --minetest.yaw_to_dir(yaw) + local hull_direction = airutils.rot_to_dir(rotation) --core.yaw_to_dir(yaw) local nhdir = {x=hull_direction.z,y=0,z=-hull_direction.x} -- lateral unit vector local longit_speed = vector.dot(velocity,hull_direction) if extern_ent then if extern_ent.curr_speed then longit_speed = extern_ent.curr_speed end - --minetest.chat_send_all(dump(longit_speed)) + --core.chat_send_all(dump(longit_speed)) end self._longit_speed = longit_speed local longit_drag = vector.multiply(hull_direction,longit_speed* longit_speed*self._longit_drag_factor*-1*airutils.sign(longit_speed)) local later_speed = airutils.dot(velocity,nhdir) - --minetest.chat_send_all('later_speed: '.. later_speed) + --core.chat_send_all('later_speed: '.. later_speed) local later_drag = vector.multiply(nhdir,later_speed*later_speed* self._later_drag_factor*-1*airutils.sign(later_speed)) local accel = vector.add(longit_drag,later_drag) @@ -319,15 +327,15 @@ function airutils.logic(self) if not extern_ent.on_rightclick then local touch_point = (self.initial_properties.collisionbox[2])-0.5 local node_bellow = airutils.nodeatpos(airutils.pos_shift(curr_pos,{y=touch_point})) - --minetest.chat_send_all(dump(node_bellow.drawtype)) + --core.chat_send_all(dump(node_bellow.drawtype)) if (node_bellow and node_bellow.drawtype ~= 'airlike') then is_flying = false end end end end - --minetest.chat_send_all(dump(is_flying)) - --if is_flying then minetest.chat_send_all('is flying') end + --core.chat_send_all(dump(is_flying)) + --if is_flying then core.chat_send_all('is flying') end local is_attached = airutils.checkAttach(self, player) if self._indicated_speed == nil then self._indicated_speed = 0 end @@ -368,9 +376,9 @@ function airutils.logic(self) else if self._smoke_spawner and not self._smoke_semaphore then self._smoke_semaphore = 1 --to set it only one time - minetest.after(5, function() + core.after(5, function() if self._smoke_spawner then - minetest.delete_particlespawner(self._smoke_spawner) + core.delete_particlespawner(self._smoke_spawner) self._smoke_spawner = nil self._smoke_semaphore = nil end @@ -417,7 +425,7 @@ function airutils.logic(self) if longit_speed < (self._min_speed+0.5) and climb_rate < -1.5 and is_flying then --[[ if player and self.driver_name then - minetest.chat_send_player(self.driver_name,core.colorize('#ff0000', " >>> STALL")) + core.chat_send_player(self.driver_name,core.colorize('#ff0000', " >>> STALL")) end ]]-- self._elevator_angle = 0 @@ -448,7 +456,7 @@ function airutils.logic(self) end end - --minetest.chat_send_all(self._angle_of_attack) + --core.chat_send_all(self._angle_of_attack) -- adjust pitch at ground if math.abs(longit_speed) > self._tail_lift_min_speed and is_flying == false then @@ -472,7 +480,7 @@ function airutils.logic(self) local delta = 0.002 if is_flying then local roll_reference = newyaw - local sdir = minetest.yaw_to_dir(roll_reference) + local sdir = core.yaw_to_dir(roll_reference) local snormal = {x=sdir.z,y=0,z=-sdir.x} -- rightside, dot is negative local prsr = airutils.dot(snormal,nhdir) local rollfactor = -90 @@ -482,7 +490,7 @@ function airutils.logic(self) --[[local rollRotation = -self._rudder_angle * 0.1 newroll = rollRotation]]-- - --minetest.chat_send_all('newroll: '.. newroll) + --core.chat_send_all('newroll: '.. newroll) else delta = 0.2 if roll > 0 then @@ -541,7 +549,7 @@ function airutils.logic(self) local h_vel_compensation = ((ref_speed * 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 - --minetest.chat_send_all(h_vel_compensation) + --core.chat_send_all(h_vel_compensation) newpitch = newpitch + (velocity.y * math.rad(max_pitch - h_vel_compensation)) if airutils.use_water_particles == true and airutils.add_splash and self._splash_x_position and self.buoyancy then @@ -571,7 +579,7 @@ function airutils.logic(self) local scale = (longit_speed*1)/takeoff_speed --get a scale of current longit speed relative to takeoff speed if scale == nil then scale = 0 end --lets avoid any nil factorized_longit_speed = airutils.quadBezier(scale, self._min_speed, longit_speed, longit_speed) --here the magic happens using a bezier curve - --minetest.chat_send_all("factor: " .. factorized_longit_speed .. " - longit: " .. longit_speed .. " - scale: " .. scale) + --core.chat_send_all("factor: " .. factorized_longit_speed .. " - longit: " .. longit_speed .. " - scale: " .. scale) if factorized_longit_speed < 0 then factorized_longit_speed = 0 end --lets avoid negative numbers if factorized_longit_speed == nil then factorized_longit_speed = longit_speed end --and nil numbers end @@ -636,7 +644,7 @@ function airutils.logic(self) --self.object:get_luaentity() --hack way to fix jitter on climb --GAUGES - --minetest.chat_send_all('rate '.. climb_rate) + --core.chat_send_all('rate '.. climb_rate) local climb_angle = airutils.get_gauge_angle(climb_rate) self._climb_rate = climb_rate @@ -667,7 +675,7 @@ function airutils.logic(self) end if player and self._use_camera_relocation then - --minetest.chat_send_all(dump(newroll)) + --core.chat_send_all(dump(newroll)) local new_eye_offset = airutils.camera_reposition(player, newpitch, newroll) player:set_eye_offset(new_eye_offset, {x = 0, y = 1, z = -30}) end @@ -678,7 +686,7 @@ function airutils.logic(self) if (longit_speed / 2) > self._max_speed and self._flap == true then if is_attached and self.driver_name then - minetest.chat_send_player(self.driver_name, core.colorize('#ff0000', S(" >>> Flaps retracted due for overspeed"))) + core.chat_send_player(self.driver_name, core.colorize('#ff0000', S(" >>> Flaps retracted due for overspeed"))) end self._flap = false end @@ -721,7 +729,7 @@ function airutils.on_punch(self, puncher, ttime, toolcaps, dir, damage) if (puncher:is_player()) then name = puncher:get_player_name() ppos = puncher:get_pos() - if (minetest.is_protected(ppos, name) and + if (core.is_protected(ppos, name) and airutils.protect_in_areas) then return end @@ -738,7 +746,7 @@ function airutils.on_punch(self, puncher, ttime, toolcaps, dir, damage) damage_vehicle(self, toolcaps, ttime, damage) end - local is_admin = minetest.check_player_privs(puncher, {server=true}) + local is_admin = core.check_player_privs(puncher, {server=true}) if self.owner == nil then self.owner = name end @@ -746,7 +754,7 @@ function airutils.on_punch(self, puncher, ttime, toolcaps, dir, damage) if is_admin == false then return end end - if is_admin == false and minetest.check_player_privs(puncher, {protection_bypass=false}) then + if is_admin == false and core.check_player_privs(puncher, {protection_bypass=false}) then 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 @@ -789,7 +797,7 @@ function airutils.on_punch(self, puncher, ttime, toolcaps, dir, damage) if self.hp_max > self._max_plane_hp then self.hp_max = self._max_plane_hp end airutils.setText(self, self._vehicle_name) else - minetest.chat_send_player(puncher:get_player_name(), S("You need steel ingots in your inventory to perform this repair.")) + core.chat_send_player(puncher:get_player_name(), S("You need steel ingots in your inventory to perform this repair.")) end end return @@ -803,7 +811,7 @@ function airutils.on_punch(self, puncher, ttime, toolcaps, dir, damage) --airutils.hurt(self,toolcaps.damage_groups.fleshy - 1) --airutils.make_sound(self,'hit') damage_vehicle(self, toolcaps, ttime, damage) - minetest.sound_play(self._collision_sound, { + core.sound_play(self._collision_sound, { object = self.object, max_hear_distance = 5, gain = 1.0, @@ -852,10 +860,10 @@ local function get_vehicle(self, player) 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) + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack) end else - minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack) + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack) end airutils.seats_destroy(self) @@ -889,11 +897,11 @@ function airutils.on_rightclick(self, clicker) copilot_name = self.co_pilot end - --minetest.chat_send_all(dump(self.driver_name)) + --core.chat_send_all(dump(self.driver_name)) local is_under_water = airutils.check_is_under_water(self.object) - --minetest.chat_send_all('name '.. dump(name) .. ' - pilot: ' .. dump(self.driver_name) .. ' - pax: ' .. dump(copilot_name)) + --core.chat_send_all('name '.. dump(name) .. ' - pilot: ' .. dump(self.driver_name) .. ' - pax: ' .. dump(copilot_name)) --========================= -- form to pilot --========================= @@ -911,7 +919,7 @@ function airutils.on_rightclick(self, clicker) if itmstck then item_name = itmstck:get_name() end --adf program function if (item_name == "compassgps:cgpsmap_marked") then - local meta = minetest.deserialize(itmstck:get_metadata()) + local meta = core.deserialize(itmstck:get_metadata()) if meta then self._adf_destiny = {x=meta["x"], z=meta["z"]} end @@ -946,7 +954,7 @@ function airutils.on_rightclick(self, clicker) -- attach pilot --========================= elseif not self.driver_name and not self._autoflymode then - if self.owner == name or minetest.check_player_privs(clicker, {protection_bypass=true}) then + if self.owner == name or core.check_player_privs(clicker, {protection_bypass=true}) then local itmstck=clicker:get_wielded_item() @@ -969,7 +977,7 @@ function airutils.on_rightclick(self, clicker) for i = max_seats,1,-1 do if self._passengers[i] and self._passengers[i] ~= "" then - local passenger = minetest.get_player_by_name(self._passengers[i]) + local passenger = core.get_player_by_name(self._passengers[i]) if passenger then airutils.dettach_pax(self, passenger) end end end @@ -993,7 +1001,7 @@ function airutils.on_rightclick(self, clicker) end else airutils.dettach_pax(self, clicker) - minetest.chat_send_player(name, core.colorize('#ff0000', S(" >>> You aren't the owner of this "..self.infotext.."."))) + core.chat_send_player(name, core.colorize('#ff0000', S(" >>> You aren't the owner of this "..self.infotext.."."))) end --========================= @@ -1002,7 +1010,7 @@ function airutils.on_rightclick(self, clicker) elseif self.driver_name ~= nil or self._autoflymode == true then local d_name = self.driver_name if d_name == nil then d_name = "" end - local player = minetest.get_player_by_name(d_name) + local player = core.get_player_by_name(d_name) if player or self._autoflymode == true then is_attached = airutils.check_passenger_is_attached(self, name) @@ -1015,10 +1023,10 @@ function airutils.on_rightclick(self, clicker) end else - minetest.chat_send_player(clicker:get_player_name(), message) + core.chat_send_player(clicker:get_player_name(), message) end else - minetest.chat_send_player(clicker:get_player_name(), message) + core.chat_send_player(clicker:get_player_name(), message) end end diff --git a/lib_planes/forms.lua b/lib_planes/forms.lua index 3c225f5..56f1b28 100644 --- a/lib_planes/forms.lua +++ b/lib_planes/forms.lua @@ -382,8 +382,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.turn_auto_pilot_on then if ent._autopilot == true then ent._autopilot = false + core.chat_send_player(self.driver_name,S(" >>> Autopilot deactivated")) else ent._autopilot = true + core.chat_send_player(self.driver_name,core.colorize('#00ff00', S(" >>> Autopilot activated"))) end end if fields.manual then diff --git a/sounds/airutils_beep.ogg b/sounds/airutils_beep.ogg new file mode 100644 index 0000000000000000000000000000000000000000..645cbce32ccbf3284345b28c1050e68f1c400e5c GIT binary patch literal 4733 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 YGsvGXi~O@V;@!(E*F@!2Peuy(8?HXQA^-pY literal 0 HcmV?d00001