Original GameJam Submission will be available under branch gamejam

master
Jordach 2021-12-21 23:24:17 +00:00
parent a60bb34a8c
commit eed20f2382
184 changed files with 3426 additions and 658 deletions

View File

@ -3,6 +3,9 @@
"minetest",
"invector",
"vector",
"Raycast"
"Raycast",
"superi",
"dump",
"VoxelArea"
]
}

BIN
menu/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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,
})

View 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
View 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,
})

View File

@ -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
View 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

View File

@ -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

View 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)

View File

@ -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)

View File

@ -1,2 +1,2 @@
name = invector
depends = solarsail
depends = solarsail, superidx

Binary file not shown.

Binary file not shown.

View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Some files were not shown because too many files have changed in this diff Show More