mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-27 19:54:40 +00:00
Add generalized engine access and challenge mods, repair SKATE turn handling, and improve collision prediction and physics transitions. Reduce mod snapshot and material update overhead, preserve authored GLB materials with lighting and shadows, and improve CPU profiling.
281 lines
22 KiB
Lua
281 lines
22 KiB
Lua
-- Host-authoritative race to copy; only settled engine results count.
|
|
local POOL = {
|
|
{ name = "Kickflip", cat = "flip", hint = "RS down, flick UP+RIGHT", ghint = "RS down, flick UP+LEFT", color = { 0.23, 0.79, 0.98, 1 } },
|
|
{ name = "Heelflip", cat = "flip", hint = "RS down, flick UP+LEFT", ghint = "RS down, flick UP+RIGHT", color = { 1.00, 0.62, 0.15, 1 } },
|
|
{ name = "Pop Shuvit", cat = "shuvit", hint = "RS scoop RIGHT side", ghint = "RS scoop LEFT side", color = { 0.70, 0.50, 1.00, 1 } },
|
|
{ name = "360 Flip", cat = "flip", hint = "RS scoop L -> DOWN -> UR", ghint = "RS scoop R -> DOWN -> UL", color = { 1.00, 0.84, 0.20, 1 } },
|
|
{ name = "Nose Grab", cat = "grab", hint = "AIR: hold LT + RS DOWN", ghint = "AIR: hold RT + RS DOWN", color = { 0.35, 0.95, 0.45, 1 } },
|
|
{ name = "Ollie", cat = "ollie", hint = "RS down, pop straight UP", ghint = "RS down, pop straight UP", color = { 0.75, 0.85, 1.00, 1 } },
|
|
{ name = "Seatbelt", cat = "grab", hint = "AIR: hold LT + RS UP", ghint = "AIR: hold RT + RS UP", color = { 1.00, 0.45, 0.75, 1 } },
|
|
{ name = "Nollie", cat = "ollie", hint = "RS tap UP then DOWN", ghint = "RS tap UP then DOWN", color = { 0.80, 0.85, 0.90, 1 } },
|
|
{ name = "Varial Kickflip", cat = "flip", hint = "RS down-LEFT to UP+RIGHT", ghint = "RS down-RIGHT to UP+LEFT", color = { 0.20, 0.90, 0.80, 1 } },
|
|
{ name = "Varial Heelflip", cat = "flip", hint = "RS down-RIGHT to UP+LEFT", ghint = "RS down-LEFT to UP+RIGHT", color = { 1.00, 0.45, 0.30, 1 } },
|
|
{ name = "360 Pop Shuvit", cat = "shuvit", hint = "RS scoop L,DOWN,RIGHT", ghint = "RS scoop R,DOWN,LEFT", color = { 0.85, 0.70, 1.00, 1 } },
|
|
{ name = "FS Pop Shuvit", cat = "shuvit", hint = "RS scoop LEFT side", ghint = "RS scoop RIGHT side", color = { 0.55, 0.65, 1.00, 1 } },
|
|
{ name = "Double Kickflip", cat = "flip", hint = "Flick, hold to keep flipping", ghint = "Flick, hold to keep flipping", color = { 0.30, 0.70, 1.00, 1 } },
|
|
{ name = "Double Heelflip", cat = "flip", hint = "Flick, hold to keep flipping", ghint = "Flick, hold to keep flipping", color = { 1.00, 0.55, 0.20, 1 } },
|
|
{ name = "Nollie Kickflip", cat = "flip", hint = "Nollie: flick to heelside", ghint = "Nollie: flick to heelside", color = { 0.45, 0.85, 0.90, 1 } },
|
|
{ name = "Nollie Heelflip", cat = "flip", hint = "Nollie: flick to toeside", ghint = "Nollie: flick to toeside", color = { 0.90, 0.60, 0.35, 1 } },
|
|
{ name = "Hardflip", cat = "flip", hint = "FS shuvit + kickflip. Awkward!", ghint = "FS shuvit + kickflip. Awkward!", color = { 1.00, 0.30, 0.45, 1 } },
|
|
{ name = "Laser Flip", cat = "flip", hint = "FS 360 shuvit + heelflip", ghint = "FS 360 shuvit + heelflip", color = { 0.60, 1.00, 0.40, 1 } },
|
|
{ name = "Inward Heelflip", cat = "flip", hint = "Shuvit + heelflip", ghint = "Shuvit + heelflip", color = { 1.00, 0.50, 0.60, 1 } },
|
|
{ name = "360 Hardflip", cat = "flip", hint = "FS 360 shuvit + kickflip", ghint = "FS 360 shuvit + kickflip", color = { 0.95, 0.35, 0.55, 1 } },
|
|
{ name = "Melon", cat = "grab", hint = "Grab BS, push board forwards", ghint = "Grab BS, push board forwards", color = { 0.40, 0.90, 0.50, 1 } },
|
|
{ name = "Method", cat = "grab", hint = "Grab and tweak it out", ghint = "Grab and tweak it out", color = { 0.50, 1.00, 0.60, 1 } },
|
|
{ name = "Mute", cat = "grab", hint = "Board sideways, front grab", ghint = "Board sideways, front grab", color = { 0.65, 0.95, 0.45, 1 } },
|
|
{ name = "Stale", cat = "grab", hint = "Board sideways, back grab", ghint = "Board sideways, back grab", color = { 0.75, 0.90, 0.40, 1 } },
|
|
{ name = "Crail", cat = "grab", hint = "Nose reach, watch the hand", ghint = "Nose reach, watch the hand", color = { 0.45, 0.85, 0.55, 1 } },
|
|
{ name = "Tail Grab", cat = "grab", hint = "Push forward, back-hand grab", ghint = "Push forward, back-hand grab", color = { 0.55, 0.80, 0.50, 1 } },
|
|
{ name = "50-50", cat = "grind", fam = true, hint = "Both trucks. Line up rail", ghint = "Both trucks. Line up rail", color = { 0.90, 0.90, 0.95, 1 } },
|
|
{ name = "Smith Grind", cat = "grind", fam = true, hint = "Line up Smith and grind", ghint = "Line up Smith and grind", color = { 0.88, 0.86, 0.90, 1 } },
|
|
{ name = "Krooked", cat = "grind", fam = true, hint = "Nose into it, crooked grind", ghint = "Nose into it, crooked grind", color = { 0.86, 0.80, 0.92, 1 } },
|
|
{ name = "Nose Blunt", cat = "grind", fam = true, hint = "Nose up onto the blunt", ghint = "Nose up onto the blunt", color = { 0.84, 0.82, 0.94, 1 } },
|
|
{ name = "Feeble", cat = "grind", fam = true, hint = "Front truck hangs feeble", ghint = "Front truck hangs feeble", color = { 0.82, 0.86, 0.92, 1 } },
|
|
{ name = "Salad", cat = "grind", fam = true, hint = "Salad grind, hold it", ghint = "Salad grind, hold it", color = { 0.80, 0.90, 0.88, 1 } },
|
|
{ name = "Blunt Slide", cat = "grind", fam = true, hint = "Up on the blunt, slide it", ghint = "Up on the blunt, slide it", color = { 0.87, 0.83, 0.93, 1 } },
|
|
{ name = "Willie", cat = "grind", fam = true, hint = "Willie grind, steezy", ghint = "Willie grind, steezy", color = { 0.83, 0.89, 0.91, 1 } },
|
|
{ name = "Over-Krooks", cat = "grind", fam = true, also = { "overkrook", "overkrooked", "krooks" }, hint = "Over the krooks", ghint = "Over the krooks", color = { 0.85, 0.81, 0.93, 1 } },
|
|
{ name = "Nose Manual", ground = true, cat = "manual", fam = true, hint = "Push partway, sweet spot", ghint = "Push partway, sweet spot", color = { 0.70, 0.95, 0.85, 1 } },
|
|
{ name = "BS Fastplant", ground = true, cat = "manual", fam = true, hint = "BS grab, push off back foot", ghint = "BS grab, push off back foot", color = { 0.95, 0.75, 0.45, 1 } },
|
|
{ name = "BS Handplant", ground = true, cat = "manual", fam = true, hint = "Ramp angle, hold at edge", ghint = "Ramp angle, hold at edge", color = { 0.93, 0.70, 0.50, 1 } },
|
|
{ name = "FS 360 Pop Shuvit", cat = "shuvit", hint = "Find the start spot", ghint = "Find the start spot", color = { 0.65, 0.60, 1.00, 1 } },
|
|
{ name = "Nollie 360 Flip", cat = "flip", hint = "Nollie shuvit + kickflip", ghint = "Nollie shuvit + kickflip", color = { 0.50, 0.80, 0.95, 1 } },
|
|
{ name = "FS Fastplant", ground = true, cat = "manual", fam = true, hint = "FS grab, push off front foot", ghint = "FS grab, push off front foot", color = { 0.94, 0.78, 0.42, 1 } },
|
|
{ name = "Triple Kickflip", cat = "flip", hint = "Flick, hold to keep flipping", ghint = "Flick, hold to keep flipping", color = { 0.25, 0.65, 1.00, 1 } },
|
|
{ name = "Triple Heelflip", cat = "flip", hint = "Flick, hold to keep flipping", ghint = "Flick, hold to keep flipping", color = { 1.00, 0.50, 0.25, 1 } },
|
|
{ name = "360 Spin", cat = "spin", spin = 360, hint = "Spin 360 either way, stomp it", ghint = "Spin 360 either way, stomp it", color = { 0.40, 1.00, 0.70, 1 } },
|
|
{ name = "540 Spin", cat = "spin", spin = 540, hint = "Spin 540 either way, stomp it", ghint = "Spin 540 either way, stomp it", color = { 0.45, 0.95, 0.75, 1 } },
|
|
{ name = "720 Spin", cat = "spin", spin = 720, hint = "Spin 720 either way, stomp it", ghint = "Spin 720 either way, stomp it", color = { 0.50, 0.90, 0.80, 1 } },
|
|
{ name = "Kickflip 180", cat = "combo", combo = { flip = "KICKFLIP", pool = 1, spin = 180 }, hint = "Kickflip + spin 180 either way", ghint = "Kickflip + spin 180 either way", color = { 0.30, 0.75, 1.00, 1 } },
|
|
{ name = "Kickflip 360", cat = "combo", combo = { flip = "KICKFLIP", pool = 1, spin = 360 }, hint = "Kickflip + spin 360 either way", ghint = "Kickflip + spin 360 either way", color = { 0.28, 0.72, 1.00, 1 } },
|
|
{ name = "Heelflip 180", cat = "combo", combo = { flip = "HEELFLIP", pool = 2, spin = 180 }, hint = "Heelflip + spin 180 either way", ghint = "Heelflip + spin 180 either way", color = { 1.00, 0.58, 0.22, 1 } },
|
|
{ name = "Heelflip 360", cat = "combo", combo = { flip = "HEELFLIP", pool = 2, spin = 360 }, hint = "Heelflip + spin 360 either way", ghint = "Heelflip + spin 360 either way", color = { 1.00, 0.55, 0.20, 1 } },
|
|
{ name = "Ollie 180", cat = "combo", combo = { flip = "OLLIE", pool = 6, spin = 180 }, hint = "Ollie + spin 180 either way", ghint = "Ollie + spin 180 either way", color = { 0.72, 0.82, 1.00, 1 } },
|
|
{ name = "Ollie 360", cat = "combo", combo = { flip = "OLLIE", pool = 6, spin = 360 }, hint = "Ollie + spin 360 either way", ghint = "Ollie + spin 360 either way", color = { 0.70, 0.80, 1.00, 1 } },
|
|
{ name = "360 Flip 180", cat = "combo", combo = { flip = "360_FLIP", pool = 4, spin = 180 }, hint = "Treflip + spin 180 either way", ghint = "Treflip + spin 180 either way", color = { 0.95, 0.80, 0.25, 1 } },
|
|
{ name = "Melon 360", cat = "combo", combo = { flip = "MELON", pool = 21, spin = 360 }, hint = "Melon + spin 360 either way", ghint = "Melon + spin 360 either way", color = { 0.42, 0.88, 0.52, 1 } },
|
|
{ name = "Nose Grab 360", cat = "combo", combo = { flip = "NOSE_GRAB", pool = 5, spin = 360 }, hint = "Nose grab + spin 360 either way", ghint = "Nose grab + spin 360 either way", color = { 0.38, 0.92, 0.48, 1 } },
|
|
{ name = "Method 360", cat = "combo", combo = { flip = "METHOD", pool = 22, spin = 360 }, hint = "Method + spin 360 either way", ghint = "Method + spin 360 either way", color = { 0.52, 0.98, 0.62, 1 } },
|
|
{ name = "Superdude", cat = "grab", hint = "Grab + superman out huge", ghint = "Grab + superman out huge", color = { 0.60, 0.90, 0.50, 1 } },
|
|
{ name = "Miracle Whip", cat = "grab", hint = "Whip it. It's a grab.", ghint = "Whip it. It's a grab.", color = { 0.65, 0.88, 0.48, 1 } },
|
|
{ name = "Late Kickflip", cat = "flip", hint = "Ollie first, flip late", ghint = "Ollie first, flip late", color = { 0.32, 0.72, 1.00, 1 } },
|
|
{ name = "Late Heelflip", cat = "flip", hint = "Ollie first, flip late", ghint = "Ollie first, flip late", color = { 1.00, 0.57, 0.23, 1 } },
|
|
{ name = "Late Shuvit", cat = "shuvit", hint = "Ollie first, shuvit late", ghint = "Ollie first, shuvit late", color = { 0.72, 0.52, 1.00, 1 } },
|
|
{ name = "Kickflip Underflip", cat = "flip", hint = "Flip it, then flip it back", ghint = "Flip it, then flip it back", color = { 0.34, 0.70, 1.00, 1 } },
|
|
{ name = "Heelflip Underflip", cat = "flip", hint = "Flip it, then flip it back", ghint = "Flip it, then flip it back", color = { 1.00, 0.53, 0.25, 1 } },
|
|
{ name = "Nollie Pop Shuvit", cat = "shuvit", hint = "Nollie stance shuvit", ghint = "Nollie stance shuvit", color = { 0.68, 0.62, 1.00, 1 } },
|
|
{ name = "FS Fastplant", ground = true, cat = "manual", fam = true, hint = "FS grab, push front foot", ghint = "FS grab, push front foot", color = { 0.94, 0.76, 0.44, 1 } },
|
|
{ name = "Airwalk", cat = "grab", hint = "Airwalk it out", ghint = "Airwalk it out", color = { 0.62, 0.89, 0.49, 1 } },
|
|
{ name = "Coffin", ground = true, cat = "grab", hint = "Lay back, feet up. Coffin", ghint = "Lay back, feet up. Coffin", color = { 0.58, 0.86, 0.52, 1 } },
|
|
{ name = "Cross Bone", cat = "grab", hint = "Cross-bone grab", ghint = "Cross-bone grab", color = { 0.66, 0.87, 0.47, 1 } },
|
|
{ name = "Caveman", ground = true, cat = "ollie", hint = "Step off, jump back on", ghint = "Step off, jump back on", color = { 0.74, 0.84, 1.00, 1 } },
|
|
{ name = "Acid Drop", ground = true, cat = "ollie", hint = "Ride off a drop", ghint = "Ride off a drop", color = { 0.73, 0.83, 1.00, 1 } },
|
|
}
|
|
|
|
local game, received, epoch = nil, nil, 0
|
|
local last_publish, menu_stamp, watching = -1, nil, false
|
|
local deck = {}
|
|
local acknowledged
|
|
local function option(k,d) local v=sdk.settings[k];if v==nil then return d end;return v end
|
|
local function net() return sdk.net.info() end
|
|
local function me() return tostring(net().local_id or "0") end
|
|
local function host() return not net().active or net().is_host end
|
|
local function now() return sdk.time.elapsed end
|
|
local function canon(s)
|
|
s=tostring(s or ""):lower():gsub("id_trick_",""):gsub("_"," ")
|
|
for _,category in ipairs({"ground","trick","flip","grab","grind","authentic","air","metrics"}) do
|
|
s=s:gsub("%f[%w]"..category.."%f[%W]","")
|
|
end
|
|
s=s:gsub("%f[%w]switch%f[%W]",""):gsub("%f[%w]fakie%f[%W]","")
|
|
return (s:gsub("[^%w]",""))
|
|
end
|
|
local aliases={stalegrab="stale",stalefish="stale",crailgrab="crail",seatbeltgrab="seatbelt",
|
|
mutegrab="mute",superman="superdude",ngairwalk="airwalk",crossbone="crossbone",
|
|
crooked="krooked",crookedgrind="krooked",krooks="krooked",nosebluntslide="noseblunt",
|
|
popshoveit="popshuvit",shuvit="popshuvit",["360shuvit"]="360popshuvit"}
|
|
local function key(s) local k=canon(s);return aliases[k] or k end
|
|
local function matches(label,t,p)
|
|
-- The scorer appends body rotation after the board/grab name. A leading
|
|
-- 360 in "360 Flip" is a board trick, never evidence of a body spin.
|
|
local spin=tonumber(tostring(label):match("%s(%d+)%s*$"))
|
|
local base=tostring(label):gsub("%s%d+%s*$","")
|
|
if p.landed_spin_degrees~=nil then spin=math.abs(p.landed_spin_degrees) end
|
|
local raw=p.landed_trick_base or ""
|
|
if base:lower():find("halfcab",1,true) then spin=180;base=base:gsub("[FfBb][Ss] ?[Hh]alf[Cc]ab","") end
|
|
if t.spin then
|
|
return spin==t.spin
|
|
end
|
|
if t.combo then
|
|
return spin==t.combo.spin and (key(base:gsub("^[FfBb][Ss] ",""))==key(POOL[t.combo.pool].name) or raw~="" and key(raw)==key(POOL[t.combo.pool].name))
|
|
end
|
|
if t.cat=="grind" or t.cat=="grab" then base=base:gsub("^[FfBb][Ss] ","") end
|
|
local k=key(base)
|
|
if k==key(t.name) or raw~="" and key(raw)==key(t.name) then return true end
|
|
for _,a in ipairs(t.also or {}) do if k==key(a) then return true end end
|
|
return false
|
|
end
|
|
local function enabled(t)
|
|
-- The extracted scoring catalog has no settled Caveman/Acid Drop labels.
|
|
-- Keep their guide entries, but never issue an unconfirmable challenge.
|
|
if t.name=="Caveman" or t.name=="Acid Drop" then return false end
|
|
local keys={flip="allow_flips",shuvit="allow_shuvits",ollie="allow_ollies",grab="allow_grabs",grind="allow_grinds",manual="allow_manuals_plants",spin="allow_spins"}
|
|
if t.combo then return option("allow_spins",true) and enabled(POOL[t.combo.pool]) end
|
|
return option(keys[t.cat],true)
|
|
end
|
|
local function draw()
|
|
if #deck==0 then
|
|
local seen={}
|
|
for i,t in ipairs(POOL) do if enabled(t) and not seen[t.name] then deck[#deck+1]=i;seen[t.name]=true end end
|
|
for i=#deck,2,-1 do local j=math.random(i);deck[i],deck[j]=deck[j],deck[i] end
|
|
end
|
|
return table.remove(deck)
|
|
end
|
|
local function publish()
|
|
if not game then sdk.net.publish("match",nil);return end
|
|
local l={};for _,id in ipairs(game.ids) do l[#l+1]=tostring(game.l[id] or 0) end
|
|
sdk.net.publish("match",{e=game.e,p=game.p,n=game.n,c=game.c,ids=game.ids,l=table.concat(l),
|
|
r=math.ceil(math.max(0,game.until_at-now())),w=game.w or 0})
|
|
last_publish=now()
|
|
end
|
|
local function read()
|
|
if host() then return game end
|
|
local w=sdk.net.read(tostring(net().host_id),"match")
|
|
if type(w)~="table" or type(w.ids)~="table" or type(w.l)~="string" then received=nil;return nil end
|
|
local s={e=w.e,p=w.p,n=w.n,c=w.c,ids=w.ids,l={},r=w.r,w=w.w}
|
|
for i,id in ipairs(w.ids) do s.l[id]=tonumber(w.l:sub(i,i)) or 0 end
|
|
received=s;return s
|
|
end
|
|
local function release()
|
|
if watching then sdk.player.suspend(false);sdk.camera.watch(nil);watching=false end
|
|
end
|
|
local function controls(s)
|
|
if s and s.p=="copy" then
|
|
local token=tostring(s.e)..":"..s.n
|
|
if acknowledged~=token then
|
|
local p=sdk.player.read()
|
|
sdk.net.publish("round",{e=s.e,n=s.n,l=p.landing_seq or 0,b=p.bail_seq or 0})
|
|
acknowledged=token
|
|
end
|
|
end
|
|
local out=s and s.p~="done" and s.p~="stopped" and (s.l[me()] or 0)<=0
|
|
if not out then release();return end
|
|
sdk.player.suspend(true);watching=true
|
|
local target
|
|
if option("spec_cam",true) then for _,id in ipairs(s.ids) do if (s.l[id] or 0)>0 then target=id;break end end end
|
|
sdk.camera.watch(target)
|
|
end
|
|
local function menu()
|
|
local s=read();local active=s and s.p~="done" and s.p~="stopped"
|
|
local stamp=tostring(host())..tostring(active)
|
|
if stamp==menu_stamp then return end;menu_stamp=stamp
|
|
sdk.ui.menu("challenge",{title="Simon Says",section="Gamemodes",items={
|
|
{id="start",label="Start match",enabled=host() and not active,description="Everyone races to land the called trick. First clean ride-away wins."},
|
|
{id="stop",label="Stop match",enabled=host() and active},
|
|
}})
|
|
end
|
|
local function next_round()
|
|
game.n=game.n+1;game.c=draw()
|
|
if not game.c then game.p="stopped";publish();return end
|
|
game.p="countdown";game.until_at=now()+option("countdown_secs",3);game.w=0
|
|
game.base={};game.pending={};publish()
|
|
end
|
|
local function start()
|
|
epoch=epoch+1;deck={}
|
|
local ids,have={me()},{[me()]=true}
|
|
for _,id in ipairs(sdk.net.players()) do id=tostring(id);if not have[id] then ids[#ids+1]=id;have[id]=true end end
|
|
game={e=tostring(me())..":"..epoch..":"..now(),ids=ids,l={},n=0,until_at=now()}
|
|
for _,id in ipairs(ids) do game.l[id]=option("lives",3) end
|
|
next_round()
|
|
end
|
|
local function resolve(winner)
|
|
game.w=winner or 0
|
|
local cost=option("sudden_on",false) and game.n>=option("sudden_round",6) and option("sudden_cost",2) or 1
|
|
local alive=0
|
|
for i,id in ipairs(game.ids) do
|
|
if i~=winner then game.l[id]=math.max(0,game.l[id]-cost) end
|
|
if game.l[id]>0 then alive=alive+1 end
|
|
end
|
|
game.p=(alive==0 or #game.ids>1 and alive<=1) and "done" or "break"
|
|
game.until_at=now()+math.max(1,option("intermission_base",4)*option("esc_dur",0.85)^(game.n-1));publish()
|
|
end
|
|
local function advance()
|
|
if not game or game.p=="done" or game.p=="stopped" then return end
|
|
local connected={[me()]=true};for _,id in ipairs(sdk.net.players()) do connected[tostring(id)]=true end
|
|
for _,id in ipairs(game.ids) do if not connected[id] then game.l[id]=0 end end
|
|
if game.p=="countdown" and now()>=game.until_at then
|
|
game.p="copy";game.until_at=now()+math.max(5,option("round_time",45)*option("esc_dur",0.85)^(game.n-1))
|
|
game.base={}
|
|
publish()
|
|
elseif game.p=="break" and now()>=game.until_at then next_round()
|
|
elseif game.p=="copy" then
|
|
local ride=math.max(0.5,option("ride_away",2)-option("esc_ride",0.2)*(game.n-1))
|
|
for i,id in ipairs(game.ids) do
|
|
local p=sdk.player.skater(id);local b=game.base[id]
|
|
if not b then
|
|
local ack=sdk.net.read(id,"round")
|
|
if type(ack)=="table" and ack.e==game.e and ack.n==game.n and type(ack.l)=="number" and type(ack.b)=="number" then
|
|
b={l=ack.l,b=ack.b};game.base[id]=b
|
|
end
|
|
end
|
|
if p and b and game.l[id]>0 then
|
|
local landed,bail=p.landing_seq or 0,p.bail_seq or 0
|
|
if bail~=b.b or p.bailing or p.on_board==false then game.pending[id]=nil end
|
|
if landed>b.l and bail==b.b and not p.bailing and (p.landed_clean==nil and p.clean~=false or p.landed_clean==true) and not (p.landed_sketchy or p.sketchy) and matches(p.landed_trick,POOL[game.c],p) then
|
|
game.pending[id]={at=now()+ride,b=bail}
|
|
end
|
|
b.l,b.b=landed,bail
|
|
local pending=game.pending[id]
|
|
if pending and now()>=pending.at and bail==pending.b and p.on_board~=false and not p.bailing then resolve(i);return end
|
|
else game.pending[id]=nil end
|
|
end
|
|
if now()>=game.until_at then resolve(nil);return end
|
|
end
|
|
if now()-last_publish>=0.25 then publish() end
|
|
end
|
|
local card_token, card_at = nil, 0
|
|
local function card(s)
|
|
local t=s and POOL[s.c]
|
|
local token=s and tostring(s.e)..":"..s.n or "idle"
|
|
if token~=card_token then card_token,card_at=token,now() end
|
|
local accent=t and t.color or {0.23,0.79,0.98,1}
|
|
local pop=option("flash",true) and 1+math.max(0,0.15-(now()-card_at)*0.3) or 1
|
|
local hint=t and (option("stance","regular")=="goofy" and t.ghint or t.hint) or "First clean ride-away wins the round."
|
|
local remaining=s and (s.r or math.ceil(math.max(0,s.until_at-now()))) or 0
|
|
local items={
|
|
{key="back",type="rect",position={0,0},size={420,220},color={0.04,0.05,0.06,0.94}},
|
|
{key="accent",type="rect",position={0,0},size={420,6},color=accent},
|
|
{key="title",type="text",position={16,14},size={388,24},text="SIMON SAYS"..(s and " | ROUND "..s.n or ""),font_size=16,color=accent},
|
|
{key="call",type="text",position={16,46},size={388,48},text=t and t.name or "Ready to play",font_size=28,color={1,1,1,1}},
|
|
{key="hint",type="text",position={16,96},size={388,38},text=hint,font_size=14,color={0.85,0.9,0.95,1}},
|
|
{key="state",type="text",position={16,140},size={388,24},text=s and (s.p.." | "..remaining.."s | Lives: "..(s.l[me()] or 0)) or "Open Gamemodes > Simon Says",font_size=17,color=accent},
|
|
{key="help",type="text",position={16,182},size={388,22},text="Start / Stop: Gamemodes > Simon Says",font_size=13,color={0.6,0.65,0.7,1}},
|
|
}
|
|
sdk.ui.canvas("simon_card",{anchor="top_right",offset={24,24},size={420,220},scale=option("card_scale",1)*pop,visible=true,items=items})
|
|
end
|
|
local function hud()
|
|
local s=read();controls(s);menu();card(s)
|
|
if not option("show_hud",true) then sdk.ui.text("simon","");return end
|
|
if not s then sdk.ui.text("simon","SIMON SAYS | Open Gamemodes > Simon Says. All players must enable this mod.");return end
|
|
local t=POOL[s.c];local lines={"SIMON SAYS | Round "..s.n.." | "..s.p}
|
|
if t then
|
|
lines[#lines+1]="Call: "..t.name.." | "..tostring(s.r or math.ceil(math.max(0,s.until_at-now()))).."s"
|
|
lines[#lines+1]=option("stance","regular")=="goofy" and t.ghint or t.hint
|
|
end
|
|
for i,id in ipairs(s.ids) do local p=sdk.player.skater(id);lines[#lines+1]=(id==me() and "You" or p and p.name or id)..": "..(s.l[id] or 0).." lives"..(s.w==i and " - round winner" or "") end
|
|
if s.p=="done" then lines[#lines+1]="Match ended. Start again from Gamemodes > Simon Says." end
|
|
sdk.ui.text("simon",table.concat(lines,"\n"))
|
|
end
|
|
return {
|
|
on_load=function() menu();hud() end,
|
|
on_fixed_update=function() if host() then advance() end;hud() end,
|
|
on_event=function(e)
|
|
if e.name=="world_changed" then release();game,received=nil,nil;publish();menu()
|
|
elseif e.name=="menu_action" and e.menu=="challenge" and host() then
|
|
if e.item=="start" and (not game or game.p=="done" or game.p=="stopped") then start()
|
|
elseif e.item=="stop" and game then game.p="stopped";publish();release() end
|
|
hud()
|
|
end
|
|
end,
|
|
on_unload=function() release();sdk.net.publish("match",nil);sdk.ui.remove_menu("challenge");sdk.ui.remove("simon_card");sdk.ui.text("simon","") end,
|
|
}
|