2021-12-07 02:03:57 +00:00

568 lines
18 KiB
Lua

-- Invector, License MIT, Author Jordach
-- Fake third person but works like third person
local default_eye_offset = vector.new(0,0,0)
local ground_eye_offset = vector.new(0,1.7,-30)
local ground_eye_offset_3r = vector.new(0,-10,0)
local player_relative = vector.new(0,0,0)
local player_rot = vector.new(0,0,0)
-- Update rate, don't touch me
local tick_speed = 0.03
-- Bone rotation things;
local kart_root_pos = vector.new(0,0,0)
local kart_root_rot = vector.new(0,0,0)
-- Particle effect things
local front_left_tyre_pos = vector.new(0,0,0)
local front_right_tyre_pos = vector.new(0,0,0)
local rear_left_tyre_pos_min = vector.new(-0.55,0.1,-0.7)
local rear_left_tyre_pos_max = vector.new(-0.4,0.2,-0.7)
local rear_right_tyre_pos_min = vector.new(0.4,0.1,-0.7)
local rear_right_tyre_pos_max = vector.new(0.55,0.2,-0.7)
local rear_left_exhaust_pos_min = vector.new(-0.35,0.65,-1.05)
local rear_left_exhaust_pos_max = vector.new(-0.4, 0.65,-1.05)
local rear_right_exhaust_pos_min = vector.new(0.35, 0.65,-1.05)
local rear_right_exhaust_pos_max = vector.new(0.4, 0.65,-1.05)
-- Handling specs
local turning_radius = 0.0472665
local drifting_radius = turning_radius * 1.75
local drifting_radius_lesser = drifting_radius * 0.5
local drifting_radius_greater = drifting_radius * 1.5
local reverse_radius = 0.0204533
-- Speed specs
local max_speed_boost = 13
local max_speed_norm = 7
local forwards_accel = 3
local reverse_accel = -0.55
local braking_factor = 0.75
-- Friction settings
local friction_track = 0.92
local friction_off_track = 0.50
local friction_air = 0
-- Drift time needed to boost
local small_drift_time = 1
local med_drift_time = 2
local big_drift_time = 3
-- Boost specs
local small_boost_time = 0.25
local med_boost_time = 0.5
local big_boost_time = 1.25
local drift_particle_def = {
amount = 50,
vertical = false,
collisiondetection = false,
minexptime = 0.25,
maxexptime = 0.5,
glow = 14
}
local exhause_particle_def = {
amount = 50,
vertical = false,
collisiondetection = false,
minexptime = 0.15,
maxexptime = 0.35
}
local kart = {
visual = "mesh",
mesh = "sam2.b3d",
use_texture_alpha = true,
backface_culling = false,
makes_footstep_sound = true,
textures = {
"sam2_kart_neo.png",
"sam2_skin.png",
},
visual_size = {x=1, y=1},
collisionbox = {-0.61, 0.0, -0.61, 0.61, 1.4, 0.61},
physical = true,
collide_with_objects = true,
pointable = false,
stepheight = 0.5/16,
-- Custom fields:
_timer = 0,
_boost_timer = 0,
_drift_timer = 0,
_stun_timer = 0,
_immune_timer = 0,
_xvel = 0,
_zvel = 0,
_dvel = 0,
_yvel = 0,
_last_cam_offset = 0,
_is_drifting = 0, -- 1 = left, -1 = right
_drift_level = 0, -- 0 = not drifting, 1 = small, 2 = medium, 3 = big / infinite
_boost_type = 0, -- 0 = regular smoke, 1 = small boost, 2 = medium boost, 3 = big boost/boost pad
_is_kart = true,
_is_ai_capable = false,
_held_item = nil,
_held_item_uses = nil,
_shields = true, -- Can take an extra hit without being stunned
_position = -1, -- Set at race start and during a race
_course_node = -1, -- Similar purpose to _position
_track_sector = -1, -- Lap / track sector for non circuits
_racer_id = -1, -- Set on game startup since it's likely singleplayer.
-- Particle ID holders
_rear_left_tyre_spawner = nil,
_rear_right_tyre_spawner = nil,
_exhaust_left_spawner = nil,
_exhaust_right_spawner = nil,
_deo = default_eye_offset,
_geo = ground_eye_offset,
_geo3r = ground_eye_offset_3r,
_prel = player_relative,
_prot = player_rot
}
function kart:on_step(dtime)
-- Avoid the kart logic over or underscaling things due to framerate variability.
local tick_scaling = solarsail.util.functions.remap(dtime, tick_speed/4, tick_speed*4, 0.25, 4)
-- Add kart stun
if self._stun_timer > 0 then
self._stun_timer = self._stun_timer - dtime
end
if self._immune_timer > 0 then
self._immune_timer = self._immune_timer - dtime
end
if self.attached_player ~= nil and self._stun_timer <= 0 then
local control = solarsail.controls.player[self.attached_player:get_player_name()]
if control == nil then return end
-- Allow exiting the kart while in creative mode to test changes;
if control.aux1 and minetest.settings:get_bool("creative_mode") then
self.attached_player:set_detach()
self.attached_player:hud_set_flags({
crosshair = true,
hotbar = true,
healthbar = true,
wielditem = true,
breathbar = true
})
self.attached_player:set_eye_offset()
self.attached_player = nil
return
end
local ratio_tick = solarsail.util.functions.remap(tick_scaling, 0.25, 4, 0, 1)
local velocity = self.object:get_velocity()
local accel = self.object:get_acceleration()
local rotation = self.object:get_yaw()
local cam_rot_offset = 0
local yaw_offset = 0
local last_rot_offsets = self.object:get_rotation()
local kart_rot_offsets = vector.new(0,0,0)
-- Identify the node under the kart for physics and boost/item detection
local kart_phys_pos = self.object:get_pos()
local kart_node_pos = table.copy(kart_phys_pos)
kart_node_pos.y = kart_node_pos.y - 1.5
local node_detector = Raycast(kart_phys_pos, kart_node_pos, false, false)
local node_pos
for pointed in node_detector do
if pointed == nil then
else
if pointed.type == "node" then
node_pos = table.copy(pointed.under)
break
end
end
end
local node_data = minetest.get_node_or_nil(node_pos)
local node = minetest.registered_nodes[node_data.name]
if node.groups.booster ~= nil then
if self._boost_timer <= 0 then
self._boost_timer = node.groups.booster
self._boost_type = 3
end
end
if node.groups.item ~= nil then
minetest.swap_node(node_pos, {name=node._swap_to})
local timer = minetest.get_node_timer(node_pos)
timer:start(3)
end
-- Handle node frictive types here;
-- Handle friction on ground
if velocity.y == 0 then
local frictive = 0
if node.groups.track == nil then
frictive = friction_off_track
else
frictive = friction_track
end
self._dvel = solarsail.util.functions.lerp(self._dvel, self._dvel * (frictive * tick_speed), ratio_tick)
else -- In air
self._dvel = solarsail.util.functions.lerp(self._dvel, self._dvel * (friction_air * tick_speed), ratio_tick)
end
-- Round down numbers when percentages exponentialise movement:
if self._dvel > 0 and self._dvel < 0.2 then
self._dvel = 0
elseif self._dvel < 0 and self._dvel > -0.2 then
self._dvel = 0
end
-- Handle controls;
-- Accel braking;
if control.up or self._boost_timer > 0 then
local boost_multi = 1
if self._boost_timer > 0 then
boost_multi = 3
end
self._dvel = self._dvel + ((forwards_accel * boost_multi) * tick_scaling)
elseif control.down then
-- Reversing
local racc_div = 1
-- Make braking half as effective compared to reversing
if self._dvel > 0 then racc_div = 2 end
self._dvel = self._dvel + ((reverse_accel/racc_div) * tick_scaling)
end
-- Drifting;
if control.jump and self._dvel > 5.5 then
-- Direction of drifting
if self._is_drifting == 0 then
if control.left then
self._is_drifting = 1
elseif control.right then
self._is_drifting = -1
end
end
-- Increment timer for boost
self._drift_timer = self._drift_timer + dtime
-- Drift steering
if control.left then
if self._is_drifting == 1 then --Left
yaw_offset = drifting_radius_greater * tick_scaling
elseif self._is_drifting == -1 then
yaw_offset = -drifting_radius_lesser * tick_scaling
end
elseif control.right then
if self._is_drifting == 1 then --Left adds, right removes
yaw_offset = drifting_radius_lesser * tick_scaling
elseif self._is_drifting == -1 then
yaw_offset = -drifting_radius_greater * tick_scaling
end
else
if self._is_drifting == 1 then --Left
yaw_offset = drifting_radius * tick_scaling
elseif self._is_drifting == -1 then
yaw_offset = -drifting_radius * tick_scaling
end
end
elseif control.left then
-- Fowards
if self._dvel > 0.25 then
yaw_offset = turning_radius * tick_scaling
-- Reversing
elseif self._dvel < -0.1 then
yaw_offset = -reverse_radius * tick_scaling
end
elseif control.right then
-- Fowards
if self._dvel > 0.5 then
yaw_offset = -turning_radius * tick_scaling
-- Reversing
elseif self._dvel < -0.1 then
yaw_offset = reverse_radius * tick_scaling
end
end
-- Tick down the level of boost until it completes
if self._boost_timer > 0 then
self._boost_timer = self._boost_timer - dtime
elseif self._boost_type > 0 then
self._boost_type = 0
end
-- Give the boost
if not control.jump then
self._is_drifting = 0
-- Maximum boost
if self._drift_timer >= big_drift_time then
self._boost_timer = big_boost_time
self._boost_type = 3
self._drift_timer = 0
-- Medium boost
elseif self._drift_timer >= med_drift_time then
self._boost_timer = med_boost_time
self._boost_type = 2
self._drift_timer = 0
-- Small boost
elseif self._drift_timer >= small_drift_time then
self._boost_timer = small_boost_time
self._boost_type = 1
self._drift_timer = 0
end
end
-- Do not give a boost if falling under the drifting speed or while in air
if self._dvel <= 5 or velocity.y ~= 0 then
self._is_drifting = 0
self._drift_timer = 0
self._boost_type = 0
self._boost_timer = 0
end
-- Use Item/Boost;
if control.LMB then
elseif control.RMB then
end
local xv, zv = solarsail.util.functions.yaw_to_vec(rotation+yaw_offset, self._dvel, false)
local new_vel = vector.new(xv, 0, zv)
new_vel = vector.normalize(new_vel)
new_vel.y = velocity.y
-- Limit velocity based on boost status
if self._boost_timer > 0 then
if self._dvel > max_speed_boost then
self._dvel = max_speed_boost
elseif self._dvel < -max_speed_boost then
self._dvel = -max_speed_boost
end
else
if self._dvel > max_speed_norm then
self._dvel = max_speed_norm
elseif self._dvel < -max_speed_norm then
self._dvel = -max_speed_norm
end
end
-- Animation and camera handler:
local frange, fspeed, fblend, floop = self.object:get_animation()
local new_frames = {x=0, y=0}
local new_fps = 60
if control.up then
if control.left then
new_frames.x = 60
new_frames.y = 79
elseif control.right then
new_frames.x = 120
new_frames.y = 139
else
new_frames.x = 0
new_frames.y = 19
end
elseif control.down then -- Reversing
if self._dvel > 0.5 then
if control.left then
new_frames.x = 60
new_frames.y = 79
elseif control.right then
new_frames.x = 120
new_frames.y = 139
else
new_frames.x = 0
new_frames.y = 19
end
elseif self._dvel < -0.5 then
if control.left then
new_frames.x = 90
new_frames.y = 109
elseif control.right then
new_frames.x = 150
new_frames.y = 169
else
new_frames.x = 30
new_frames.y = 49
end
end
else -- Coming to a stop or idle
if self._dvel > 0.5 then
if control.left then
new_frames.x = 60
new_frames.y = 79
elseif control.right then
new_frames.x = 120
new_frames.y = 139
else
new_frames.x = 0
new_frames.y = 19
end
elseif self._dvel < -0.5 then
if control.left then
new_frames.x = 90
new_frames.y = 109
elseif control.right then
new_frames.x = 150
new_frames.y = 169
else
new_frames.x = 30
new_frames.y = 49
end
else -- Idle
if control.left then
new_frames.x = 79
new_frames.y = 79
elseif control.right then
new_frames.x = 139
new_frames.y = 139
else
new_frames.x = 19
new_frames.y = 19
end
end
end
-- Compare against frame table to avoid re-applying the animation
if frange.x ~= new_frames.x then
self.object:set_animation(new_frames, 60, 0.1, true)
end
-- Reversing camera mode if required
if self._dvel < -0.5 and control.down then
cam_rot_offset = 3.142
end
-- Particlespawner handler
-- Exhausts;
local exhaust_particle = table.copy(exhause_particle_def)
exhaust_particle.attached = self.object
exhaust_particle.minvel = vector.new((-new_vel.x/4)-0.05, (-new_vel.y/4)+0.5, (-new_vel.z/4)-0.25)
exhaust_particle.maxvel = vector.new((-new_vel.x/2)-0.15, (-new_vel.y/2)+1.2, (-new_vel.z/2)-0.5)
exhaust_particle.minpos = rear_left_exhaust_pos_min
exhaust_particle.maxpos = rear_left_exhaust_pos_max
if self._boost_type == 0 and self._exhaust_left_spawner == nil then
exhaust_particle.time = 0
exhaust_particle.glow = 0
exhaust_particle.node = {name = "invector:smoke_node"}
exhaust_particle.minsize = 1
exhaust_particle.maxsize = 1.15
self._exhaust_left_spawner = minetest.add_particlespawner(exhaust_particle)
exhaust_particle.minpos = rear_right_exhaust_pos_min
exhaust_particle.maxpos = rear_right_exhaust_pos_max
exhaust_particle.minvel.x = (-new_vel.x/4)+0.05
exhaust_particle.maxvel.x = (-new_vel.x/2)+0.15
self._exhaust_right_spawner = minetest.add_particlespawner(exhaust_particle)
elseif self._boost_type == 1 then
exhaust_particle.time = self._boost_timer
exhaust_particle.glow = 14
exhaust_particle.node = {name = "invector:boost_1_node"}
exhaust_particle.minsize = 1.15
exhaust_particle.maxsize = 1.4
minetest.add_particlespawner(exhaust_particle)
exhaust_particle.minpos = rear_right_exhaust_pos_min
exhaust_particle.maxpos = rear_right_exhaust_pos_max
exhaust_particle.minvel.x = (-new_vel.x/4)+0.05
exhaust_particle.maxvel.x = (-new_vel.x/2)+0.15
minetest.add_particlespawner(exhaust_particle)
elseif self._boost_type == 2 then
exhaust_particle.time = self._boost_timer
exhaust_particle.glow = 14
exhaust_particle.node = {name = "invector:boost_2_node"}
exhaust_particle.minsize = 1.4
exhaust_particle.maxsize = 1.65
minetest.add_particlespawner(exhaust_particle)
exhaust_particle.minpos = rear_right_exhaust_pos_min
exhaust_particle.maxpos = rear_right_exhaust_pos_max
exhaust_particle.minvel.x = (-new_vel.x/4)+0.05
exhaust_particle.maxvel.x = (-new_vel.x/2)+0.15
minetest.add_particlespawner(exhaust_particle)
elseif self._boost_type == 3 then
exhaust_particle.time = self._boost_timer
exhaust_particle.glow = 14
exhaust_particle.node = {name = "invector:boost_3_node"}
exhaust_particle.minsize = 1.65
exhaust_particle.maxsize = 1.95
minetest.add_particlespawner(exhaust_particle)
exhaust_particle.minpos = rear_right_exhaust_pos_min
exhaust_particle.maxpos = rear_right_exhaust_pos_max
exhaust_particle.minvel.x = (-new_vel.x/4)+0.05
exhaust_particle.maxvel.x = (-new_vel.x/2)+0.15
minetest.add_particlespawner(exhaust_particle)
end
-- Disable regular smoke when boosting
if self._boost_type > 0 and self._exhaust_left_spawner ~= nil then
minetest.delete_particlespawner(self._exhaust_left_spawner)
minetest.delete_particlespawner(self._exhaust_right_spawner)
self._exhaust_left_spawner = nil
self._exhaust_right_spawner = nil
end
-- Rear Tyres;
local drift_particle = table.copy(drift_particle_def)
drift_particle.attached = self.object
drift_particle.minvel = vector.new(-new_vel.x/2, (-new_vel.y/2)+0.5, -new_vel.z/2)
drift_particle.maxvel = vector.new(-new_vel.x, (-new_vel.y)+0.75, -new_vel.z)
if self._drift_timer >= big_drift_time and self._rear_left_tyre_spawner == nil then
self._drift_level = 3
drift_particle.time = 0
drift_particle.texture = "invector_drift_big.png"
drift_particle.minsize = 3
drift_particle.maxsize = 4
drift_particle.minpos = rear_left_tyre_pos_min
drift_particle.maxpos = rear_left_tyre_pos_max
self._rear_left_tyre_spawner = minetest.add_particlespawner(drift_particle)
drift_particle.minpos = rear_right_tyre_pos_min
drift_particle.maxpos = rear_right_tyre_pos_max
self._rear_right_tyre_spawner = minetest.add_particlespawner(drift_particle)
elseif self._drift_timer >= med_drift_time and self._drift_level == 1 then
self._drift_level = 2
drift_particle.time = 1.35
drift_particle.texture = "invector_drift_medium.png"
drift_particle.minsize = 2
drift_particle.maxsize = 3
drift_particle.minpos = rear_left_tyre_pos_min
drift_particle.maxpos = rear_left_tyre_pos_max
minetest.add_particlespawner(drift_particle)
drift_particle.minpos = rear_right_tyre_pos_min
drift_particle.maxpos = rear_right_tyre_pos_max
minetest.add_particlespawner(drift_particle)
elseif self._drift_timer >= small_drift_time and self._drift_level == 0 then
self._drift_level = 1
drift_particle.time = 1.35
drift_particle.texture = "invector_drift_small.png"
drift_particle.minsize = 1
drift_particle.maxsize = 2
drift_particle.minpos = rear_left_tyre_pos_min
drift_particle.maxpos = rear_left_tyre_pos_max
minetest.add_particlespawner(drift_particle)
drift_particle.minpos = rear_right_tyre_pos_min
drift_particle.maxpos = rear_right_tyre_pos_max
minetest.add_particlespawner(drift_particle)
elseif self._rear_left_tyre_spawner ~= nil then
-- Clear max boost particles
minetest.delete_particlespawner(self._rear_left_tyre_spawner)
minetest.delete_particlespawner(self._rear_right_tyre_spawner)
self._rear_left_tyre_spawner = nil
self._rear_right_tyre_spawner = nil
self._drift_level = 0
end
-- Set camera rotation
if not control.sneak then
if self.attached_player:get_look_vertical() ~= 0 then
self.attached_player:set_look_vertical(0)
end
if self.attached_player:get_look_horizontal() ~= rotation+cam_rot_offset then
self.attached_player:set_look_horizontal(rotation+cam_rot_offset)
end
end
-- Set object rotation and speed
self.object:set_velocity({x=xv, y=velocity.y, z=zv})
self.object:set_rotation(vector.new(kart_rot_offsets.x, rotation+yaw_offset, 0))
else -- Self destruct
--self.object:remove()
end
end
invector.functions.register_kart("sam2", kart)