Original GameJam Submission will be available under branch gamejam
5
.vscode/settings.json
vendored
@ -3,6 +3,9 @@
|
||||
"minetest",
|
||||
"invector",
|
||||
"vector",
|
||||
"Raycast"
|
||||
"Raycast",
|
||||
"superi",
|
||||
"dump",
|
||||
"VoxelArea"
|
||||
]
|
||||
}
|
BIN
menu/background.png
Normal file
After Width: | Height: | Size: 141 B |
BIN
menu/header.png
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 202 KiB |
Before Width: | Height: | Size: 2.3 KiB |
@ -4,6 +4,331 @@
|
||||
|
||||
invector.ai = {}
|
||||
|
||||
function invector.ai.chase_player(ai_kart, target_kart)
|
||||
invector.ai.difficulty = {
|
||||
[1] = {tmin=22, tmax=23*2, frate=12},
|
||||
[2] = {tmin=21, tmax=22*2, frate=11},
|
||||
[3] = {tmin=20, tmax=21*2, frate=10},
|
||||
[4] = {tmin=19, tmax=20*2, frate=9},
|
||||
[5] = {tmin=18, tmax=19*2, frate=8},
|
||||
[6] = {tmin=17, tmax=18*2, frate=7},
|
||||
[7] = {tmin=16, tmax=17*2, frate=6},
|
||||
[8] = {tmin=15, tmax=16*2, frate=5},
|
||||
[9] = {tmin=14, tmax=15*2, frate=4},
|
||||
[10] = {tmin=13, tmax=14*2, frate=3},
|
||||
[11] = {tmin=12, tmax=13*2, frate=2},
|
||||
[12] = {tmin=11, tmax=12*2, frate=1},
|
||||
}
|
||||
|
||||
local controls_template = {
|
||||
up = false,
|
||||
down = false,
|
||||
left = false,
|
||||
right = false,
|
||||
jump = false,
|
||||
aux1 = false,
|
||||
sneak = false,
|
||||
LMB = false,
|
||||
RMB = false,
|
||||
}
|
||||
|
||||
invector.ai.known_node_targets = {}
|
||||
|
||||
function invector.ai.think(self)
|
||||
local controls = table.copy(self._last_control)
|
||||
|
||||
local kart_pos = self.object:get_pos()
|
||||
local node_pos = vector.new(
|
||||
math.floor(kart_pos.x),
|
||||
math.floor(kart_pos.y),
|
||||
math.floor(kart_pos.z)
|
||||
)
|
||||
|
||||
local kart_yaw = self.object:get_yaw()
|
||||
local vel = self.object:get_velocity()
|
||||
local thonk_timer_new = 1
|
||||
|
||||
controls.left = false
|
||||
controls.right = false
|
||||
controls.up = false
|
||||
controls.jump = false
|
||||
-- Prefer waypoints that are marked as track rather than off track
|
||||
-- by lack of the node group.
|
||||
local cross = 0
|
||||
local function preferential_waypoints(pos, target_pos)
|
||||
local area_min = vector.new(node_pos.x-32, node_pos.y-32, node_pos.z-32)
|
||||
local area_max = vector.new(node_pos.x+32, node_pos.y+32, node_pos.z+32)
|
||||
local node_target
|
||||
-- Scan for the start/finish line first if it's up next
|
||||
if invector.tracks[invector.current_track] == nil then
|
||||
error("Track data missing?")
|
||||
elseif invector.tracks[invector.current_track].track_num_waypoints == self._waypoint then
|
||||
node_target = "invector:sector_marker"
|
||||
else
|
||||
node_target = "invector:waypoint_" .. self._waypoint + 1
|
||||
end
|
||||
|
||||
local target_nodes = minetest.find_nodes_in_area(area_min, area_max, node_target, false)
|
||||
-- Make a list of valid nodes
|
||||
local node_targets = {}
|
||||
for i, pos in pairs(target_nodes) do
|
||||
local check_for_non_track = table.copy(pos)
|
||||
check_for_non_track.y = check_for_non_track.y - 1
|
||||
local node = minetest.get_node_or_nil(check_for_non_track)
|
||||
if node == nil then -- Skip nil nodes
|
||||
else
|
||||
local node_def = minetest.registered_nodes[node.name]
|
||||
if node_def.groups == nil then
|
||||
elseif node_def.groups.track then
|
||||
table.insert(node_targets, table.copy(pos))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Select a node to navigate to from the known good list
|
||||
-- Prioritise the closest waypoint that's on track
|
||||
local best_pos = {}
|
||||
local best_dist
|
||||
for k, v in pairs(node_targets) do
|
||||
local dist = math.abs(solarsail.util.functions.pos_to_dist(kart_pos, v))
|
||||
|
||||
if best_dist == nil or dist < best_dist then
|
||||
best_dist = dist
|
||||
best_pos = k
|
||||
end
|
||||
end
|
||||
|
||||
--local test_pos = node_targets[best_pos] --node_targets[math.random(1, #node_targets)]
|
||||
local test_pos = node_targets[best_pos]
|
||||
local x, z = solarsail.util.functions.yaw_to_vec(kart_yaw, 1)
|
||||
local kart_forwards = vector.new(x, 0, z)
|
||||
|
||||
local cross
|
||||
if test_pos == nil then
|
||||
else
|
||||
local delta = vector.normalize(vector.subtract(test_pos, kart_pos))
|
||||
cross = vector.cross(delta, kart_forwards)
|
||||
|
||||
--[[
|
||||
minetest.add_particle({
|
||||
pos = test_pos,
|
||||
velocity = vector.new(0, 2, 0),
|
||||
expiration_time = 3,
|
||||
size = 3,
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
texture = "invector_kart_shield.png",
|
||||
glow = 14
|
||||
})
|
||||
]]
|
||||
|
||||
if cross.y <= -0.15 then
|
||||
controls.right = true
|
||||
elseif cross.y >= 0.15 then
|
||||
controls.left = true
|
||||
end
|
||||
|
||||
if cross.y <= -0.5 then
|
||||
controls.jump = true
|
||||
elseif cross.y >= 0.5 then
|
||||
controls.jump = true
|
||||
end
|
||||
|
||||
-- Manage drifting controls based on direction while drifting
|
||||
if self._is_drifting == -1 then
|
||||
if cross.y <= -0.65 then
|
||||
elseif cross.y <= -0.3 then
|
||||
controls.left = false
|
||||
controls.right = false
|
||||
elseif cross.y <= -0.15 then
|
||||
controls.left = true
|
||||
controls.right = false
|
||||
end
|
||||
elseif self._is_drifting == 1 then
|
||||
if cross.y >= 0.65 then
|
||||
elseif cross.y >= 0.3 then
|
||||
controls.left = false
|
||||
controls.right = false
|
||||
elseif cross.y >= 0.15 then
|
||||
controls.left = false
|
||||
controls.right = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
controls.up = true
|
||||
|
||||
return cross
|
||||
end
|
||||
|
||||
-- Only steer when on the ground
|
||||
if vel.y == 0 then
|
||||
cross = preferential_waypoints()
|
||||
end
|
||||
|
||||
-- Figure out when the next thinking step is based on AI params
|
||||
local function thonk_timer()
|
||||
local thonk_time =
|
||||
math.random(
|
||||
self._ai_reaction_timing.min,
|
||||
self._ai_reaction_timing.max
|
||||
)
|
||||
return thonk_time / 100
|
||||
end
|
||||
thonk_timer_new = thonk_timer()
|
||||
|
||||
-- Check if there are walls or objects in front or beside the kart
|
||||
-- that would block forwards trajectory, this function will override
|
||||
-- preferential_waypoints and adds a second to the thonk_timer
|
||||
local function eyesight()
|
||||
local bonus_thonk_time = 0
|
||||
local possible_coll = false
|
||||
|
||||
local lookx, lookz = solarsail.util.functions.yaw_to_vec(kart_yaw, 1)
|
||||
local look_vec = vector.new(lookx, 0, lookz)
|
||||
local kart_pos_new = table.copy(kart_pos)
|
||||
kart_pos_new.y = kart_pos_new.y + 0.25
|
||||
local center_look =
|
||||
Raycast(kart_pos_new, vector.add(kart_pos_new, vector.multiply(look_vec, 1.25)), false, false)
|
||||
|
||||
local node_pos, node, node_def
|
||||
-- Search to avoid collisions
|
||||
if center_look == nil then
|
||||
else
|
||||
for pointed in center_look do
|
||||
if pointed.type == "node" then
|
||||
node_pos = table.copy(pointed.under)
|
||||
if node_pos ~= nil then
|
||||
node = minetest.get_node_or_nil(node_pos).name
|
||||
node_def = minetest.registered_nodes[node]
|
||||
end
|
||||
if node_def.walkable == nil then
|
||||
if pointed.type == "node" then
|
||||
possible_coll = true
|
||||
break
|
||||
end
|
||||
elseif node_def.walkable then
|
||||
if pointed.type == "node" then
|
||||
possible_coll = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Turn away from the possible collision for a short moment
|
||||
if possible_coll then
|
||||
bonus_thonk_time = math.random(50, 250) / 100
|
||||
controls.up = false
|
||||
controls.down = true
|
||||
controls.jump = false
|
||||
if cross.y > 0 then
|
||||
controls.left = false
|
||||
controls.right = true
|
||||
elseif cross.y < 0 then
|
||||
controls.right = false
|
||||
controls.left = true
|
||||
end
|
||||
end
|
||||
|
||||
return thonk_timer_new+bonus_thonk_time
|
||||
end
|
||||
if vel.y == 0 then
|
||||
thonk_timer_new = eyesight()
|
||||
end
|
||||
|
||||
-- Always reset controls
|
||||
controls.LMB = false
|
||||
controls.RMB = false
|
||||
local function use_items()
|
||||
if self._held_item > 0 then
|
||||
if self._held_item == 1 then -- TNT
|
||||
if math.random(0, 99) > 5 then -- Use chance
|
||||
if math.random(0, 99) > 75 then -- Fire forwards or backwards
|
||||
controls.LMB = true
|
||||
else
|
||||
controls.RMB = true
|
||||
end
|
||||
end
|
||||
elseif self._held_item == 2 then -- Prox TNT
|
||||
if math.random(0, 99) > 5 then -- Use chance
|
||||
if math.random(0, 99) > 85 then -- Fire forwards or backwards
|
||||
controls.LMB = true
|
||||
else
|
||||
controls.RMB = true
|
||||
end
|
||||
end
|
||||
elseif self._held_item == 3 then -- Rocket
|
||||
if math.random(0, 99) > 5 then -- Use chance
|
||||
if math.random(0, 99) < 75 then -- Fire forwards or backwards
|
||||
controls.LMB = true
|
||||
else
|
||||
controls.RMB = true
|
||||
end
|
||||
end
|
||||
elseif self._held_item == 4 then -- Missile
|
||||
if math.random(0, 99) > 5 then -- Use chance
|
||||
if math.random(0, 99) < 85 then -- Fire forwards or backwards
|
||||
controls.LMB = true
|
||||
else
|
||||
controls.RMB = true
|
||||
end
|
||||
end
|
||||
elseif self._held_item == 5 then -- Shield
|
||||
if not self._shields then
|
||||
controls.LMB = true
|
||||
end
|
||||
elseif self._held_item == 6 then -- Ion Cannon
|
||||
if math.random(0, 99) > 5 then -- Use chance
|
||||
if math.random(0, 99) > 75 then -- Fire forwards or backwards
|
||||
controls.LMB = true
|
||||
else
|
||||
controls.RMB = true
|
||||
end
|
||||
end
|
||||
elseif self._held_item == 7 then -- Sand
|
||||
if math.random(0, 99) > 5 then -- Use chance
|
||||
if math.random(0, 99) < 75 then -- Fire forwards or backwards
|
||||
controls.LMB = true
|
||||
else
|
||||
controls.RMB = true
|
||||
end
|
||||
end
|
||||
elseif self._held_item == 8 then -- Mese Crystal
|
||||
if self._boost_timer <= 0 then
|
||||
controls.LMB = true
|
||||
end
|
||||
elseif self._held_item == 9 then -- Mese Shards
|
||||
if math.random(0, 99) > 5 then -- Use chance
|
||||
if math.random(0, 99) > 75 then -- Fire forwards or backwards
|
||||
controls.LMB = true
|
||||
else
|
||||
controls.RMB = true
|
||||
end
|
||||
end
|
||||
elseif self._held_item == 10 then -- Nanite Boosters
|
||||
if self._boost_timer <= 0 then
|
||||
controls.LMB = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Only use items when on the ground
|
||||
if vel.y == 0 then
|
||||
use_items()
|
||||
end
|
||||
|
||||
-- Only apply control modulation when on the ground
|
||||
local result_controls = {}
|
||||
if vel.y == 0 then
|
||||
for k, v in pairs(controls) do
|
||||
if math.random(1,100) <= self._ai_button_press_success then
|
||||
if v then -- Quickly invert keys when the AI makes a "mistake"
|
||||
controls[k] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return controls, thonk_timer_new
|
||||
end
|
BIN
mods/invector/blends/formspec kart sel.blend
Normal file
BIN
mods/invector/blends/formspec title.blend
Normal file
BIN
mods/invector/blends/formspec title.blend1
Normal file
BIN
mods/invector/blends/formspect track sel.blend
Normal file
BIN
mods/invector/blends/formspect track sel.blend1
Normal file
BIN
mods/invector/blends/item icon ion cannon.blend
Normal file
BIN
mods/invector/blends/item icon ion cannon.blend1
Normal file
BIN
mods/invector/blends/item icon mese crystal.blend
Normal file
BIN
mods/invector/blends/item icon mese crystal.blend1
Normal file
BIN
mods/invector/blends/item icon mese shard.blend
Normal file
BIN
mods/invector/blends/item icon mese shard.blend1
Normal file
BIN
mods/invector/blends/item icon prox tnt.blend
Normal file
BIN
mods/invector/blends/item icon prox tnt.blend1
Normal file
BIN
mods/invector/blends/item icon sand.blend
Normal file
BIN
mods/invector/blends/item icon sand.blend1
Normal file
BIN
mods/invector/blends/item icon shield.blend
Normal file
BIN
mods/invector/blends/item icon shield.blend1
Normal file
BIN
mods/invector/blends/item icon tnt.blend
Normal file
BIN
mods/invector/blends/item icon tnt.blend1
Normal file
BIN
mods/invector/blends/item missile.blend
Normal file
BIN
mods/invector/blends/item missile.blend1
Normal file
BIN
mods/invector/blends/item nanite.blend
Normal file
BIN
mods/invector/blends/item nanite.blend1
Normal file
BIN
mods/invector/blends/item rocket.blend
Normal file
BIN
mods/invector/blends/item rocket.blend1
Normal file
BIN
mods/invector/blends/item_rocket.blend
Normal file
BIN
mods/invector/blends/item_rocket.blend1
Normal file
BIN
mods/invector/blends/item_sand.blend
Normal file
BIN
mods/invector/blends/item_tnt.blend
Normal file
BIN
mods/invector/blends/jump_pad.blend
Normal file
BIN
mods/invector/blends/numeric_positions.blend
Normal file
BIN
mods/invector/blends/numeric_positions.blend1
Normal file
BIN
mods/invector/blends/starting_grid_markers.blend
Normal file
BIN
mods/invector/blends/starting_grid_markers.blend1
Normal file
BIN
mods/invector/blends/titlebanner.blend
Normal file
BIN
mods/invector/blends/titlebanner.blend1
Normal file
BIN
mods/invector/blends/ui_icon_eternity.blend
Normal file
BIN
mods/invector/blends/ui_icon_missing_tracks.blend
Normal file
BIN
mods/invector/blends/ui_icon_missing_tracks.blend1
Normal file
@ -35,6 +35,53 @@ minetest.register_node("invector:boost_3_node", {
|
||||
groups = {not_in_builder_inv=1},
|
||||
})
|
||||
|
||||
local inv_wall = {
|
||||
description = "invisible wall",
|
||||
paramtype = "light",
|
||||
groups = {invector = 1}
|
||||
}
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
inv_wall.drawtype = "glasslike"
|
||||
inv_wall.tiles = {"core_azan_leaves.png"}
|
||||
else
|
||||
inv_wall.drawtype = "airlike"
|
||||
end
|
||||
minetest.register_node("invector:invisible_wall", inv_wall)
|
||||
|
||||
minetest.register_node("invector:starting_grid_marker", {
|
||||
description = "Decorative starting grid, useful for people wanting layout",
|
||||
tiles = {"invector_grid_start.png"},
|
||||
groups = {invector=1, track=1},
|
||||
drawtype = "mesh",
|
||||
mesh = "starting_grid_markers.b3d",
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
|
||||
after_place_node = function(pos)
|
||||
local x_offset = 3
|
||||
local z_offset = 4
|
||||
for x=1, 2 do
|
||||
for z=0,5 do
|
||||
if x==1 then
|
||||
if z==0 then
|
||||
else
|
||||
local place_pos = vector.new(pos.x, pos.y, pos.z + (z_offset * z))
|
||||
minetest.set_node(place_pos, {name="invector:starting_grid_marker"})
|
||||
end
|
||||
else
|
||||
if z==0 then
|
||||
local place_pos = vector.new(pos.x+x_offset, pos.y, pos.z + 1)
|
||||
minetest.set_node(place_pos, {name="invector:starting_grid_marker"})
|
||||
else
|
||||
local place_pos = vector.new(pos.x+x_offset, pos.y, (pos.z + 1) + (z_offset * z))
|
||||
minetest.set_node(place_pos, {name="invector:starting_grid_marker"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("invector:boost_pad", {
|
||||
description = "Boost Pad",
|
||||
tiles = {
|
||||
@ -57,7 +104,7 @@ minetest.register_node("invector:boost_pad", {
|
||||
use_texture_alpha = "blend",
|
||||
drawtype = "mesh",
|
||||
mesh = "invector_pad.b3d",
|
||||
groups = {invector = 1, booster = 1, track = 1},
|
||||
groups = {invector = 1, booster = 0.5, track = 1},
|
||||
on_place = solarsail.util.functions.sensible_facedir_simple
|
||||
})
|
||||
|
||||
@ -85,7 +132,61 @@ minetest.register_node("invector:boost_pad_mega", {
|
||||
mesh = "invector_pad_mega.b3d",
|
||||
selection_box = mega_pad,
|
||||
collision_box = mega_pad,
|
||||
groups = {invector = 1, booster = 2, track = 1},
|
||||
groups = {invector = 1, booster = 1, track = 1},
|
||||
on_place = solarsail.util.functions.sensible_facedir_simple
|
||||
})
|
||||
|
||||
minetest.register_node("invector:jump_pad", {
|
||||
description = "Jump Pad",
|
||||
tiles = {
|
||||
"invector_pad_top.png",
|
||||
"invector_pad_bottom.png",
|
||||
"invector_pad_side.png",
|
||||
{
|
||||
name = "jump_pad_holo_anim.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 1.5,
|
||||
type = "vertical_frames"
|
||||
},
|
||||
}
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
light_source = 14,
|
||||
use_texture_alpha = "blend",
|
||||
drawtype = "mesh",
|
||||
mesh = "invector_pad.b3d",
|
||||
groups = {invector = 1, jump = 7, track = 1},
|
||||
on_place = solarsail.util.functions.sensible_facedir_simple
|
||||
})
|
||||
|
||||
minetest.register_node("invector:jump_pad_mega", {
|
||||
description = "Jump Pad",
|
||||
tiles = {
|
||||
"invector_pad_top.png",
|
||||
"invector_pad_bottom.png",
|
||||
"invector_pad_side.png",
|
||||
{
|
||||
name = "jump_pad_holo_anim.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 1.5,
|
||||
type = "vertical_frames"
|
||||
},
|
||||
}
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
light_source = 14,
|
||||
use_texture_alpha = "blend",
|
||||
drawtype = "mesh",
|
||||
mesh = "invector_pad_mega.b3d",
|
||||
selection_box = mega_pad,
|
||||
collision_box = mega_pad,
|
||||
groups = {invector = 1, jump = 14, track = 1},
|
||||
on_place = solarsail.util.functions.sensible_facedir_simple
|
||||
})
|
||||
|
||||
@ -93,6 +194,7 @@ local function reset_item_pad_small(pos, elapsed)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:stop()
|
||||
minetest.swap_node(pos, {name="invector:item_pad_online"})
|
||||
minetest.sound_play("item_pad_online", {pos=pos, max_hear_distance=16}, true)
|
||||
end
|
||||
|
||||
minetest.register_node("invector:item_pad_offline", {
|
||||
@ -134,7 +236,7 @@ minetest.register_node("invector:item_pad_online", {
|
||||
use_texture_alpha = "blend",
|
||||
drawtype = "mesh",
|
||||
mesh = "invector_pad.b3d",
|
||||
groups = {invector = 1, item = 1},
|
||||
groups = {invector = 1, track = 1, item = 1},
|
||||
_swap_to = "invector:item_pad_offline",
|
||||
on_place = solarsail.util.functions.sensible_facedir_simple
|
||||
})
|
||||
@ -143,6 +245,7 @@ local function reset_item_pad_mega(pos, elapsed)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:stop()
|
||||
minetest.swap_node(pos, {name="invector:item_pad_mega_online"})
|
||||
minetest.sound_play("item_pad_online", {pos=pos, max_hear_distance=16}, true)
|
||||
end
|
||||
|
||||
minetest.register_node("invector:item_pad_mega_offline", {
|
||||
@ -159,7 +262,7 @@ minetest.register_node("invector:item_pad_mega_offline", {
|
||||
mesh = "invector_pad_mega.b3d",
|
||||
selection_box = mega_pad,
|
||||
collision_box = mega_pad,
|
||||
groups = {invector = 1, not_in_builder_inv=1},
|
||||
groups = {invector = 1, track = 1, not_in_builder_inv=1},
|
||||
on_place = solarsail.util.functions.sensible_facedir_simple,
|
||||
on_timer = reset_item_pad_mega
|
||||
})
|
||||
@ -188,7 +291,111 @@ minetest.register_node("invector:item_pad_mega_online", {
|
||||
mesh = "invector_pad_mega.b3d",
|
||||
selection_box = mega_pad,
|
||||
collision_box = mega_pad,
|
||||
groups = {invector = 1, item = 1},
|
||||
groups = {invector = 1, track = 1, item = 1},
|
||||
_swap_to = "invector:item_pad_mega_offline",
|
||||
on_place = solarsail.util.functions.sensible_facedir_simple
|
||||
})
|
||||
|
||||
-- Enable or disable visibility of hidden waypoint nodes on the track
|
||||
|
||||
local node_params = {
|
||||
walkable = false,
|
||||
pointable = true,
|
||||
paramtype = "light",
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
},
|
||||
groups = {invector = 1, track = 1}
|
||||
}
|
||||
|
||||
-- Waypoint nodes
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
node_params.tiles = {"invector_track_waypoint.png"}
|
||||
node_params.drawtype = "glasslike"
|
||||
else
|
||||
node_params.drawtype = "airlike"
|
||||
end
|
||||
|
||||
for i=1, 100 do
|
||||
local nparams = table.copy(node_params)
|
||||
nparams.after_place_node = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "AI/Position Waypoint N: "..i)
|
||||
end
|
||||
|
||||
nparams.on_destruct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "")
|
||||
end
|
||||
|
||||
nparams.groups.waypoint = i
|
||||
minetest.register_node("invector:waypoint_"..i, nparams)
|
||||
invector.ai.known_node_targets[i] = "invector:waypoint_"..i
|
||||
end
|
||||
|
||||
-- Start/finish line/sector marker
|
||||
local nparams = table.copy(node_params)
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
nparams.tiles = {"invector_track_sector.png"}
|
||||
nparams.drawtype = "glasslike"
|
||||
|
||||
nparams.after_place_node = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Sector/Start Line Waypoint")
|
||||
end
|
||||
|
||||
nparams.on_destruct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "")
|
||||
end
|
||||
else
|
||||
nparams.drawtype = "airlike"
|
||||
end
|
||||
|
||||
nparams.groups.sector = 1
|
||||
nparams.groups.waypoint = 101
|
||||
minetest.register_node("invector:sector_marker", nparams)
|
||||
invector.ai.known_node_targets[101] = "invector:sector_marker"
|
||||
|
||||
-- Utilities
|
||||
|
||||
minetest.register_craftitem("invector:kart_stick", {
|
||||
inventory_image = "invector_drift_big.png",
|
||||
description = "Spawns unusable karts where you point it.",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
pos.y = pos.y+0.5
|
||||
local ent = minetest.add_entity(pos, "invector:kart")
|
||||
local entlua = ent:get_luaentity()
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craftitem("invector:ai_kart_stick", {
|
||||
inventory_image = "invector_drift_small.png",
|
||||
description = "Spawns AI karts where you point it.",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
pos.y = pos.y+0.5
|
||||
local racer_id = 2
|
||||
local ent = minetest.add_entity(pos, "invector:kart")
|
||||
ent:set_yaw(6.284)
|
||||
ent:set_acceleration(vector.new(0, -9.71, 0))
|
||||
invector.racers[racer_id].kart_ref = ent
|
||||
local entlua = ent:get_luaentity()
|
||||
entlua._is_ai = invector.racers[racer_id].is_ai
|
||||
entlua._position = invector.racers[racer_id].position
|
||||
entlua._racer_id = racer_id
|
||||
entlua._ai_reaction_timing.min = invector.racers[racer_id].ai_difficulty.tmin
|
||||
entlua._ai_reaction_timing.max = invector.racers[racer_id].ai_difficulty.tmax
|
||||
entlua._ai_button_press_success = invector.racers[racer_id].ai_difficulty.frate
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craftitem("invector:laser_pointer", {
|
||||
inventory_image = "invector_drift_med.png",
|
||||
description = "Prints to console the location of the node that was clicked.",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
print(dump(pointed_thing.under))
|
||||
end,
|
||||
})
|
||||
|
8
mods/invector/eternity_blocks.lua
Normal file
@ -0,0 +1,8 @@
|
||||
-- Invector, License MIT, Author Jordach
|
||||
|
||||
minetest.register_node("invector:eternity_grass", {
|
||||
description = "Wireframe, prototyping node",
|
||||
tiles = {"core_grass.png"},
|
||||
groups = {invector=1},
|
||||
stack_max = 60000,
|
||||
})
|
599
mods/invector/game.lua
Normal file
@ -0,0 +1,599 @@
|
||||
-- Invector, License MIT, Author Jordach
|
||||
minetest.after(0.01, minetest.clear_objects, {mode = "full"})
|
||||
|
||||
invector.game = {}
|
||||
-- Racers are numerically indexed as .racers[1-12]
|
||||
-- with fields being generally as'
|
||||
-- .racers[1] = {
|
||||
-- player = player_ref,
|
||||
-- pname = player:get_player_name(),
|
||||
-- kart = kart_ref, should be set at race start.
|
||||
-- color = "colour"
|
||||
-- sector = 1/2/3... also known as laps
|
||||
-- waypoint =
|
||||
-- is_ai = false, or true, depending on if they
|
||||
-- have an AI mind and can be replaced by a player
|
||||
-- ai_difficulty = 0-10, requires is_ai set.
|
||||
--}
|
||||
|
||||
invector.racers = {}
|
||||
invector.course_progress = {}
|
||||
invector.host_player = false
|
||||
invector.game_started = false
|
||||
invector.game_starting = false
|
||||
invector.current_track = "none"
|
||||
invector.known_colours = {
|
||||
"aqua",
|
||||
"black",
|
||||
"blue",
|
||||
"brown",
|
||||
"green",
|
||||
"magenta",
|
||||
"mint",
|
||||
"orange",
|
||||
"red",
|
||||
"violet",
|
||||
"white",
|
||||
"yellow"
|
||||
}
|
||||
|
||||
-- Fill out the racers
|
||||
for i=1, 12 do
|
||||
invector.racers[i] = {
|
||||
is_ai = true,
|
||||
colour = false,
|
||||
player_ref = false,
|
||||
position = -1,
|
||||
waypoint = 0,
|
||||
sector = -1,
|
||||
player_name = "CPU "..i,
|
||||
kart_ref = false,
|
||||
ai_difficulty = { -- dummy data for later
|
||||
tmin = 20,
|
||||
tmax = 21,
|
||||
frate = 50
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
--[[
|
||||
track_name = {
|
||||
-- Where the starting grid sits for 12th place
|
||||
-- Use invector:starting_grid_marker to set it neatly
|
||||
grid_pos_offset = vector.new(1,2,3)
|
||||
|
||||
-- Track data here;
|
||||
track_data = {
|
||||
[1] = {
|
||||
[1] = {
|
||||
[1] = data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Music options
|
||||
music = "song_name"
|
||||
crescendo_music = "song_name_alt"
|
||||
|
||||
-- UI/Formspec options
|
||||
track_icon_model = "model.b3d"
|
||||
track_icon_materials = {"texture.png", "texture.png"}
|
||||
track_name = "Track Name"
|
||||
|
||||
-- Game settings
|
||||
track_num_laps = 3
|
||||
track_num_waypoints = 1-100
|
||||
|
||||
-- Size of the track in 16x16x16 sections, will be force loaded by the server
|
||||
track_mapblocks_size_min = vector.new(1,1,1)
|
||||
track_mapblocks_size_max = vector.new(3,3,3)
|
||||
}
|
||||
]]
|
||||
invector.tracks = {}
|
||||
|
||||
function invector.game.register_track(track_id, track_def)
|
||||
invector.tracks[track_id] = table.copy(track_def)
|
||||
end
|
||||
|
||||
function invector.game.get_racer_id(player_ref)
|
||||
local index = -1
|
||||
local pname = player_ref:get_player_name()
|
||||
for i=1, 12 do
|
||||
if pname == invector.racers[i].player_ref:get_player_name() then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function invector.game.shuffle_positions()
|
||||
local pos = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
||||
table.shuffle(pos)
|
||||
for i=1, 12 do
|
||||
invector.racers[i].position = pos[i]
|
||||
end
|
||||
end
|
||||
|
||||
function invector.game.get_unused_colours()
|
||||
local unused = table.copy(invector.known_colours)
|
||||
-- Remove duplicates from shuffle due to player assignment
|
||||
for i=1, 12 do
|
||||
for k, v in pairs(unused) do
|
||||
if v == invector.racers[i].colour then
|
||||
table.remove(unused, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
return unused
|
||||
end
|
||||
|
||||
-- Randomly assign colours to AI and not use doubles
|
||||
function invector.game.shuffle_colours()
|
||||
local unused = table.copy(invector.game.get_unused_colours())
|
||||
-- Then assign it to the CPU
|
||||
for i=1, 12 do
|
||||
local randpos = math.random(1, #unused)
|
||||
if not invector.racers[i].colour and invector.racers[i].is_ai then
|
||||
invector.racers[i].colour = unused[randpos]
|
||||
table.remove(unused, randpos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function invector.game.set_ai_difficulty()
|
||||
local num_players = 0
|
||||
-- Scan for players first
|
||||
for i=1, 12 do
|
||||
if invector.racers[i].player_ref ~= false then
|
||||
num_players = num_players + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Don't bother if there are 12 players
|
||||
if num_players == 12 then return end
|
||||
|
||||
local ai_diff = 0 + num_players
|
||||
-- Then set AI difficulty inversely, the more players the harder it starts
|
||||
for i=0, 11 do
|
||||
if invector.racers[i+1].is_ai then
|
||||
invector.racers[i+1].ai_difficulty = table.copy(invector.ai.difficulty[ai_diff+i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function invector.game.load_track()
|
||||
for x=invector.tracks[invector.current_track].track_mapblocks_size_min.x,
|
||||
invector.tracks[invector.current_track].track_mapblocks_size_max.x do
|
||||
for y=invector.tracks[invector.current_track].track_mapblocks_size_min.y,
|
||||
invector.tracks[invector.current_track].track_mapblocks_size_max.y do
|
||||
for z=invector.tracks[invector.current_track].track_mapblocks_size_min.z,
|
||||
invector.tracks[invector.current_track].track_mapblocks_size_max.z do
|
||||
|
||||
local block_pos = vector.new(0+(x*16)-16, 0+(y*16)-16, 0+(z*16)-16)
|
||||
minetest.forceload_block(block_pos, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
superi.load_chunking(
|
||||
invector.tracks[invector.current_track].track_mapblocks_size_min,
|
||||
invector.tracks[invector.current_track].track_mapblocks_size_max,
|
||||
invector.tracks[invector.current_track].track_data
|
||||
)
|
||||
end
|
||||
|
||||
function invector.game.get_kart_grid_pos(racer_id)
|
||||
local position = invector.racers[racer_id].position
|
||||
local offset = table.copy(invector.tracks[invector.current_track].grid_pos_offset)
|
||||
offset.y = offset.y + 0.5
|
||||
-- Is odd
|
||||
if position % 2 == 1 then
|
||||
offset.x = offset.x+3
|
||||
offset.z = offset.z+1
|
||||
end
|
||||
|
||||
local dist = math.floor((position/2) + 0.5)
|
||||
if dist == 1 then
|
||||
offset.z = offset.z + (4*5)
|
||||
elseif dist == 2 then
|
||||
offset.z = offset.z + (4*4)
|
||||
elseif dist == 3 then
|
||||
offset.z = offset.z + (4*3)
|
||||
elseif dist == 4 then
|
||||
offset.z = offset.z + (4*2)
|
||||
elseif dist == 5 then
|
||||
offset.z = offset.z + (4*1)
|
||||
end
|
||||
return offset
|
||||
end
|
||||
|
||||
function invector.game.spawn_racers_into_karts()
|
||||
for i=1, 12 do
|
||||
--if i>1 then break end
|
||||
local kart_ent
|
||||
local grid_pos = table.copy(invector.game.get_kart_grid_pos(i))
|
||||
if invector.racers[i].is_ai then
|
||||
kart_ent = minetest.add_entity(grid_pos, "invector:kart")
|
||||
elseif invector.racers[i].player_ref ~= false then
|
||||
kart_ent = solarsail.player.set_model(invector.racers[i].player_ref, "invector:kart", {x=1, y=1}, 60,
|
||||
invector.karts.kart._geo, invector.karts.kart._geo3r, "",
|
||||
invector.karts.kart._prel, invector.karts.kart._prot)
|
||||
kart_ent:set_pos(grid_pos)
|
||||
else
|
||||
error("Uhhhhhhhhhhh, kart that should be either an AI or player, but is somehow neither.")
|
||||
end
|
||||
kart_ent:set_properties({
|
||||
textures = {
|
||||
invector.racers[i].colour .. "_kart_neo.png",
|
||||
invector.racers[i].colour .. "_skin.png",
|
||||
"transparent.png",
|
||||
"transparent.png"
|
||||
}
|
||||
})
|
||||
invector.racers[i].kart_ref = kart_ent
|
||||
kart_ent:set_yaw(6.283)
|
||||
kart_ent:set_acceleration(vector.new(0, -9.71, 0))
|
||||
local kart_lua = kart_ent:get_luaentity()
|
||||
kart_lua._is_ai = invector.racers[i].is_ai
|
||||
kart_lua._position = invector.racers[i].position
|
||||
kart_lua._racer_id = i
|
||||
kart_lua._ai_reaction_timing.min = invector.racers[i].ai_difficulty.tmin
|
||||
kart_lua._ai_reaction_timing.max = invector.racers[i].ai_difficulty.tmax
|
||||
kart_lua._ai_button_press_success = invector.racers[i].ai_difficulty.frate
|
||||
end
|
||||
end
|
||||
|
||||
function invector.game.recursive_timer(time)
|
||||
if time > 0 then
|
||||
for k, player in pairs(minetest.get_connected_players()) do
|
||||
minetest.sound_play("count_"..time, {to_player=player:get_player_name(), gain=0.8})
|
||||
end
|
||||
minetest.after(2, invector.game.recursive_timer, time-1)
|
||||
else
|
||||
for k, player in pairs(minetest.get_connected_players()) do
|
||||
minetest.sound_play("count_go", {to_player=player:get_player_name(), gain=0.8})
|
||||
end
|
||||
invector.game_started = true
|
||||
end
|
||||
end
|
||||
|
||||
function invector.game.prepare_game()
|
||||
invector.game_starting = true
|
||||
-- Load world map based on paramaters
|
||||
invector.game.load_track()
|
||||
-- set AI difficulty
|
||||
invector.game.set_ai_difficulty()
|
||||
-- shuffle AI kart colours
|
||||
invector.game.shuffle_colours()
|
||||
-- shuffle kart positions
|
||||
invector.game.shuffle_positions()
|
||||
-- start karts proper
|
||||
invector.game.spawn_racers_into_karts()
|
||||
-- start the countdown
|
||||
invector.game.recursive_timer(3)
|
||||
end
|
||||
|
||||
function invector.game.fade_music(player_ref)
|
||||
local pname = player_ref:get_player_name()
|
||||
if invector.music[pname].sound_ref ~= false then
|
||||
minetest.sound_fade(invector.music[pname].sound_ref, 0.325, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function invector.game.play_music(player_ref, song_name)
|
||||
local pname = player_ref:get_player_name()
|
||||
invector.music[pname].sound_ref =
|
||||
minetest.sound_play(song_name, {to_player=pname, loop=true, gain=0.65})
|
||||
end
|
||||
|
||||
invector.music = {}
|
||||
-- Music engine handler only available in regular gameplay.
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
minetest.register_globalstep(function(dtime)
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local racer_id = invector.game.get_racer_id(player)
|
||||
local pname = player:get_player_name()
|
||||
if not invector.game_starting then
|
||||
if invector.music[pname].sound_name ~= "coffee_break" then
|
||||
invector.music[pname].sound_name = "coffee_break"
|
||||
-- Only needed for game startup
|
||||
if invector.music[pname].sound_ref ~= false then
|
||||
invector.game.fade_music(player)
|
||||
minetest.after(2, invector.game.play_music, player, "funk_of_the_coffee_break")
|
||||
else
|
||||
invector.game.play_music(player, "funk_of_the_coffee_break")
|
||||
end
|
||||
end
|
||||
else
|
||||
if invector.racers[racer_id].sector == 3 then
|
||||
if invector.music[pname].sound_name ~= invector.tracks[invector.current_track].crescendo_music then
|
||||
invector.music[pname].sound_name = invector.tracks[invector.current_track].crescendo_music
|
||||
invector.game.fade_music(player)
|
||||
invector.game.play_music(player, invector.tracks[invector.current_track].crescendo_music)
|
||||
end
|
||||
elseif invector.racers[racer_id].sector < 3 then
|
||||
if invector.music[pname].sound_name ~= invector.tracks[invector.current_track].music then
|
||||
invector.music[pname].sound_name = invector.tracks[invector.current_track].music
|
||||
invector.game.fade_music(player)
|
||||
invector.game.play_music(player, invector.tracks[invector.current_track].music)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function invector_formspec_colour_selection_formspec(player)
|
||||
local formspec = "size[18,9]formspec_version[3]"
|
||||
local colours
|
||||
|
||||
if player == invector.host_player then
|
||||
colours = table.copy(invector.known_colours)
|
||||
else
|
||||
colours = invector.game.get_unused_colours()
|
||||
end
|
||||
|
||||
local x=0
|
||||
local y=0.5
|
||||
|
||||
formspec = formspec .. "image[0,-0.5;4,2;invector_title.png]"
|
||||
formspec = formspec .. "image[3.15,-0.15;3,1.5;invector_kart_select.png]"
|
||||
for k, colour in pairs(colours) do
|
||||
formspec = formspec .. "model["..x..","..(y)..";3,3;"..
|
||||
colour.."_kart;default_kart.b3d;"..
|
||||
colour.."_kart_neo.png,"..
|
||||
colour.."_skin.png,transparent.png,transparent.png;-15,180;true;false;{0,0}]"
|
||||
|
||||
formspec = formspec .. "button["..x..","..(y+3)..
|
||||
";3,1;"..
|
||||
"button_"..colour..
|
||||
";"..string.upper(colour:sub(1,1))..string.sub(colour, 2, string.len(colour)) .. "]"
|
||||
x = x + 3
|
||||
if x == 18 then
|
||||
x = 0
|
||||
y = y + 4.5
|
||||
end
|
||||
end
|
||||
|
||||
return formspec
|
||||
end
|
||||
|
||||
local function invector_formspec_track_selection_formspec(player_ref)
|
||||
if player_ref ~= invector.host_player then return "" end
|
||||
|
||||
local formspec = "size[16,8]formspec_version[3]"
|
||||
formspec = formspec .. "image[0,-0.5;4,2;invector_title.png]"
|
||||
formspec = formspec .. "image[3.15,-0.15;3,1.5;invector_track_select.png]"
|
||||
|
||||
local x = 0
|
||||
local y = 1
|
||||
for track_id, track_data in pairs(invector.tracks) do
|
||||
formspec = formspec .. "model["..x..","..(y)..";2,2;"..
|
||||
track_data.track_button..";"..
|
||||
track_data.track_icon_model..";"..
|
||||
track_data.track_icon_materials..";"..
|
||||
track_data.track_icon_rotation..";"..
|
||||
"true;false;"..track_data.track_icon_animation.."]"
|
||||
|
||||
formspec = formspec .. "button["..x..","..(y+2)..";2,1;"..
|
||||
track_data.track_button..";"..
|
||||
track_data.track_name.."]"
|
||||
|
||||
x = x + 2
|
||||
if x == 16 then
|
||||
x = 0
|
||||
y = y + 3
|
||||
end
|
||||
end
|
||||
|
||||
return formspec
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local pname = player:get_player_name()
|
||||
local racer_id = invector.game.get_racer_id(player)
|
||||
if formname == "invector_colour_picker" then
|
||||
if fields.quit then
|
||||
minetest.after(0.01, minetest.show_formspec, pname, formname, invector_formspec_colour_selection_formspec(player))
|
||||
else
|
||||
for k, colour in pairs(fields) do
|
||||
invector.racers[racer_id].colour = colour:lower()
|
||||
end
|
||||
|
||||
if invector.host_player == player then
|
||||
minetest.show_formspec(pname, "invector_track_selection", invector_formspec_track_selection_formspec(player))
|
||||
else
|
||||
minetest.show_formspec(pname, "exit", "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if formname == "invector_track_selection" then
|
||||
if fields.quit then
|
||||
minetest.after(0.01, minetest.show_formspec, pname, formname, invector_formspec_track_selection_formspec(player))
|
||||
else
|
||||
-- Attempt to find a match between button and track data ID
|
||||
for key, value in pairs(fields) do
|
||||
for track_name, track_data in pairs(invector.tracks) do
|
||||
if key == track_name then
|
||||
invector.current_track = key
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- MASIVE TODO FIX FOR MULTIPLAYER
|
||||
minetest.close_formspec(pname, formname)
|
||||
invector.game.prepare_game()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
-- Set the host as the first player to join; future releases will be automated
|
||||
if not invector.host_player then
|
||||
invector.host_player = player
|
||||
else
|
||||
minetest.kick_player(player_name, "Invector cannot support more than one client at the moment.")
|
||||
end
|
||||
invector.music[player_name] = {sound_ref=false, sound_name="notplaying"}
|
||||
player:set_properties({
|
||||
textures = {"transparent.png", "transparent.png"},
|
||||
pointable = false,
|
||||
collisionbox = {-0.01, -0.01, -0.01, 0.01, 0.01, 0.01}
|
||||
})
|
||||
|
||||
player:set_nametag_attributes({
|
||||
color = "#00000000"
|
||||
})
|
||||
|
||||
for i=1, 12 do
|
||||
if invector.racers[i].is_ai then
|
||||
invector.racers[i].is_ai = false
|
||||
invector.racers[i].player_ref = player
|
||||
invector.racers[i].player_name = string.gsub(player_name, "_", " ")
|
||||
-- Stop at the first entry.
|
||||
break
|
||||
elseif i == 12 and invector.racers[i].is_ai == true then
|
||||
-- Disconnect the player if there's no room;
|
||||
minetest.kick_player(player_name, "This server cannot fit more than 12 players.")
|
||||
end
|
||||
end
|
||||
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
minetest.show_formspec(player_name, "invector_colour_picker", invector_formspec_colour_selection_formspec(player))
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local lname = player:get_player_name()
|
||||
if invector.host_player == player then
|
||||
-- Next player becomes the host.
|
||||
for i=1, 12 do
|
||||
if not invector.racers[i].is_ai and lname ~= invector.racers[i].player_name then
|
||||
invector.host_player = invector.racers[i].player_ref
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Remove the player from the known racers;
|
||||
for i=1, 12 do
|
||||
if lname == invector.racers[i].player_ref:get_player_name() then
|
||||
invector.racers[i].is_ai = true
|
||||
invector.racers[i].player_ref = false
|
||||
invector.racers[i].player_name = "CPU " .. i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Special creative mode commands
|
||||
|
||||
minetest.register_chatcommand("jukebox", {
|
||||
description = "Play a looped version of any sound.",
|
||||
func = function(name, param)
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
minetest.sound_play(param, {to_player=name, loop=true, gain=0.65})
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("save_chunks", {
|
||||
description = "Save track to disk, 1,1,1 = starting mapblocks, 3,3,3 ending mapblocks, 0,0,0 = offset in blocks, filename",
|
||||
func = function(name, param)
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
local pos = string.split(param, ",")
|
||||
if #pos > 10 then
|
||||
error("Too many arguments used")
|
||||
end
|
||||
|
||||
local start_pos = vector.new(tonumber(pos[1]), tonumber(pos[2]), tonumber(pos[3]))
|
||||
local end_pos = vector.new(tonumber(pos[4]), tonumber(pos[5]), tonumber(pos[6]))
|
||||
local offset_pos = vector.new(tonumber(pos[7]), tonumber(pos[8]), tonumber(pos[9]))
|
||||
local filename = pos[10]
|
||||
superi.save_chunking(start_pos, end_pos, filename, offset_pos)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("load_chunks", {
|
||||
description = "Load track from Lua data",
|
||||
func = function(name, param)
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
local pos = string.split(param, ",")
|
||||
if #pos > 1 then
|
||||
error("Too many arguments used")
|
||||
end
|
||||
local track_name = pos[1]
|
||||
superi.load_chunking(
|
||||
invector.tracks[track_name].track_mapblocks_size_min,
|
||||
invector.tracks[track_name].track_mapblocks_size_max,
|
||||
invector.tracks[track_name].track_data
|
||||
)
|
||||
invector.game_started = true
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("fake_race_start", {
|
||||
description = "Fakes a race for creative mode",
|
||||
func = function(name, param)
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local racer_id = invector.game.get_racer_id(player)
|
||||
invector.racers[racer_id].colour = invector.known_colours[math.random(1, #invector.known_colours)]
|
||||
invector.current_track = param
|
||||
-- set AI difficulty
|
||||
invector.game.set_ai_difficulty()
|
||||
-- shuffle AI kart colours
|
||||
invector.game.shuffle_colours()
|
||||
-- shuffle kart positions
|
||||
invector.game.shuffle_positions()
|
||||
-- Nerf the AI bot for testing
|
||||
invector.racers[2].ai_difficulty = table.copy(invector.ai.difficulty[12])
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("test_kart", {
|
||||
description = "Spawn a kart in creative mode only.",
|
||||
func = function(name)
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local racer_id = invector.game.get_racer_id(player)
|
||||
local ent = solarsail.player.set_model(player, "invector:kart", {x=1, y=1}, 60,
|
||||
invector.karts.kart._geo, invector.karts.kart._geo3r, "",
|
||||
invector.karts.kart._prel, invector.karts.kart._prot)
|
||||
local col = invector.known_colours[math.random(1, #invector.known_colours)]
|
||||
ent:set_properties({
|
||||
textures = {
|
||||
col .. "_kart_neo.png",
|
||||
col .. "_skin.png",
|
||||
"transparent.png",
|
||||
"transparent.png"
|
||||
}
|
||||
})
|
||||
ent:set_yaw(player:get_look_horizontal())
|
||||
ent:set_acceleration(vector.new(0, -9.71, 0))
|
||||
invector.racers[racer_id].kart_ref = ent
|
||||
local kart_lua = ent:get_luaentity()
|
||||
kart_lua._is_ai = invector.racers[racer_id].is_ai
|
||||
kart_lua._position = invector.racers[racer_id].position
|
||||
kart_lua._racer_id = racer_id
|
||||
kart_lua._ai_reaction_timing.min = invector.racers[racer_id].ai_difficulty.tmin
|
||||
kart_lua._ai_reaction_timing.max = invector.racers[racer_id].ai_difficulty.tmax
|
||||
kart_lua._ai_button_press_success = invector.racers[racer_id].ai_difficulty.frate
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("place_node", {
|
||||
description = "places a flat 16x1x16 at 0, 8, 0",
|
||||
func = function(name)
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
for x=0,15 do
|
||||
for z=0,15 do
|
||||
minetest.set_node(vector.new(x, 8, z), {name="solarsail:wireframe"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
@ -5,6 +5,7 @@
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
minetest.override_item("", {
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
wield_image = "transparent.png",
|
||||
range = 1,
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1,
|
||||
@ -21,27 +22,34 @@ else
|
||||
full_punch_interval = 1,
|
||||
max_drop_level = 0,
|
||||
groupcaps = {
|
||||
debug = {times={[1]=1,[2]=0.5,[3]=0.25}, uses=0},
|
||||
invector = {times={[1]=1,[2]=0.5,[3]=0.25}, uses=0},
|
||||
debug = {times={[1]=0.125,[2]=0.125/2,[3]=0.125/4}, uses=0},
|
||||
invector = {times={[1]=0.125,[2]=0.125/2,[3]=0.125/4}, uses=0},
|
||||
},
|
||||
damage_groups = {},
|
||||
}
|
||||
})
|
||||
|
||||
-- Unlimited node placement
|
||||
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack)
|
||||
if placer and placer:is_player() then
|
||||
return minetest.is_creative_enabled(placer:get_player_name())
|
||||
end
|
||||
end)
|
||||
|
||||
function minetest.handle_node_drops(pos, drops, digger)
|
||||
local inv = digger:get_inventory()
|
||||
if inv then
|
||||
for _, item in ipairs(drops) do
|
||||
if not inv:contains_item("main", item, true) then
|
||||
inv:add_item("main", item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
invector = {}
|
||||
invector.functions = {}
|
||||
-- Racers are numerically indexed as .racers[1-12]
|
||||
-- with fields being generally as'
|
||||
-- .racers[1] = {
|
||||
-- player = player_ref,
|
||||
-- pname = player:get_player_name(),
|
||||
-- kart = kart_ref, should be set at race start.
|
||||
-- is_ai = false, or true, depending on if they
|
||||
-- have an AI mind and can be replaced by a player
|
||||
-- ai_difficulty = 0-10, requires is_ai set.
|
||||
--}
|
||||
invector.racers = {}
|
||||
|
||||
invector.path = minetest.get_modpath("invector")
|
||||
|
||||
@ -49,6 +57,11 @@ local function exec(file)
|
||||
dofile(invector.path.."/"..file..".lua")
|
||||
end
|
||||
|
||||
exec("ai")
|
||||
exec("blocks")
|
||||
exec("eternity_blocks")
|
||||
exec("item")
|
||||
exec("game")
|
||||
exec("kart")
|
||||
exec("karts/sam2")
|
||||
exec("karts/kart")
|
||||
exec("tracks/test_track")
|
524
mods/invector/item.lua
Normal file
@ -0,0 +1,524 @@
|
||||
-- Invector, License MIT, Author Jordach
|
||||
local tick_speed = 0.03
|
||||
|
||||
-- Global table.
|
||||
invector.items = {}
|
||||
invector.items.pad_timer = 4.25
|
||||
|
||||
-- TNT ID 1
|
||||
|
||||
local tnt_ent = {
|
||||
visual = "mesh",
|
||||
mesh = "item_tnt.b3d",
|
||||
makes_footstep_sound = false,
|
||||
textures = {"item_tnt.png"},
|
||||
visual_size = {x=5, y=5},
|
||||
collision_box = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
physical = true,
|
||||
collide_with_objects = true,
|
||||
pointable = false,
|
||||
|
||||
_timer = 0
|
||||
}
|
||||
|
||||
function tnt_ent:explode()
|
||||
local pos = self.object:get_pos()
|
||||
local radius = minetest.get_objects_inside_radius(pos, 5)
|
||||
for key, entity in pairs(radius) do
|
||||
local entity_lua = entity:get_luaentity()
|
||||
|
||||
if entity_lua == nil then
|
||||
elseif entity_lua._is_kart == nil then
|
||||
elseif entity_lua._is_kart then
|
||||
if entity_lua._immune_timer > 0 then -- Ignore karts with Nanite Regen
|
||||
elseif entity_lua._stun_timer > 0 then -- Ignore karts that are already stunned
|
||||
elseif not entity_lua._shields then -- Remove shields from karts
|
||||
entity_lua._stun_timer = 2
|
||||
entity_lua:clear_boost()
|
||||
else
|
||||
entity_lua._shields = false
|
||||
local textures = entity:get_properties().textures
|
||||
textures[3] = "transparent.png"
|
||||
entity:set_properties({textures = textures})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = 200,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
minexptime = 0.5,
|
||||
maxexptime = 0.95,
|
||||
minpos = vector.new(pos.x-0.5, pos.y+0, pos.z-0.5),
|
||||
maxpos = vector.new(pos.x+0.5, pos.y+0.5, pos.z+0.5),
|
||||
minvel = vector.new(-4, 0, -4),
|
||||
maxvel = vector.new( 4, 4, 4),
|
||||
time = 0.05,
|
||||
glow = 0,
|
||||
node = {name = "invector:smoke_node"},
|
||||
minsize = 2,
|
||||
maxsize = 3
|
||||
})
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
function tnt_ent:on_step(dtime, moveresult)
|
||||
self._timer = self._timer + dtime
|
||||
local tick_scaling = solarsail.util.functions.remap(dtime, tick_speed/4, tick_speed*4, 0.25, 4)
|
||||
local ratio_tick = solarsail.util.functions.remap(tick_scaling, 0.25, 4, 0, 1)
|
||||
local vel = self.object:get_velocity()
|
||||
-- Handle air and ground friction
|
||||
if moveresult.touching_ground then
|
||||
vel.x = (vel.x * 0.25) * tick_scaling
|
||||
vel.z = (vel.z * 0.25) * tick_scaling
|
||||
end
|
||||
-- Give it some bounce
|
||||
if moveresult.collisions[1] == nil then
|
||||
elseif moveresult.collisions[1].axis == "x" then
|
||||
vel.x = -vel.x * 0.98
|
||||
elseif moveresult.collisions[1].axis == "z" then
|
||||
vel.z = -vel.z * 0.98
|
||||
end
|
||||
self.object:set_velocity(vel)
|
||||
|
||||
local texture = self.object:get_properties().textures
|
||||
if math.floor(self._timer) % 2 == 0 then
|
||||
if texture[1] == "item_tnt.png^[brighten" then
|
||||
self.object:set_properties({textures="item_tnt.png"})
|
||||
end
|
||||
else
|
||||
if texture[1] == "item_tnt.png" then
|
||||
self.object:set_properties({textures="item_tnt.png^[brighten"})
|
||||
end
|
||||
end
|
||||
|
||||
if self._timer > 5 then
|
||||
self:explode()
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("invector:item_tnt", tnt_ent)
|
||||
|
||||
-- Prox TNT ID 2
|
||||
local prox_ent = {
|
||||
visual = "mesh",
|
||||
mesh = "item_tnt.b3d",
|
||||
makes_footstep_sound = false,
|
||||
textures = {"item_prox.png"},
|
||||
visual_size = {x=5, y=5},
|
||||
collision_box = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
physical = true,
|
||||
collide_with_objects = true,
|
||||
pointable = false,
|
||||
|
||||
_timer = 0
|
||||
}
|
||||
|
||||
function prox_ent:explode()
|
||||
if self.object == nil then return end
|
||||
local pos = self.object:get_pos()
|
||||
if pos == nil then return end
|
||||
local radius = minetest.get_objects_inside_radius(pos, 5)
|
||||
|
||||
for key, entity in pairs(radius) do
|
||||
local entity_lua = entity:get_luaentity()
|
||||
|
||||
if entity_lua == nil then
|
||||
elseif entity_lua._is_kart == nil then
|
||||
elseif entity_lua._is_kart then
|
||||
if entity_lua._shields then -- Remove shields from karts
|
||||
entity_lua._shields = false
|
||||
local textures = entity:get_properties().textures
|
||||
textures[3] = "transparent.png"
|
||||
entity:set_properties({textures = textures})
|
||||
else
|
||||
entity_lua._stun_timer = 2
|
||||
entity_lua:clear_boost()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = 200,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
minexptime = 0.25,
|
||||
maxexptime = 0.45,
|
||||
minpos = vector.new(pos.x-0.5, pos.y+0, pos.z-0.5),
|
||||
maxpos = vector.new(pos.x+0.5, pos.y+0.5, pos.z+0.5),
|
||||
minvel = vector.new(-2, 0, -2),
|
||||
maxvel = vector.new(2, 2, 2),
|
||||
time = 0.05,
|
||||
glow = 0,
|
||||
node = {name = "invector:smoke_node"},
|
||||
minsize = 2,
|
||||
maxsize = 3
|
||||
})
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
function prox_ent:on_step(dtime, moveresult)
|
||||
self._timer = self._timer + dtime
|
||||
local tick_scaling = solarsail.util.functions.remap(dtime, tick_speed/4, tick_speed*4, 0.25, 4)
|
||||
local ratio_tick = solarsail.util.functions.remap(tick_scaling, 0.25, 4, 0, 1)
|
||||
local vel = self.object:get_velocity()
|
||||
local pos = self.object:get_pos()
|
||||
-- Handle air and ground friction
|
||||
if moveresult.touching_ground then
|
||||
vel.x = (vel.x * 0.25) * tick_scaling
|
||||
vel.z = (vel.z * 0.25) * tick_scaling
|
||||
end
|
||||
-- Give it some bounce
|
||||
if moveresult.collisions[1] == nil then
|
||||
elseif moveresult.collisions[1].axis == "x" then
|
||||
vel.x = -vel.x * 0.98
|
||||
elseif moveresult.collisions[1].axis == "z" then
|
||||
vel.z = -vel.z * 0.98
|
||||
end
|
||||
self.object:set_velocity(vel)
|
||||
|
||||
-- Scan for karts
|
||||
if self._timer > 1.75 then
|
||||
local ents = minetest.get_objects_inside_radius(pos, 3)
|
||||
|
||||
for key, entity in pairs(ents) do
|
||||
local entity_lua = entity:get_luaentity()
|
||||
|
||||
if entity_lua == nil then
|
||||
elseif entity_lua._is_kart == nil then
|
||||
elseif entity_lua._is_kart then
|
||||
self:explode()
|
||||
end
|
||||
end
|
||||
end
|
||||
if self._timer > 15 then
|
||||
self:explode()
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("invector:item_ptnt", prox_ent)
|
||||
|
||||
-- Rocket ID 3
|
||||
|
||||
invector.items.rocket_particle_trail = {
|
||||
amount = 25,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
minexptime = 0.25,
|
||||
maxexptime = 0.5,
|
||||
node = {name="invector:smoke_node"},
|
||||
minpos = vector.new(0,0,0),
|
||||
maxpos = vector.new(0,0,0),
|
||||
minvel = vector.new(0,0.25,0),
|
||||
maxvel = vector.new(0,1,0),
|
||||
}
|
||||
|
||||
local rocket_ent = {
|
||||
visual = "mesh",
|
||||
mesh = "item_rocket.b3d",
|
||||
makes_footstep_sound = false,
|
||||
textures = {"item_rocket_body.png", "item_rocket_detail.png"},
|
||||
collision_box = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25},
|
||||
physical = true,
|
||||
collide_with_objects = true,
|
||||
pointable = false,
|
||||
visual_size = {x=5.5, y=5.5},
|
||||
_timer = 0
|
||||
}
|
||||
|
||||
function rocket_ent:explode()
|
||||
local pos = self.object:get_pos()
|
||||
local radius = minetest.get_objects_inside_radius(pos, 2)
|
||||
for key, entity in pairs(radius) do
|
||||
local entity_lua = entity:get_luaentity()
|
||||
|
||||
if entity_lua == nil then
|
||||
elseif entity_lua._is_kart == nil then
|
||||
elseif entity_lua._is_kart then
|
||||
if entity_lua._immune_timer > 0 then -- Ignore karts with Nanite Regen
|
||||
elseif entity_lua._stun_timer > 0 then -- Ignore karts that are already stunned
|
||||
elseif not entity_lua._shields then -- Remove shields from karts
|
||||
entity_lua._stun_timer = 2
|
||||
entity_lua:clear_boost()
|
||||
else
|
||||
entity_lua._shields = false
|
||||
local textures = entity:get_properties().textures
|
||||
textures[3] = "transparent.png"
|
||||
entity:set_properties({textures = textures})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = 200,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
minexptime = 0.5,
|
||||
maxexptime = 0.95,
|
||||
minpos = vector.new(pos.x-0.5, pos.y+0, pos.z-0.5),
|
||||
maxpos = vector.new(pos.x+0.5, pos.y+0.5, pos.z+0.5),
|
||||
minvel = vector.new(-4, 0, -4),
|
||||
maxvel = vector.new( 4, 4, 4),
|
||||
time = 0.05,
|
||||
glow = 0,
|
||||
node = {name = "invector:smoke_node"},
|
||||
minsize = 2,
|
||||
maxsize = 3
|
||||
})
|
||||
self.object:remove()
|
||||
end
|
||||
|
||||
function rocket_ent:on_step(dtime, moveresult)
|
||||
self._timer = self._timer + dtime
|
||||
|
||||
-- Explode on any X or Z facing collision
|
||||
if moveresult.collisions[1] == nil then
|
||||
elseif moveresult.collisions[1].axis == "x" then
|
||||
self:explode()
|
||||
elseif moveresult.collisions[1].axis == "z" then
|
||||
self:explode()
|
||||
end
|
||||
|
||||
-- Remove after flying too long
|
||||
if self._timer > 15 then
|
||||
self:explode()
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("invector:item_rocket", rocket_ent)
|
||||
|
||||
-- Missile ID 4
|
||||
|
||||
-- Shield ID 5
|
||||
|
||||
-- ION Cannon ID 6
|
||||
|
||||
-- Pocket Sand ID 7
|
||||
|
||||
local sand_ent = {
|
||||
visual = "mesh",
|
||||
mesh = "item_sand.b3d",
|
||||
makes_footstep_sound = false,
|
||||
textures = {"item_sand.png"},
|
||||
visual_size = {x=5, y=5},
|
||||
collision_box = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
physical = true,
|
||||
collide_with_objects = true,
|
||||
pointable = false,
|
||||
|
||||
_timer = 0
|
||||
}
|
||||
|
||||
function sand_ent:on_step(dtime, moveresult)
|
||||
local tick_scaling = solarsail.util.functions.remap(dtime, tick_speed/4, tick_speed*4, 0.25, 4)
|
||||
self._timer = self._timer + dtime
|
||||
|
||||
local vel = self.object:get_velocity()
|
||||
-- Handle air and ground friction
|
||||
if moveresult.touching_ground then
|
||||
vel.x = (vel.x * 0.25) * tick_scaling
|
||||
vel.z = (vel.z * 0.25) * tick_scaling
|
||||
end
|
||||
-- Give it some bounce
|
||||
if moveresult.collisions[1] == nil then
|
||||
elseif moveresult.collisions[1].axis == "x" then
|
||||
vel.x = -vel.x * 0.98
|
||||
elseif moveresult.collisions[1].axis == "z" then
|
||||
vel.z = -vel.z * 0.98
|
||||
end
|
||||
self.object:set_velocity(vel)
|
||||
|
||||
if self._timer > 10 then
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("invector:item_sand", sand_ent)
|
||||
|
||||
-- Mese Crystal ID 8
|
||||
|
||||
-- Mese Shards ID 9
|
||||
|
||||
-- Nanite Boosters ID 10
|
||||
|
||||
-- Time saver for converting an ID to texture.
|
||||
invector.items.id_to_textures = {
|
||||
[1] = "invector_ui_tnt.png",
|
||||
[2] = "invector_ui_prox_tnt.png",
|
||||
[3] = "invector_ui_rocket.png",
|
||||
[4] = "invector_ui_missile.png",
|
||||
[5] = "invector_ui_shield.png",
|
||||
[6] = "invector_ui_ion_cannon.png",
|
||||
[7] = "invector_ui_sand.png",
|
||||
[8] = "invector_ui_mese_crystal.png",
|
||||
[9] = "invector_ui_mese_shards.png",
|
||||
[10] = "invector_ui_nanite_boosters.png",
|
||||
}
|
||||
|
||||
-- [kart position][0-99] = item ID
|
||||
invector.items.chance = {}
|
||||
|
||||
-- Item distribution stats based on position;
|
||||
local function item_distributor(position, item_defs)
|
||||
invector.items.chance[position] = {}
|
||||
for id, range in pairs(item_defs) do
|
||||
for i=range.min, range.max do
|
||||
invector.items.chance[position][i] = id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
item_distributor(1,
|
||||
{
|
||||
[1] = {min = 0, max = 24},
|
||||
[2] = {min = 25, max = 35},
|
||||
[3] = {min = 36, max = 56},
|
||||
[5] = {min = 57, max = 67},
|
||||
[7] = {min = 68, max = 100},
|
||||
--[9] = {min = 90, max = 100}
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(2,
|
||||
{
|
||||
[1] = {min = 0, max = 9},
|
||||
[2] = {min = 10, max = 19},
|
||||
[3] = {min = 20, max = 39},
|
||||
--[4] = {min = 40, max = 49},
|
||||
[5] = {min = 40, max = 54},
|
||||
--[6] = {min = 55, max = 64},
|
||||
[7] = {min = 55, max = 74},
|
||||
[8] = {min = 75, max = 100},
|
||||
--[9] = {min = 90, max = 100}
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(3,
|
||||
{
|
||||
[1] = {min = 0, max = 4}, --TNT
|
||||
[2] = {min = 5, max = 9}, --PTNT
|
||||
[3] = {min = 10, max = 24}, --Rocket
|
||||
--[4] = {min = 25, max = 49}, --Missile
|
||||
[5] = {min = 25, max = 59}, --Shield
|
||||
--[6] = {min = 60, max = 64}, -- ION Cannon
|
||||
[7] = {min = 60, max = 74}, -- Pocket Sand
|
||||
[8] = {min = 75, max = 100}, -- Mese Crystal
|
||||
--[9] = {min = 95, max = 100} -- Mese Shards
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(4,
|
||||
{
|
||||
[3] = {min = 0, max = 24}, --Rocket
|
||||
--[4] = {min = 25, max = 34}, --Missile
|
||||
[5] = {min = 25, max = 44}, --Shield
|
||||
--[6] = {min = 45, max = 49}, -- ION Cannon
|
||||
[7] = {min = 45, max = 59}, -- Pocket Sand
|
||||
[8] = {min = 60, max = 100}, -- Mese Crystal
|
||||
--[9] = {min = 95, max = 100} -- Mese Shards
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(5,
|
||||
{
|
||||
[3] = {min = 0, max = 24}, --Rocket
|
||||
--[4] = {min = 25, max = 34}, --Missile
|
||||
[5] = {min = 25, max = 44}, --Shield
|
||||
--[6] = {min = 45, max = 49}, -- ION Cannon
|
||||
[7] = {min = 45, max = 59}, -- Pocket Sand
|
||||
[8] = {min = 60, max = 100}, -- Mese Crystal
|
||||
--[9] = {min = 95, max = 100} -- Mese Shards
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(6,
|
||||
{
|
||||
[3] = {min = 0, max = 24}, --Rocket
|
||||
--[4] = {min = 25, max = 34}, --Missile
|
||||
[5] = {min = 25, max = 44}, --Shield
|
||||
--[6] = {min = 45, max = 49}, -- ION Cannon
|
||||
[7] = {min = 45, max = 59}, -- Pocket Sand
|
||||
[8] = {min = 60, max = 100}, -- Mese Crystal
|
||||
--[9] = {min = 95, max = 100} -- Mese Shards
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(7,
|
||||
{
|
||||
[3] = {min = 0, max = 4}, --Rocket
|
||||
--[4] = {min = 5, max = 34}, --Missile
|
||||
--[6] = {min = 35, max = 49}, -- ION Cannon
|
||||
[8] = {min = 5, max = 99}, -- Mese Crystal
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(8,
|
||||
{
|
||||
[3] = {min = 0, max = 4}, --Rocket
|
||||
--[4] = {min = 5, max = 34}, --Missile
|
||||
--[6] = {min = 35, max = 49}, -- ION Cannon
|
||||
[8] = {min = 5, max = 99}, -- Mese Crystal
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(9,
|
||||
{
|
||||
[3] = {min = 0, max = 4}, --Rocket
|
||||
--[4] = {min = 5, max = 34}, --Missile
|
||||
--[6] = {min = 35, max = 49}, -- ION Cannon
|
||||
[8] = {min = 5, max = 99}, -- Mese Crystal
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(10,
|
||||
{
|
||||
--[4] = {min = 0, max = 29}, --Missile
|
||||
--[6] = {min = 30, max = 49}, -- ION Cannon
|
||||
[8] = {min = 0, max = 74}, -- Mese Crystal
|
||||
[10] = {min = 75, max = 99}, -- Nanite Regenerators
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(11,
|
||||
{
|
||||
--[4] = {min = 0, max = 29}, --Missile
|
||||
[8] = {min = 0, max = 69}, -- Mese Crystal
|
||||
[10] = {min = 70, max = 99}, -- Nanite Regenerators
|
||||
}
|
||||
)
|
||||
|
||||
item_distributor(12,
|
||||
{
|
||||
[8] = {min = 0, max = 69}, -- Mese Crystal
|
||||
[10] = {min = 70, max = 99}, -- Nanite Regenerators
|
||||
}
|
||||
)
|
||||
|
||||
-- Debug position all items have equal chance
|
||||
item_distributor(13, {
|
||||
[1] = {min = 0, max = 9},
|
||||
[2] = {min = 10, max = 19},
|
||||
[3] = {min = 20, max = 39},
|
||||
--[4] = {min = 30, max = 39},
|
||||
[5] = {min = 40, max = 49},
|
||||
--[6] = {min = 50, max = 59},
|
||||
[7] = {min = 50, max = 69},
|
||||
[8] = {min = 70, max = 79},
|
||||
[9] = {min = 80, max = 89},
|
||||
[10] = {min = 90, max = 99}
|
||||
})
|
||||
|
||||
-- Give items;
|
||||
function invector.items.give_item(kart_ref)
|
||||
local rand = math.random(0, 99)
|
||||
local item_id
|
||||
if kart_ref._position == nil or kart_ref._position == -1 then
|
||||
item_id = invector.items.chance[13][rand]
|
||||
else
|
||||
item_id = invector.items.chance[kart_ref._position][rand]
|
||||
end
|
||||
return item_id
|
||||
end
|
@ -4,19 +4,4 @@ invector.karts = {}
|
||||
function invector.functions.register_kart(name, def)
|
||||
invector.karts[name] = def
|
||||
minetest.register_entity("invector:"..name, def)
|
||||
end
|
||||
|
||||
-- This is fixed in code due to this not being a multiplayer release.
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
solarsail.player.set_model(player, "invector:sam2", {x=0, y=159}, 60,
|
||||
invector.karts.sam2._geo, invector.karts.sam2._geo3r, "",
|
||||
invector.karts.sam2._prel, invector.karts.sam2._prot)
|
||||
else
|
||||
end
|
||||
end)
|
||||
|
||||
-- Items and how to shuffle them based on positions;
|
||||
|
||||
invector.items = {}
|
||||
|
||||
end
|
925
mods/invector/karts/kart.lua
Normal file
@ -0,0 +1,925 @@
|
||||
-- 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 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
|
||||
|
||||
-- Empty controls
|
||||
local controls = {
|
||||
up = false,
|
||||
down = false,
|
||||
left = false,
|
||||
right = false,
|
||||
jump = false,
|
||||
aux1 = false,
|
||||
sneak = false,
|
||||
LMB = false,
|
||||
RMB = false,
|
||||
}
|
||||
|
||||
local drift_particle_def = {
|
||||
amount = 50,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
minexptime = 0.25,
|
||||
maxexptime = 0.5,
|
||||
glow = 14
|
||||
}
|
||||
|
||||
local exhaust_particle_def = {
|
||||
amount = 50,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
minexptime = 0.15,
|
||||
maxexptime = 0.35
|
||||
}
|
||||
|
||||
local nanite_particle_def = {
|
||||
amount = 1000,
|
||||
time = 10,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
glow = 14,
|
||||
minexptime = 0.5,
|
||||
maxexptime = 0.9,
|
||||
minvel = vector.new(0,1,0),
|
||||
maxvel = vector.new(0,1.5,0),
|
||||
minpos = vector.new(-0.5, 0, -0.5),
|
||||
maxpos = vector.new(0.5, 0.8, 0.5),
|
||||
minsize = 3,
|
||||
maxsize = 6,
|
||||
texture = "invector_ui_nanite_boosters.png"
|
||||
}
|
||||
|
||||
local kart = {
|
||||
visual = "mesh",
|
||||
mesh = "default_kart.b3d",
|
||||
use_texture_alpha = true,
|
||||
backface_culling = false,
|
||||
makes_footstep_sound = false,
|
||||
textures = {
|
||||
"blue_kart_neo.png",
|
||||
"blue_skin.png",
|
||||
"transparent.png",
|
||||
"transparent.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:
|
||||
_attached_player = nil,
|
||||
_timer = 0,
|
||||
_boost_timer = 0,
|
||||
_ai_timer = 0,
|
||||
_drift_timer = 0,
|
||||
_stun_timer = 0,
|
||||
_immune_timer = 0,
|
||||
_collision_timer = 0,
|
||||
_collision_mod = vector.new(0,0,0),
|
||||
_dvel = 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, -- Useful for homing weapons or AOE weapons
|
||||
_is_creative = minetest.settings:get_bool("creative_mode"),
|
||||
|
||||
-- Set DURING INIT
|
||||
_is_ai = false,
|
||||
_position = -1, -- Set at race start and during a race
|
||||
_racer_id = -1, -- Set on race start since it's likely singleplayer.
|
||||
_ai_reaction_timing = {min = 2, max = 25}, -- divided by 10 due to math.random() shenanigans
|
||||
_ai_reaction_time = 0,
|
||||
_ai_button_press_success = 35, -- Any number rolled lower than X is considered a failure
|
||||
_ai_last_waypoint = -1,
|
||||
_ai_dist_rand = math.random(1, 250) / 100,
|
||||
_track_sector = 1, -- Lap / track sector for non circuits
|
||||
_waypoint = 0, -- Similar purpose to _position
|
||||
_held_item = 0,
|
||||
_shields = false, -- Can take an extra hit without being stunned, can be bypassed by certain weapons
|
||||
_last_control = table.copy(controls), -- Memorises the last held button for AI to not make things go wrong
|
||||
|
||||
-- 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
|
||||
}
|
||||
|
||||
-- Things like rockets, missiles, ION Cannon
|
||||
function kart:spawn_item_forwards()
|
||||
local ent
|
||||
local pos = self.object:get_pos()
|
||||
local yaw = self.object:get_yaw()
|
||||
local vel = self.object:get_velocity()
|
||||
local xm, zm = solarsail.util.functions.yaw_to_vec(yaw, 1)
|
||||
local spawn_pos = vector.add(pos, {x=xm*2, y=1.25, z=zm*2})
|
||||
local spawn_vel = vector.add(vel, vector.new(xm*15, 0, zm*15))
|
||||
if self._held_item == 3 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_rocket")
|
||||
local particle = table.copy(invector.items.rocket_particle_trail)
|
||||
particle.attached = ent
|
||||
minetest.add_particlespawner(particle)
|
||||
ent:set_velocity(spawn_vel)
|
||||
elseif self._held_item == 4 then
|
||||
--ent = minetest.add_entity(spawn_pos, "invector:item_missile")
|
||||
--local particle = table.copy(invector.items.rocket_particle_trail)
|
||||
--particle.attached = ent
|
||||
--minetest.add_particlespawner(particle)
|
||||
--ent:set_velocity(spawn_vel)
|
||||
elseif self._held_item == 6 then
|
||||
--ent = minetest.add_entity(spawn_pos, "invector:item_ion_cannon")
|
||||
--ent:set_velocity(spawn_vel)
|
||||
end
|
||||
ent:set_yaw(yaw)
|
||||
end
|
||||
|
||||
function kart:spawn_item_backwards()
|
||||
local ent
|
||||
local pos = self.object:get_pos()
|
||||
local yaw = self.object:get_yaw()
|
||||
local vel = self.object:get_velocity()
|
||||
local xm, zm = solarsail.util.functions.yaw_to_vec(yaw, 1)
|
||||
local spawn_pos = vector.add(pos, {x=-xm*2, y=1.25, z=-zm*2})
|
||||
local spawn_vel = vector.add(vel, vector.new(xm*-15, 0, zm*-15))
|
||||
if self._held_item == 3 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_rocket")
|
||||
local particle = table.copy(invector.items.rocket_particle_trail)
|
||||
particle.attached = ent
|
||||
minetest.add_particlespawner(particle)
|
||||
ent:set_velocity(spawn_vel)
|
||||
elseif self._held_item == 4 then
|
||||
--ent = minetest.add_entity(spawn_pos, "invector:item_missile")
|
||||
--local particle = table.copy(invector.items.rocket_particle_trail)
|
||||
--particle.attached = ent
|
||||
--minetest.add_particlespawner(particle)
|
||||
--ent:set_velocity(spawn_vel)
|
||||
elseif self._held_item == 6 then
|
||||
--ent = minetest.add_entity(spawn_pos, "invector:item_ion_cannon")
|
||||
--ent:set_velocity(spawn_vel)
|
||||
end
|
||||
ent:set_yaw(yaw + 3.142)
|
||||
end
|
||||
|
||||
-- Things like sand, TNT, PTNT, mese shards etc
|
||||
function kart:throw_item_forwards()
|
||||
local ent
|
||||
local pos = self.object:get_pos()
|
||||
local yaw = self.object:get_yaw()
|
||||
local vel = self.object:get_velocity()
|
||||
local xm, zm = solarsail.util.functions.yaw_to_vec(yaw, 1)
|
||||
local spawn_pos = vector.add(pos, {x=xm*2, y=0.35, z=zm*2})
|
||||
local throw_vel = vector.add(vel, {x=xm*7.2, y=8, z=zm*7.2})
|
||||
if self._held_item == 1 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_tnt")
|
||||
elseif self._held_item == 2 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_ptnt")
|
||||
elseif self._held_item == 7 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_sand")
|
||||
elseif self._held_item == 9 then
|
||||
--ent = minetest.add_entity(spawn_pos, "invector:item_mese_shards")
|
||||
end
|
||||
ent:set_acceleration(vector.new(0, -9.71, 0))
|
||||
ent:set_velocity(throw_vel)
|
||||
end
|
||||
|
||||
function kart:drop_item_backwards()
|
||||
local ent
|
||||
local pos = self.object:get_pos()
|
||||
local yaw = self.object:get_yaw()
|
||||
local vel = self.object:get_velocity()
|
||||
local xm, zm = solarsail.util.functions.yaw_to_vec(yaw, 1)
|
||||
local spawn_pos = vector.add(pos, {x=-xm*2, y=0.35, z=-zm*2})
|
||||
local throw_vel = vector.add(vel, {x=0, y=1, z=0})
|
||||
if self._held_item == 1 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_tnt")
|
||||
elseif self._held_item == 2 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_ptnt")
|
||||
elseif self._held_item == 7 then
|
||||
ent = minetest.add_entity(spawn_pos, "invector:item_sand")
|
||||
elseif self._held_item == 9 then
|
||||
--ent = minetest.add_entity(spawn_pos, "invector:item_mese_shards")
|
||||
end
|
||||
ent:set_velocity(throw_vel)
|
||||
ent:set_acceleration(vector.new(0, -9.71, 0))
|
||||
end
|
||||
|
||||
function kart:clear_boost()
|
||||
self._is_drifting = 0
|
||||
self._drift_timer = 0
|
||||
self._boost_type = 0
|
||||
self._boost_timer = 0
|
||||
end
|
||||
|
||||
function kart:on_step(dtime, moveresult)
|
||||
if not invector.game_started and not self._is_creative then
|
||||
return
|
||||
end
|
||||
|
||||
-- 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)
|
||||
local ratio_tick = solarsail.util.functions.remap(tick_scaling, 0.25, 4, 0, 1)
|
||||
|
||||
-- Add kart stun
|
||||
if self._stun_timer > 0 then
|
||||
self._stun_timer = self._stun_timer - dtime
|
||||
end
|
||||
-- Nanite Regeneration
|
||||
if self._immune_timer > 0 then
|
||||
self._immune_timer = self._immune_timer - dtime
|
||||
end
|
||||
|
||||
-- Impulse from other karts
|
||||
if self._collision_timer > 0 then
|
||||
self._collision_timer = self._collision_timer - dtime
|
||||
else -- Clear the vector
|
||||
self._collision_timer = 0
|
||||
self._collision_mod = vector.new(0,0,0)
|
||||
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
|
||||
|
||||
local velocity = self.object:get_velocity()
|
||||
local rotation = self.object:get_yaw()
|
||||
local cam_rot_offset = 0
|
||||
local yaw_offset = 0
|
||||
|
||||
local kart_rot_offsets = vector.new(0,0,0)
|
||||
local xv, zv
|
||||
-- Animation and camera handler:
|
||||
local frange, fspeed, fblend, floop = self.object:get_animation()
|
||||
local new_frames = {x=0, y=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 - 0.5
|
||||
local node_detector = Raycast(kart_phys_pos, kart_node_pos, false, false)
|
||||
local node_pos
|
||||
if node_detector == nil then
|
||||
else
|
||||
for pointed in node_detector do
|
||||
if pointed.type == "node" then
|
||||
node_pos = table.copy(pointed.under)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
local node_data, node
|
||||
if node_pos ~= nil then
|
||||
node_data = minetest.get_node_or_nil(node_pos)
|
||||
node = minetest.registered_nodes[node_data.name]
|
||||
end
|
||||
|
||||
|
||||
for k, coll in pairs(moveresult.collisions) do
|
||||
if coll.type == "object" then
|
||||
local ent_lua = coll.object:get_luaentity()
|
||||
if ent_lua._is_kart ~= nil then
|
||||
if ent_lua._raceri_id ~= self._racer_id then
|
||||
local nx, nz = solarsail.util.functions.yaw_to_vec(rotation, self._dvel*0.75)
|
||||
local kart_forwards = vector.new(nx, 0, nz)
|
||||
ent_lua._collision_timer = 1
|
||||
ent_lua._collision_mod = vector.add(
|
||||
kart_forwards,
|
||||
vector.subtract(
|
||||
vector.multiply(coll.old_velocity, 0.5),
|
||||
vector.multiply(coll.new_velocity, 0.5)
|
||||
)
|
||||
)
|
||||
self._dvel = 0
|
||||
--break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle boost, item, jump, and waypoints
|
||||
if node_data ~= nil then
|
||||
-- Handle the start/finish line first, then handle waypoints
|
||||
if node.groups.sector ~= nil then
|
||||
if invector.tracks[invector.current_track] == nil then
|
||||
elseif invector.tracks[invector.current_track].track_num_waypoints == self._waypoint then
|
||||
self._waypoint = 0
|
||||
self._track_sector = self._track_sector + 1
|
||||
invector.racers[self._racer_id].sector = self._track_sector
|
||||
invector.racers[self._racer_id].waypoint = self._waypoint
|
||||
print("lap: " .. self._track_sector)
|
||||
print("waypoint: " .. 0)
|
||||
self._ai_dist_rand = math.random(1, 250) / 100
|
||||
end
|
||||
elseif node.groups.waypoint ~= nil then
|
||||
if node.groups.waypoint == self._waypoint + 1 then
|
||||
self._waypoint = 0 + node.groups.waypoint
|
||||
invector.racers[self._racer_id].waypoint = self._waypoint
|
||||
print("waypoint: " .. node.groups.waypoint)
|
||||
self._ai_dist_rand = math.random(1, 250) / 100
|
||||
end
|
||||
end
|
||||
|
||||
if node.groups.booster ~= nil then
|
||||
if self._boost_timer <= 0 then
|
||||
if node.groups.booster == 1 then
|
||||
minetest.sound_play("boost_pad_small", {object=self.object, max_hear_distance=8}, true)
|
||||
elseif node.groups.booster == 2 then
|
||||
minetest.sound_play("boost_pad_big", {object=self.object, max_hear_distance=8}, true)
|
||||
end
|
||||
end
|
||||
self._boost_timer = node.groups.booster
|
||||
self._boost_type = 3
|
||||
end
|
||||
|
||||
if node.groups.jump ~= nil then
|
||||
if moveresult.touching_ground then
|
||||
velocity.y = node.groups.jump
|
||||
end
|
||||
end
|
||||
|
||||
if node.groups.item ~= nil then
|
||||
if self._held_item == 0 then
|
||||
self._held_item = invector.items.give_item(self.object)
|
||||
local textures = self.object:get_properties().textures
|
||||
textures[4] = invector.items.id_to_textures[self._held_item]
|
||||
self.object:set_properties({textures = textures})
|
||||
if self._attached_player ~= nil then
|
||||
minetest.sound_play("item_voice_"..self._held_item, {to_player=self._attached_player:get_player_name(), gain=0.8})
|
||||
end
|
||||
minetest.swap_node(node_pos, {name=node._swap_to})
|
||||
minetest.sound_play("item_pickup", {object=self.object, max_hear_distance=8}, true)
|
||||
local timer = minetest.get_node_timer(node_pos)
|
||||
timer:start(invector.items.pad_timer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle node frictive types here;
|
||||
-- Handle friction on ground
|
||||
if moveresult.touching_ground then
|
||||
local frictive = 0
|
||||
if node == nil then
|
||||
frictive = friction_off_track
|
||||
elseif node.groups == nil then
|
||||
frictive = friction_off_track
|
||||
elseif 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
|
||||
end
|
||||
|
||||
-- Round down numbers when percentages exponentialise movement:
|
||||
if self._dvel > 0 and self._dvel < 0.05 then
|
||||
self._dvel = 0
|
||||
elseif self._dvel < 0 and self._dvel > -0.05 then
|
||||
self._dvel = 0
|
||||
end
|
||||
|
||||
-- Handle controls for players and AI
|
||||
local control = controls
|
||||
if self._attached_player ~= nil then
|
||||
control = solarsail.controls.player[self._attached_player:get_player_name()]
|
||||
self._last_control = control
|
||||
elseif self._is_ai and self._ai_timer >= self._ai_reaction_time then
|
||||
local new_control, new_reaction_time = invector.ai.think(self)
|
||||
control = new_control
|
||||
self._last_control = new_control
|
||||
self._ai_timer = 0
|
||||
self._ai_reaction_time = new_reaction_time
|
||||
elseif self._is_ai then
|
||||
control = self._last_control
|
||||
self._ai_timer = self._ai_timer + dtime
|
||||
end
|
||||
|
||||
if self._stun_timer <= 0 then
|
||||
-- Handle controls;
|
||||
-- Accel braking;
|
||||
if control.up or self._forced_boost then
|
||||
local boost_multi = 1
|
||||
if self._boost_timer > 0 then
|
||||
boost_multi = 3
|
||||
elseif node == nil then
|
||||
boost_multi = 0.15
|
||||
elseif node.groups == nil then
|
||||
boost_multi = 0.15
|
||||
elseif node.groups.track == nil then
|
||||
boost_multi = 0.15
|
||||
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, turning, do not turn when airborne;
|
||||
if velocity.y ~= 0 then
|
||||
elseif 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
|
||||
else
|
||||
-- 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
|
||||
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
|
||||
|
||||
-- Give the boost
|
||||
if not control.jump and self._boost_timer <= 0 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
|
||||
minetest.sound_play("drift_boost_big", {object=self.object, max_hear_distance=8}, true)
|
||||
-- Medium boost
|
||||
elseif self._drift_timer >= med_drift_time then
|
||||
self._boost_timer = med_boost_time
|
||||
self._boost_type = 2
|
||||
self._drift_timer = 0
|
||||
minetest.sound_play("drift_boost_med", {object=self.object, max_hear_distance=8}, true)
|
||||
-- Small boost
|
||||
elseif self._drift_timer >= small_drift_time then
|
||||
self._boost_timer = small_boost_time
|
||||
self._boost_type = 1
|
||||
self._drift_timer = 0
|
||||
minetest.sound_play("drift_boost_small", {object=self.object, max_hear_distance=8}, true)
|
||||
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
|
||||
if self._is_drifting ~= 0 then self:clear_boost() end
|
||||
end
|
||||
|
||||
-- Use Item/Item Alt mode; usually in the form of throw forwards or backwards
|
||||
if control.LMB and self._held_item > 0 then
|
||||
if self._held_item == 1 or self._held_item == 2 then -- TNT family
|
||||
self:drop_item_backwards()
|
||||
elseif self._held_item == 3 or self._held_item == 4 then -- Rocket Family
|
||||
self:spawn_item_forwards()
|
||||
elseif self._held_item == 5 then -- Shield
|
||||
if not self._shields then
|
||||
self._shields = true
|
||||
local textures = self.object:get_properties().textures
|
||||
textures[3] = "invector_kart_shield.png"
|
||||
self.object:set_properties({textures=textures})
|
||||
end
|
||||
elseif self._held_item == 6 then -- Ion Cannon
|
||||
self:spawn_item_forwards()
|
||||
elseif self._held_item == 7 then -- Sand
|
||||
self:drop_item_backwards()
|
||||
elseif self._held_item == 8 then -- Mese Crystal
|
||||
self._boost_timer = 2
|
||||
self._boost_type = 3
|
||||
minetest.sound_play("boost_pad_big", {object=self.object, max_hear_distance=8}, true)
|
||||
elseif self._held_item == 9 then -- Mese Shards
|
||||
elseif self._held_item == 10 then -- Nanites
|
||||
local particles = table.copy(nanite_particle_def)
|
||||
particles.attached = self.object
|
||||
minetest.add_particlespawner(particles)
|
||||
self._boost_timer = 10
|
||||
self._boost_type = 3
|
||||
self._immune_timer = 10
|
||||
end
|
||||
self._held_item = 0
|
||||
local textures = self.object:get_properties().textures
|
||||
textures[4] = "transparent.png"
|
||||
self.object:set_properties({textures = textures})
|
||||
elseif control.RMB and self._held_item > 0 then
|
||||
if self._held_item == 1 or self._held_item == 2 then
|
||||
self:throw_item_forwards()
|
||||
elseif self._held_item == 3 or self._held_item == 4 then -- Rocket Family
|
||||
self:spawn_item_backwards()
|
||||
elseif self._held_item == 5 then -- Shield
|
||||
if not self._shields then
|
||||
self._shields = true
|
||||
local textures = self.object:get_properties().textures
|
||||
textures[3] = "invector_kart_shield.png"
|
||||
self.object:set_properties({textures=textures})
|
||||
end
|
||||
elseif self._held_item == 6 then -- Ion Cannon
|
||||
self:spawn_item_backwards()
|
||||
elseif self._held_item == 7 then -- Sand
|
||||
self:throw_item_forwards()
|
||||
elseif self._held_item == 8 then -- Mese Crystal
|
||||
self._boost_timer = 2
|
||||
self._boost_type = 3
|
||||
minetest.sound_play("boost_pad_big", {object=self.object, max_hear_distance=8}, true)
|
||||
elseif self._held_item == 9 then -- Mese Shards
|
||||
elseif self._held_item == 10 then -- Nanites
|
||||
local particles = table.copy(nanite_particle_def)
|
||||
particles.attached = self.object
|
||||
minetest.add_particlespawner(particles)
|
||||
self._boost_timer = 10
|
||||
self._boost_type = 3
|
||||
self._immune_timer = 10
|
||||
end
|
||||
self._held_item = 0
|
||||
local textures = self.object:get_properties().textures
|
||||
textures[4] = "transparent.png"
|
||||
self.object:set_properties({textures = textures})
|
||||
end
|
||||
|
||||
-- Animation frames while driving.
|
||||
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
|
||||
|
||||
xv, zv = solarsail.util.functions.yaw_to_vec(rotation+yaw_offset, self._dvel)
|
||||
local new_vel = vector.new(xv, 0, zv)
|
||||
new_vel = vector.normalize(new_vel)
|
||||
new_vel.y = velocity.y
|
||||
|
||||
-- Particlespawner handler
|
||||
-- Exhausts;
|
||||
local exhaust_particle = table.copy(exhaust_particle_def)
|
||||
exhaust_particle.attached = self.object
|
||||
exhaust_particle.minvel = vector.new(-0.05, 0.5, -0.25)
|
||||
exhaust_particle.maxvel = vector.new(-0.15, 1.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 = 0.05
|
||||
exhaust_particle.maxvel.x = 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 = 0.05
|
||||
exhaust_particle.maxvel.x = 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 = 0.05
|
||||
exhaust_particle.maxvel.x = 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 = 0.05
|
||||
exhaust_particle.maxvel.x = 0.15
|
||||
minetest.add_particlespawner(exhaust_particle)
|
||||
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 then
|
||||
if self._drift_level == 2 then
|
||||
minetest.sound_play("drift_spark_big", {object=self.object, max_hear_distance=8}, true)
|
||||
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)
|
||||
end
|
||||
elseif self._drift_timer >= med_drift_time and self._drift_level == 1 then
|
||||
minetest.sound_play("drift_spark_med", {object=self.object, max_hear_distance=8}, true)
|
||||
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
|
||||
minetest.sound_play("drift_spark_small", {object=self.object, max_hear_distance=8}, true)
|
||||
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
|
||||
|
||||
-- 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
|
||||
else
|
||||
-- Stunned animation.
|
||||
new_frames.x = 180
|
||||
new_frames.y = 209
|
||||
end
|
||||
|
||||
-- Set camera rotation for players only
|
||||
if not self._last_control.sneak and self._attached_player ~= nil 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
|
||||
-- Reversing camera mode if required
|
||||
if self._dvel < -0.5 and control.down then
|
||||
cam_rot_offset = 3.142
|
||||
end
|
||||
self._attached_player:set_look_horizontal(rotation+cam_rot_offset)
|
||||
end
|
||||
end
|
||||
|
||||
-- Set object rotation and speed, if the kart enters a stunned state; it'll slide in place
|
||||
if not moveresult.touching_ground then
|
||||
xv = nil
|
||||
zv = nil
|
||||
end
|
||||
|
||||
if xv == nil or zv == nil then
|
||||
self.object:set_velocity(
|
||||
vector.add(
|
||||
velocity,
|
||||
vector.new(
|
||||
self._collision_mod.x*self._collision_timer,
|
||||
self._collision_mod.y*self._collision_timer,
|
||||
self._collision_mod.z*self._collision_timer
|
||||
)
|
||||
)
|
||||
)
|
||||
else
|
||||
self.object:set_velocity(
|
||||
vector.add(
|
||||
{x=xv, y=velocity.y, z=zv},
|
||||
vector.new(
|
||||
self._collision_mod.x*self._collision_timer,
|
||||
self._collision_mod.y*self._collision_timer,
|
||||
self._collision_mod.z*self._collision_timer
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
self.object:set_rotation(vector.new(kart_rot_offsets.x, rotation+yaw_offset, 0))
|
||||
|
||||
-- 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
|
||||
|
||||
-- Disable regular smoke when boosting or stunned
|
||||
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
|
||||
elseif self._stun_timer > 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
|
||||
|
||||
-- Allow exiting the kart while in creative mode to test changes even when stunned;
|
||||
if control.aux1 and self._is_creative then
|
||||
if self._attached_player ~= nil 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
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
invector.functions.register_kart("kart", kart)
|
@ -1,568 +0,0 @@
|
||||
-- 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)
|
@ -1,2 +1,2 @@
|
||||
name = invector
|
||||
depends = solarsail
|
||||
depends = solarsail, superidx
|
BIN
mods/invector/models/default_kart.b3d
Normal file
BIN
mods/invector/models/item_rocket.b3d
Normal file
394
mods/invector/models/item_rocket.obj
Normal file
@ -0,0 +1,394 @@
|
||||
# Blender v2.79 (sub 0) OBJ File: 'RocketEnt.blend'
|
||||
# www.blender.org
|
||||
mtllib rocket_ent.mtl
|
||||
o Cube_Cube.001
|
||||
v 0.250000 -0.250000 0.250000
|
||||
v 0.250000 0.250000 0.250000
|
||||
v 0.250000 -0.250000 1.890625
|
||||
v 0.250000 0.250000 1.890625
|
||||
v -0.250000 -0.250000 0.250000
|
||||
v -0.250000 0.250000 0.250000
|
||||
v -0.250000 -0.250000 1.890625
|
||||
v -0.250000 0.250000 1.890625
|
||||
v 0.250000 -0.250000 1.890625
|
||||
v 0.250000 0.250000 1.890625
|
||||
v -0.250000 -0.250000 1.890625
|
||||
v -0.250000 0.250000 1.890625
|
||||
v 0.170860 0.170859 2.250000
|
||||
v 0.170860 -0.170859 2.250000
|
||||
v -0.170859 -0.170859 2.250000
|
||||
v -0.170859 0.170859 2.250000
|
||||
v 0.499488 -0.234375 -0.331009
|
||||
v 0.499488 0.234375 -0.331009
|
||||
v 0.242973 -0.234375 0.373761
|
||||
v 0.242973 0.234375 0.373761
|
||||
v 0.382026 -0.234375 -0.373761
|
||||
v 0.382026 0.234375 -0.373761
|
||||
v 0.125511 -0.234375 0.331008
|
||||
v 0.125511 0.234375 0.331008
|
||||
v -0.382027 -0.234375 -0.373761
|
||||
v -0.382027 0.234375 -0.373761
|
||||
v -0.125512 -0.234375 0.331009
|
||||
v -0.125512 0.234375 0.331009
|
||||
v -0.499489 -0.234375 -0.331008
|
||||
v -0.499489 0.234375 -0.331008
|
||||
v -0.242973 -0.234375 0.373761
|
||||
v -0.242973 0.234375 0.373761
|
||||
v 0.234375 0.382027 -0.373761
|
||||
v 0.234375 0.499488 -0.331009
|
||||
v 0.234375 0.125512 0.331008
|
||||
v 0.234375 0.242973 0.373761
|
||||
v -0.234375 0.382027 -0.373761
|
||||
v -0.234375 0.499488 -0.331008
|
||||
v -0.234375 0.125512 0.331008
|
||||
v -0.234375 0.242973 0.373761
|
||||
v 0.234375 -0.499488 -0.331009
|
||||
v 0.234375 -0.382027 -0.373761
|
||||
v 0.234375 -0.242973 0.373761
|
||||
v 0.234375 -0.125512 0.331008
|
||||
v -0.234375 -0.499488 -0.331008
|
||||
v -0.234375 -0.382027 -0.373761
|
||||
v -0.234375 -0.242973 0.373761
|
||||
v -0.234375 -0.125512 0.331008
|
||||
v 0.062500 0.250000 1.125000
|
||||
v 0.062500 0.375000 1.125000
|
||||
v 0.031250 0.250000 1.875000
|
||||
v 0.031250 0.312500 1.875000
|
||||
v -0.062500 0.250000 1.125000
|
||||
v -0.062500 0.375000 1.125000
|
||||
v -0.031250 0.250000 1.875000
|
||||
v -0.031250 0.312500 1.875000
|
||||
v 0.062500 -0.375000 1.125000
|
||||
v 0.062500 -0.250000 1.125000
|
||||
v 0.031250 -0.312500 1.875000
|
||||
v 0.031250 -0.250000 1.875000
|
||||
v -0.062500 -0.375000 1.125000
|
||||
v -0.062500 -0.250000 1.125000
|
||||
v -0.031250 -0.312500 1.875000
|
||||
v -0.031250 -0.250000 1.875000
|
||||
v -0.250000 0.062500 1.125000
|
||||
v -0.375000 0.062500 1.125000
|
||||
v -0.250000 0.031250 1.875000
|
||||
v -0.312500 0.031250 1.875000
|
||||
v -0.250000 -0.062500 1.125000
|
||||
v -0.375000 -0.062500 1.125000
|
||||
v -0.250000 -0.031250 1.875000
|
||||
v -0.312500 -0.031250 1.875000
|
||||
v 0.375000 0.062500 1.125000
|
||||
v 0.250000 0.062500 1.125000
|
||||
v 0.312500 0.031250 1.875000
|
||||
v 0.250000 0.031250 1.875000
|
||||
v 0.375000 -0.062500 1.125000
|
||||
v 0.250000 -0.062500 1.125000
|
||||
v 0.312500 -0.031250 1.875000
|
||||
v 0.250000 -0.031250 1.875000
|
||||
vt 0.531250 0.937500
|
||||
vt 0.531250 1.000000
|
||||
vt 0.312500 1.000000
|
||||
vt 0.312500 0.937500
|
||||
vt 0.312500 0.937500
|
||||
vt 0.312500 1.000000
|
||||
vt 0.093750 1.000000
|
||||
vt 0.093750 0.937500
|
||||
vt 0.125000 0.375000
|
||||
vt 0.125000 0.500000
|
||||
vt -0.000000 0.500000
|
||||
vt 0.000000 0.375000
|
||||
vt 0.468750 0.828125
|
||||
vt 0.468750 0.890625
|
||||
vt 0.250000 0.890625
|
||||
vt 0.250000 0.828125
|
||||
vt 0.234375 0.859375
|
||||
vt 0.234375 0.921875
|
||||
vt 0.015625 0.921875
|
||||
vt 0.015625 0.859375
|
||||
vt 0.171875 0.750000
|
||||
vt 0.171875 0.812500
|
||||
vt 0.109375 0.812500
|
||||
vt 0.109375 0.750000
|
||||
vt 0.265625 0.750000
|
||||
vt 0.265625 0.656250
|
||||
vt 0.328125 0.671875
|
||||
vt 0.328125 0.734375
|
||||
vt 0.187500 0.578125
|
||||
vt 0.093750 0.578125
|
||||
vt 0.109375 0.515625
|
||||
vt 0.171875 0.515625
|
||||
vt 0.109375 0.781250
|
||||
vt 0.015625 0.781250
|
||||
vt 0.031250 0.718750
|
||||
vt 0.093750 0.718750
|
||||
vt 0.328125 0.656250
|
||||
vt 0.328125 0.750000
|
||||
vt 0.265625 0.734375
|
||||
vt 0.265625 0.671875
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.250000 0.750000
|
||||
vt 0.250000 0.687500
|
||||
vt 0.265625 0.687500
|
||||
vt 0.265625 0.750000
|
||||
vt 0.203125 0.687500
|
||||
vt 0.203125 0.703125
|
||||
vt 0.078125 0.703125
|
||||
vt 0.078125 0.687500
|
||||
vt 0.187500 0.687500
|
||||
vt 0.187500 0.703125
|
||||
vt 0.062500 0.703125
|
||||
vt 0.062500 0.687500
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.250000 0.781250
|
||||
vt 0.250000 0.718750
|
||||
vt 0.265625 0.781250
|
||||
vt 0.203125 0.687500
|
||||
vt 0.203125 0.703125
|
||||
vt 0.078125 0.703125
|
||||
vt 0.078125 0.687500
|
||||
vt 0.187500 0.687500
|
||||
vt 0.187500 0.703125
|
||||
vt 0.062500 0.703125
|
||||
vt 0.062500 0.687500
|
||||
vt 0.218750 0.703125
|
||||
vt 0.218750 0.718750
|
||||
vt 0.093750 0.718750
|
||||
vt 0.093750 0.703125
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.187500 0.687500
|
||||
vt 0.187500 0.703125
|
||||
vt 0.062500 0.703125
|
||||
vt 0.062500 0.687500
|
||||
vt 0.203125 0.703125
|
||||
vt 0.203125 0.687500
|
||||
vt 0.265625 0.687500
|
||||
vt 0.265625 0.703125
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.203125 0.687500
|
||||
vt 0.203125 0.703125
|
||||
vt 0.078125 0.703125
|
||||
vt 0.078125 0.687500
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.203125 0.671875
|
||||
vt 0.203125 0.687500
|
||||
vt 0.078125 0.687500
|
||||
vt 0.078125 0.671875
|
||||
vt 0.203125 0.703125
|
||||
vt 0.203125 0.687500
|
||||
vt 0.265625 0.687500
|
||||
vt 0.265625 0.703125
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.265625 0.656250
|
||||
vt 0.265625 0.718750
|
||||
vt 0.140625 0.718750
|
||||
vt 0.140625 0.656250
|
||||
vt 0.203125 0.984375
|
||||
vt 0.203125 0.968750
|
||||
vt 0.265625 0.968750
|
||||
vt 0.265625 0.984375
|
||||
vt 0.265625 0.953125
|
||||
vt 0.296875 0.953125
|
||||
vt 0.296875 0.984375
|
||||
vt 0.265625 0.984375
|
||||
vt 0.203125 0.984375
|
||||
vt 0.203125 0.968750
|
||||
vt 0.265625 0.968750
|
||||
vt 0.265625 0.984375
|
||||
vt 0.468750 0.953125
|
||||
vt 0.500000 0.953125
|
||||
vt 0.500000 0.984375
|
||||
vt 0.468750 0.984375
|
||||
vt 0.156250 0.859375
|
||||
vt 0.171875 0.859375
|
||||
vt 0.171875 0.921875
|
||||
vt 0.156250 0.921875
|
||||
vt 0.203125 0.984375
|
||||
vt 0.203125 0.968750
|
||||
vt 0.265625 0.968750
|
||||
vt 0.265625 0.984375
|
||||
vt 0.265625 0.953125
|
||||
vt 0.296875 0.953125
|
||||
vt 0.296875 0.984375
|
||||
vt 0.265625 0.984375
|
||||
vt 0.203125 0.984375
|
||||
vt 0.203125 0.968750
|
||||
vt 0.265625 0.968750
|
||||
vt 0.265625 0.984375
|
||||
vt 0.468750 0.953125
|
||||
vt 0.500000 0.953125
|
||||
vt 0.500000 0.984375
|
||||
vt 0.468750 0.984375
|
||||
vt 0.328125 0.843750
|
||||
vt 0.343750 0.843750
|
||||
vt 0.343750 0.906250
|
||||
vt 0.328125 0.906250
|
||||
vt 0.156250 0.859375
|
||||
vt 0.171875 0.859375
|
||||
vt 0.171875 0.921875
|
||||
vt 0.156250 0.921875
|
||||
vt 0.265625 0.953125
|
||||
vt 0.296875 0.953125
|
||||
vt 0.296875 0.984375
|
||||
vt 0.265625 0.984375
|
||||
vt 0.328125 0.843750
|
||||
vt 0.343750 0.843750
|
||||
vt 0.343750 0.906250
|
||||
vt 0.328125 0.906250
|
||||
vt 0.468750 0.953125
|
||||
vt 0.500000 0.953125
|
||||
vt 0.500000 0.984375
|
||||
vt 0.468750 0.984375
|
||||
vt 0.203125 0.984375
|
||||
vt 0.203125 0.968750
|
||||
vt 0.265625 0.968750
|
||||
vt 0.265625 0.984375
|
||||
vt 0.156250 0.859375
|
||||
vt 0.171875 0.859375
|
||||
vt 0.171875 0.921875
|
||||
vt 0.156250 0.921875
|
||||
vt 0.265625 0.953125
|
||||
vt 0.296875 0.953125
|
||||
vt 0.296875 0.984375
|
||||
vt 0.265625 0.984375
|
||||
vt 0.328125 0.843750
|
||||
vt 0.343750 0.843750
|
||||
vt 0.343750 0.906250
|
||||
vt 0.328125 0.906250
|
||||
vt 0.468750 0.953125
|
||||
vt 0.500000 0.953125
|
||||
vt 0.500000 0.984375
|
||||
vt 0.468750 0.984375
|
||||
vt 0.203125 0.984375
|
||||
vt 0.203125 0.968750
|
||||
vt 0.265625 0.968750
|
||||
vt 0.265625 0.984375
|
||||
vt 0.343750 0.875000
|
||||
vt 0.343750 0.859375
|
||||
vt 0.406250 0.859375
|
||||
vt 0.406250 0.875000
|
||||
vn 1.0000 0.0000 -0.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn -0.0000 0.0000 -1.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn -0.9766 0.0000 0.2151
|
||||
vn 0.0000 -0.9766 0.2151
|
||||
vn 0.0000 0.9766 0.2151
|
||||
vn 0.9766 0.0000 0.2151
|
||||
vn 0.9397 0.0000 0.3420
|
||||
vn -0.3420 0.0000 0.9397
|
||||
vn -0.9397 0.0000 -0.3420
|
||||
vn 0.3420 0.0000 -0.9397
|
||||
vn 0.9397 0.0000 -0.3420
|
||||
vn 0.3420 0.0000 0.9397
|
||||
vn -0.9397 0.0000 0.3420
|
||||
vn -0.3420 0.0000 -0.9397
|
||||
vn 0.0000 -0.3420 0.9397
|
||||
vn -0.0000 0.3420 -0.9397
|
||||
vn -0.0000 -0.9397 -0.3420
|
||||
vn 0.0000 0.9397 0.3420
|
||||
vn 0.0000 0.3420 0.9397
|
||||
vn -0.0000 -0.3420 -0.9397
|
||||
vn 0.0000 -0.9397 0.3420
|
||||
vn -0.0000 0.9397 -0.3420
|
||||
vn 0.9991 0.0000 0.0416
|
||||
vn -0.9991 0.0000 0.0416
|
||||
vn 0.0000 0.9965 0.0830
|
||||
vn 0.0000 0.9991 0.0416
|
||||
vn 0.0000 -0.9991 0.0416
|
||||
vn -0.9965 0.0000 0.0830
|
||||
vn 0.9965 0.0000 0.0830
|
||||
vn 0.0000 -0.9965 0.0830
|
||||
usemtl None
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 7/5/2 8/6/2 6/7/2 5/8/2
|
||||
f 5/9/3 6/10/3 2/11/3 1/12/3
|
||||
f 3/13/4 7/14/4 5/15/4 1/16/4
|
||||
f 8/17/5 4/18/5 2/19/5 6/20/5
|
||||
f 14/21/6 13/22/6 16/23/6 15/24/6
|
||||
f 12/25/7 11/26/7 15/27/7 16/28/7
|
||||
f 11/29/8 9/30/8 14/31/8 15/32/8
|
||||
f 10/33/9 12/34/9 16/35/9 13/36/9
|
||||
f 9/37/10 10/38/10 13/39/10 14/40/10
|
||||
f 17/41/11 18/42/11 20/43/11 19/44/11
|
||||
f 19/45/12 20/46/12 24/47/12 23/48/12
|
||||
f 23/49/13 24/50/13 22/51/13 21/52/13
|
||||
f 21/53/14 22/54/14 18/55/14 17/56/14
|
||||
f 19/57/4 23/58/4 21/59/4 17/60/4
|
||||
f 24/61/5 20/62/5 18/63/5 22/64/5
|
||||
f 25/65/15 26/66/15 28/67/15 27/68/15
|
||||
f 27/69/16 28/70/16 32/71/16 31/72/16
|
||||
f 31/73/17 32/74/17 30/75/17 29/76/17
|
||||
f 29/77/18 30/78/18 26/66/18 25/79/18
|
||||
f 27/80/4 31/81/4 29/82/4 25/83/4
|
||||
f 32/84/5 28/85/5 26/86/5 30/87/5
|
||||
f 33/88/1 34/89/1 36/90/1 35/91/1
|
||||
f 35/92/19 36/93/19 40/94/19 39/95/19
|
||||
f 39/96/2 40/97/2 38/98/2 37/99/2
|
||||
f 37/100/20 38/101/20 34/102/20 33/103/20
|
||||
f 35/104/21 39/105/21 37/106/21 33/107/21
|
||||
f 40/108/22 36/109/22 34/110/22 38/111/22
|
||||
f 41/112/1 42/113/1 44/114/1 43/115/1
|
||||
f 43/116/23 44/117/23 48/118/23 47/119/23
|
||||
f 47/120/2 48/121/2 46/122/2 45/123/2
|
||||
f 45/124/24 46/125/24 42/126/24 41/127/24
|
||||
f 43/128/25 47/129/25 45/130/25 41/131/25
|
||||
f 48/132/26 44/133/26 42/134/26 46/135/26
|
||||
f 49/136/27 50/137/27 52/138/27 51/139/27
|
||||
f 51/140/6 52/141/6 56/142/6 55/143/6
|
||||
f 55/144/28 56/145/28 54/146/28 53/147/28
|
||||
f 53/148/3 54/149/3 50/150/3 49/151/3
|
||||
f 56/152/29 52/153/29 50/154/29 54/155/29
|
||||
f 57/156/27 58/157/27 60/158/27 59/159/27
|
||||
f 59/160/6 60/161/6 64/162/6 63/163/6
|
||||
f 63/164/28 64/165/28 62/166/28 61/167/28
|
||||
f 61/168/3 62/169/3 58/170/3 57/171/3
|
||||
f 64/172/5 60/173/5 58/174/5 62/175/5
|
||||
f 65/176/30 66/177/30 68/178/30 67/179/30
|
||||
f 67/180/6 68/181/6 72/182/6 71/183/6
|
||||
f 71/184/31 72/185/31 70/186/31 69/187/31
|
||||
f 69/188/3 70/189/3 66/190/3 65/191/3
|
||||
f 72/192/32 68/193/32 66/194/32 70/195/32
|
||||
f 73/196/30 74/197/30 76/198/30 75/199/30
|
||||
f 75/200/6 76/201/6 80/202/6 79/203/6
|
||||
f 79/204/31 80/205/31 78/206/31 77/207/31
|
||||
f 77/208/3 78/209/3 74/210/3 73/211/3
|
||||
f 75/212/33 79/213/33 77/214/33 73/215/33
|
||||
f 59/216/34 63/217/34 61/218/34 57/219/34
|
BIN
mods/invector/models/item_sand.b3d
Normal file
BIN
mods/invector/models/item_tnt.b3d
Normal file
BIN
mods/invector/models/starting_grid_markers.b3d
Normal file
BIN
mods/invector/models/ui_icon_eternity.b3d
Normal file
BIN
mods/invector/models/ui_icon_missing_tracks.b3d
Normal file
BIN
mods/invector/sounds/boost_pad_big.ogg
Normal file
BIN
mods/invector/sounds/boost_pad_small.ogg
Normal file
BIN
mods/invector/sounds/count_1.ogg
Normal file
BIN
mods/invector/sounds/count_2.ogg
Normal file
BIN
mods/invector/sounds/count_3.ogg
Normal file
BIN
mods/invector/sounds/count_go.ogg
Normal file
BIN
mods/invector/sounds/drift_boost_big.ogg
Normal file
BIN
mods/invector/sounds/drift_boost_med.ogg
Normal file
BIN
mods/invector/sounds/drift_boost_small.ogg
Normal file
BIN
mods/invector/sounds/drift_spark_big.ogg
Normal file
BIN
mods/invector/sounds/drift_spark_med.ogg
Normal file
BIN
mods/invector/sounds/drift_spark_small.ogg
Normal file
BIN
mods/invector/sounds/funk_of_the_coffee_break.ogg
Normal file
BIN
mods/invector/sounds/item_pad_online.ogg
Normal file
BIN
mods/invector/sounds/item_pickup.ogg
Normal file
BIN
mods/invector/sounds/item_voice_1.ogg
Normal file
BIN
mods/invector/sounds/item_voice_10.ogg
Normal file
BIN
mods/invector/sounds/item_voice_2.ogg
Normal file
BIN
mods/invector/sounds/item_voice_3.ogg
Normal file
BIN
mods/invector/sounds/item_voice_4.ogg
Normal file
BIN
mods/invector/sounds/item_voice_5.ogg
Normal file
BIN
mods/invector/sounds/item_voice_6.ogg
Normal file
BIN
mods/invector/sounds/item_voice_7.ogg
Normal file
BIN
mods/invector/sounds/item_voice_8.ogg
Normal file
BIN
mods/invector/sounds/item_voice_9.ogg
Normal file
BIN
mods/invector/sounds/the_ravers_of_eternity.ogg
Normal file
BIN
mods/invector/sounds/the_rush_of_eternity.ogg
Normal file
BIN
mods/invector/src_textures/jump_pad/0001.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
mods/invector/src_textures/jump_pad/0002.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
mods/invector/src_textures/jump_pad/0003.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
mods/invector/src_textures/jump_pad/0004.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
mods/invector/src_textures/jump_pad/0005.png
Normal file
After Width: | Height: | Size: 612 B |
BIN
mods/invector/src_textures/jump_pad/0006.png
Normal file
After Width: | Height: | Size: 6.8 KiB |