123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- --localize global functions
- local vector_new = vector.new
- local math_hypot = math.hypot
- local atan = math.atan
- local cos = math.cos
- local sin = math.sin
- local abs = math.abs
- local pi = math.pi
- local min = math.min
- --max speed settings
- local max_ballooning_vertical_speed = 1
- local max_ballooning_horizontal_speed = 3
- local function is_water_is_air(pos)
- local node_name = minetest.get_node(pos).name
- return minetest.get_item_group(node_name, "water") > 0,
- node_name == "air"
- end
- local function get_vertical_acceleration(self)
- local heat = self.heat
- local vel_y = self.object:getvelocity().y
- local acc_candidate = heat / 1000 - 0.5
-
- --enforce max speed
- if vel_y > max_ballooning_vertical_speed
- and acc_candidate * vel_y > 0
- then
- return 0
- else
- return acc_candidate
- end
- end
- --if balloon is submerged
- local function float_up(self, vel)
- self.submerged = true
- vel.y = 1
- return vel
- end
- local function swim(self, vel)
- --allow controls, allow up
- local pos = self.object:get_pos()
- --keep y constant or rising
- local acc_y = get_vertical_acceleration(self)
-
- if self.submerged or acc_y < 0
- then
- self.submerged = false
- vel.y = 0
- return 0, vel
- else
- return acc_y, vel
- end
- end
- local tau = pi * 2
- --returns the radian equivalent of a in the range [0, tau)
- local function to_radian(a)
- if a < 0
- then
- return to_radian(a + tau)
- elseif a >= tau
- then
- return to_radian(a - tau)
- else
- return a
- end
- end
- --decides which is the shortest way to rotate towards where the player is looking
- local function get_rotate_direction(a, b)
- return to_radian(a - b) < to_radian(b - a)
- end
- --rotates the balloon towards where the player is looking
- local pi_192ths = pi / 192 --radians to turn each step
- local function rotate(self, player)
- -- + pi so it finishes rotating when looking towards where the player is looking
- local player_yaw = player:get_look_horizontal() + pi
- local self_yaw = self.object:getyaw()
-
- if get_rotate_direction(player_yaw, self_yaw)
- then
- self.object:setyaw(to_radian(self_yaw - pi_192ths))
- else
- self.object:setyaw(to_radian(self_yaw + pi_192ths))
- end
- end
- --takes wasd and turns it into a 2d vector
- local pi_halves = pi / 2
- function get_direction(right, left, up, down)
- local inline, cross = 0, 0
- local move = right or left or up or down
- if left then cross = 1 end
- if right then cross = cross - 1 end
- if up then inline = 1 end
- if down then inline = inline - 1 end
- local arg
- if inline < 0
- then
- return atan(cross / inline) + pi, move
- elseif inline > 0
- then
- return atan(cross / inline), move
- else
- return pi_halves * cross, move
- end
- end
- --[[
- space to rotate where the player is looking
- wasd to apply acceleration
- shift to let out hot air, cooling the balloon
- ]]
- local function handle_control(self, vel)
- if not self.pilot
- then
- return 0, 0
- end
- local player = minetest.get_player_by_name(self.pilot)
- if not player --player left, balloon should get deleted
- then
- return 0, 0
- end
- local control = player:get_player_control()
- if control.sneak --lowering heat quickly
- then
- local heat = self.heat - 30
- if heat < 0
- then
- self.heat = 0
- else
- self.heat = heat
- end
- end
-
- if control.jump --rotate towards player yaw
- then
- rotate(self, player)
- end
-
- --taking direction from get_direction
- --and turning it into radians.
- --if max speed is reached, only acceleration in the opposite direction is applied.
- local dir_radians, move = get_direction(control.right, control.left, control.up, control.down)
- if move and math_hypot(vel.x, vel.z)
- then
- dir_radians = dir_radians + player:get_look_horizontal()
- local x, z = -sin(dir_radians), cos(dir_radians)
- if math_hypot(vel.x, vel.z) > max_ballooning_horizontal_speed
- then
- if x * vel.x > 0
- then
- x = 0
- end
- if z * vel.z > 0
- then
- z = 0
- end
- end
- return x, z
- end
- return 0, 0
- end
- --[[handle movement in different cases
- movement restrictions:
- -on ground: only vertical movement
- -on water: free movement, though vertical only if up
- -submerged: free movement, vertical goes up automatically
- -in air: completely free movement
- ]]
- return function(self)
- local pos_in = self.object:get_pos()
- local pos_under = vector_new(pos_in.x, pos_in.y - 0.1, pos_in.z)
- local on_water, in_air = is_water_is_air(pos_under)
- local acc = vector_new(0, 0, 0)
- local vel = self.object:getvelocity()
-
-
- if is_water_is_air(pos_in) --if submerged
- then
- vel = float_up(self, vel)
- acc.x, acc.z = handle_control(self, vel)
- self.object:setvelocity(vel)
- elseif on_water --if on water
- then
- acc.y, vel = swim(self, vel)
- self.object:setvelocity(vel)
- acc.x, acc.z = handle_control(self, vel)
- elseif in_air
- then
- --allow controls and height change
- acc.y = get_vertical_acceleration(self)
- acc.x, acc.z = handle_control(self, vel)
- else --if on ground
- --only allow height change
- acc.y = get_vertical_acceleration(self)
- vel.x = 0
- vel.z = 0
- self.object:setvelocity(vel)
- end
- self.object:setacceleration(acc)
- end
|