mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-11-23 22:22:56 +00:00
Remove old Lua scripting support
This commit is contained in:
parent
c33d387031
commit
60a6c97834
|
@ -45,7 +45,6 @@ backend-sdl = ["sdl2", "sdl2-sys"]
|
||||||
backend-glutin = ["winit", "glutin", "render-opengl"]
|
backend-glutin = ["winit", "glutin", "render-opengl"]
|
||||||
backend-horizon = []
|
backend-horizon = []
|
||||||
render-opengl = []
|
render-opengl = []
|
||||||
scripting-lua = ["lua-ffi"]
|
|
||||||
discord-rpc = ["discord-rich-presence"]
|
discord-rpc = ["discord-rich-presence"]
|
||||||
netplay = ["serde_cbor"]
|
netplay = ["serde_cbor"]
|
||||||
editor = []
|
editor = []
|
||||||
|
@ -54,7 +53,6 @@ android = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
#glutin = { path = "./3rdparty/glutin/glutin", optional = true }
|
#glutin = { path = "./3rdparty/glutin/glutin", optional = true }
|
||||||
#lua-ffi = { path = "./3rdparty/luajit-rs", optional = true }
|
|
||||||
#winit = { path = "./3rdparty/winit", optional = true, default_features = false, features = ["x11"] }
|
#winit = { path = "./3rdparty/winit", optional = true, default_features = false, features = ["x11"] }
|
||||||
#sdl2 = { path = "./3rdparty/rust-sdl2", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
|
#sdl2 = { path = "./3rdparty/rust-sdl2", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
|
||||||
#sdl2-sys = { path = "./3rdparty/rust-sdl2/sdl2-sys", optional = true, features = ["bundled", "static-link"] }
|
#sdl2-sys = { path = "./3rdparty/rust-sdl2/sdl2-sys", optional = true, features = ["bundled", "static-link"] }
|
||||||
|
@ -75,7 +73,6 @@ itertools = "0.10"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
lewton = { version = "0.10", optional = true }
|
lewton = { version = "0.10", optional = true }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
lua-ffi = { git = "https://github.com/doukutsu-rs/lua-ffi.git", rev = "e0b2ff5960f7ef9974aa9675cebe4907bee0134f", optional = true }
|
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
open = "3.2"
|
open = "3.2"
|
||||||
|
@ -90,7 +87,7 @@ serde_cbor = { version = "0.11", optional = true }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
strum = "0.24"
|
strum = "0.24"
|
||||||
strum_macros = "0.24"
|
strum_macros = "0.24"
|
||||||
# remove and replace when drain_filter is in stable
|
# remove and replace when extract_if is in stable
|
||||||
vec_mut_scan = "0.4"
|
vec_mut_scan = "0.4"
|
||||||
webbrowser = { version = "0.8.6", optional = true }
|
webbrowser = { version = "0.8.6", optional = true }
|
||||||
winit = { git = "https://github.com/doukutsu-rs/winit.git", rev = "878f206d19af01b0977277929eee5e32667453c0", optional = true, default_features = false, features = ["x11"] }
|
winit = { git = "https://github.com/doukutsu-rs/winit.git", rev = "878f206d19af01b0977277929eee5e32667453c0", optional = true, default_features = false, features = ["x11"] }
|
||||||
|
|
|
@ -319,11 +319,6 @@ pub fn init(options: LaunchOptions) -> GameResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut game = Box::pin(Game::new(&mut context)?);
|
let mut game = Box::pin(Game::new(&mut context)?);
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
unsafe {
|
|
||||||
(*game.state.get()).lua.update_refs(&mut *game.state.get(), &mut *context);
|
|
||||||
}
|
|
||||||
|
|
||||||
game.state.get_mut().fs_container = Some(fs_container);
|
game.state.get_mut().fs_container = Some(fs_container);
|
||||||
|
|
||||||
#[cfg(feature = "discord-rpc")]
|
#[cfg(feature = "discord-rpc")]
|
||||||
|
|
|
@ -244,10 +244,6 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mu
|
||||||
) -> GameResult {
|
) -> GameResult {
|
||||||
#[allow(unused_mut, unused_assignments)]
|
#[allow(unused_mut, unused_assignments)]
|
||||||
let mut npc_hook_ran = false;
|
let mut npc_hook_ran = false;
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
{
|
|
||||||
npc_hook_ran = state.lua.try_run_npc_hook(self.id, self.npc_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.npc_type {
|
match self.npc_type {
|
||||||
_ if npc_hook_ran => Ok(()),
|
_ if npc_hook_ran => Ok(()),
|
||||||
|
|
|
@ -1,539 +0,0 @@
|
||||||
-- sandboxing (still tbd)
|
|
||||||
--_ENV = {
|
|
||||||
-- ipairs = ipairs,
|
|
||||||
-- next = next,
|
|
||||||
-- pairs = pairs,
|
|
||||||
-- pcall = pcall,
|
|
||||||
-- tonumber = tonumber,
|
|
||||||
-- tostring = tostring,
|
|
||||||
-- type = type,
|
|
||||||
-- unpack = unpack,
|
|
||||||
-- coroutine = { create = coroutine.create, resume = coroutine.resume,
|
|
||||||
-- running = coroutine.running, status = coroutine.status,
|
|
||||||
-- wrap = coroutine.wrap },
|
|
||||||
-- string = { byte = string.byte, char = string.char, find = string.find,
|
|
||||||
-- format = string.format, gmatch = string.gmatch, gsub = string.gsub,
|
|
||||||
-- len = string.len, lower = string.lower, match = string.match,
|
|
||||||
-- rep = string.rep, reverse = string.reverse, sub = string.sub,
|
|
||||||
-- upper = string.upper },
|
|
||||||
-- table = { insert = table.insert, maxn = table.maxn, remove = table.remove,
|
|
||||||
-- sort = table.sort },
|
|
||||||
-- math = { abs = math.abs, acos = math.acos, asin = math.asin,
|
|
||||||
-- atan = math.atan, atan2 = math.atan2, ceil = math.ceil, cos = math.cos,
|
|
||||||
-- cosh = math.cosh, deg = math.deg, exp = math.exp, floor = math.floor,
|
|
||||||
-- fmod = math.fmod, frexp = math.frexp, huge = math.huge,
|
|
||||||
-- ldexp = math.ldexp, log = math.log, log10 = math.log10, max = math.max,
|
|
||||||
-- min = math.min, modf = math.modf, pi = math.pi, pow = math.pow,
|
|
||||||
-- rad = math.rad, random = math.random, sin = math.sin, sinh = math.sinh,
|
|
||||||
-- sqrt = math.sqrt, tan = math.tan, tanh = math.tanh },
|
|
||||||
-- os = { clock = os.clock, difftime = os.difftime, time = os.time },
|
|
||||||
--}
|
|
||||||
|
|
||||||
-- __doukutsu_rs is an internal API used meant to be used solely by doukutsu-rs to implement higher-level,
|
|
||||||
-- documented APIs and is a subject to change. Do NOT use it or your scripts will break.
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch = {}
|
|
||||||
doukutsu = {}
|
|
||||||
|
|
||||||
ModCS = {
|
|
||||||
Flag = {},
|
|
||||||
Game = {
|
|
||||||
Act = nil,
|
|
||||||
},
|
|
||||||
Mod = {},
|
|
||||||
NPC = {},
|
|
||||||
Organya = {},
|
|
||||||
Player = {},
|
|
||||||
Rect = {},
|
|
||||||
SkipFlag = {},
|
|
||||||
Sound = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._known_settings = {
|
|
||||||
["doukutsu-rs.intro.event_id"] = 0x1000,
|
|
||||||
["doukutsu-rs.intro.stage_id"] = 0x1001,
|
|
||||||
["doukutsu-rs.intro.pos"] = 0x1002,
|
|
||||||
["doukutsu-rs.new_game.event_id"] = 0x1003,
|
|
||||||
["doukutsu-rs.new_game.stage_id"] = 0x1004,
|
|
||||||
["doukutsu-rs.new_game.pos"] = 0x1005,
|
|
||||||
["doukutsu-rs.window.height"] = 0x1100,
|
|
||||||
["doukutsu-rs.window.width"] = 0x1101,
|
|
||||||
["doukutsu-rs.window.title"] = 0x1102,
|
|
||||||
["doukutsu-rs.font_scale"] = 0x2000,
|
|
||||||
["doukutsu-rs.tsc.encoding"] = 0x3000,
|
|
||||||
["doukutsu-rs.tsc.encrypted"] = 0x3001,
|
|
||||||
}
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._requires = {}
|
|
||||||
|
|
||||||
require = function(modname)
|
|
||||||
if __doukutsu_rs_runtime_dont_touch._requires[modname] == nil then
|
|
||||||
local mod = __doukutsu_rs:loadScript(modname)
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._requires[modname] = { mod = mod, loaded = True }
|
|
||||||
else
|
|
||||||
return __doukutsu_rs_runtime_dont_touch._requires[modname].mod
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._registered = {
|
|
||||||
tick = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._handlers = setmetatable({
|
|
||||||
tick = function(scene)
|
|
||||||
if type(ModCS.Game.Act) == 'function' then
|
|
||||||
pcall(ModCS.Game.Act)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, h in pairs(__doukutsu_rs_runtime_dont_touch._registered.tick) do
|
|
||||||
pcall(h, scene)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}, {
|
|
||||||
__index = function(self, event)
|
|
||||||
error("Unknown event: " .. event)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._registeredNPCHooks = {}
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._tryNPCHook = function(npc_id, npc_type)
|
|
||||||
local hook = __doukutsu_rs_runtime_dont_touch._registeredNPCHooks[npc_type]
|
|
||||||
if hook ~= nil then
|
|
||||||
local npc = __doukutsu_rs_runtime_dont_touch._getNPCRef(npc_id)
|
|
||||||
if npc ~= nil then
|
|
||||||
local status, err = pcall(hook, npc)
|
|
||||||
|
|
||||||
if not status then
|
|
||||||
print("error in npc handler:" .. err)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._initializeScript = function(script)
|
|
||||||
-- for compatibility with Lua 5.2+, copy-pasted from Lua mailing list
|
|
||||||
-- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
|
|
||||||
local _setfenv = setfenv or function(f, t)
|
|
||||||
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
|
|
||||||
local name
|
|
||||||
local up = 0
|
|
||||||
repeat
|
|
||||||
up = up + 1
|
|
||||||
name = debug.getupvalue(f, up)
|
|
||||||
until name == '_ENV' or name == nil
|
|
||||||
if name then
|
|
||||||
|
|
||||||
debug.upvaluejoin(f, up, function()
|
|
||||||
return name
|
|
||||||
end, 1)
|
|
||||||
debug.setupvalue(f, up, t)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
global_copy = {}
|
|
||||||
for k, v in pairs(_G) do
|
|
||||||
global_copy[k] = v
|
|
||||||
end
|
|
||||||
|
|
||||||
_setfenv(script, global_copy)
|
|
||||||
script()
|
|
||||||
end
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._createPlayerRef = function(player_id)
|
|
||||||
local player_ref = { id = player_id }
|
|
||||||
|
|
||||||
function player_ref.damage(self, value)
|
|
||||||
__doukutsu_rs:playerCommand(rawget(self, "id"), 0x200, value)
|
|
||||||
end
|
|
||||||
|
|
||||||
setmetatable(player_ref, {
|
|
||||||
__index = function(self, property)
|
|
||||||
if property == "x" then
|
|
||||||
return __doukutsu_rs:playerCommand(rawget(self, "id"), 0x10)
|
|
||||||
elseif property == "y" then
|
|
||||||
return __doukutsu_rs:playerCommand(rawget(self, "id"), 0x11)
|
|
||||||
elseif property == "velX" then
|
|
||||||
return __doukutsu_rs:playerCommand(rawget(self, "id"), 0x12)
|
|
||||||
elseif property == "velY" then
|
|
||||||
return __doukutsu_rs:playerCommand(rawget(self, "id"), 0x13)
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__newindex = function(self, property, val)
|
|
||||||
if property == "x" then
|
|
||||||
__doukutsu_rs:playerCommand(rawget(self, "id"), 0x110, val)
|
|
||||||
elseif property == "y" then
|
|
||||||
__doukutsu_rs:playerCommand(rawget(self, "id"), 0x111, val)
|
|
||||||
elseif property == "velX" then
|
|
||||||
__doukutsu_rs:playerCommand(rawget(self, "id"), 0x112, val)
|
|
||||||
elseif property == "velY" then
|
|
||||||
__doukutsu_rs:playerCommand(rawget(self, "id"), 0x113, val)
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
return player_ref
|
|
||||||
end
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._npcRefs = {}
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._getNPCRef = function(npc_id)
|
|
||||||
if __doukutsu_rs_runtime_dont_touch._npcRefs[npc_id] == nil then
|
|
||||||
local npc_ref = __doukutsu_rs_runtime_dont_touch._createNPCRef(npc_id)
|
|
||||||
if npc_ref == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._npcRefs[npc_id] = npc_ref
|
|
||||||
|
|
||||||
return npc_ref
|
|
||||||
end
|
|
||||||
|
|
||||||
return __doukutsu_rs_runtime_dont_touch._npcRefs[npc_id]
|
|
||||||
end
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._createNPCRef = function(npc_id)
|
|
||||||
local npc_ref = { id = npc_id }
|
|
||||||
|
|
||||||
function npc_ref.closestPlayer(self)
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x200)
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.random(self, min, max)
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x201, min, max)
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.hitLeftWall(self)
|
|
||||||
local flags = __doukutsu_rs:npcCommand(rawget(self, "id"), 0x0f)
|
|
||||||
return (flags & 1) ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.hitCeiling(self)
|
|
||||||
local flags = __doukutsu_rs:npcCommand(rawget(self, "id"), 0x0f)
|
|
||||||
return (flags & 2) ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.hitRightWall(self)
|
|
||||||
local flags = __doukutsu_rs:npcCommand(rawget(self, "id"), 0x0f)
|
|
||||||
return (flags & 4) ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.hitFloor(self)
|
|
||||||
local flags = __doukutsu_rs:npcCommand(rawget(self, "id"), 0x0f)
|
|
||||||
return (flags & 8) ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.parentNPC(self)
|
|
||||||
local id = __doukutsu_rs:npcCommand(rawget(self, "id"), 0x1c)
|
|
||||||
return __doukutsu_rs_runtime_dont_touch._getNPCRef(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.getAnimRect(self)
|
|
||||||
local l, t, r, b = __doukutsu_rs:npcCommand(rawget(self, "id"), 0x202)
|
|
||||||
return { l, t, r, b }
|
|
||||||
end
|
|
||||||
|
|
||||||
function npc_ref.setAnimRect(self, l, t, r, b)
|
|
||||||
if type(l) == "number" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x203, l, t, r, b)
|
|
||||||
elseif type(l) == "table" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x203, l[1], l[2], l[3], l[4])
|
|
||||||
else
|
|
||||||
error("Invalid parameters supplied.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
setmetatable(npc_ref, {
|
|
||||||
__index = function(self, property)
|
|
||||||
if property == "x" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x10)
|
|
||||||
elseif property == "y" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x11)
|
|
||||||
elseif property == "velX" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x12)
|
|
||||||
elseif property == "velY" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x13)
|
|
||||||
elseif property == "velX2" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x14)
|
|
||||||
elseif property == "velY2" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x15)
|
|
||||||
elseif property == "actionNum" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x16)
|
|
||||||
elseif property == "animNum" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x17)
|
|
||||||
elseif property == "actionCounter" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x18)
|
|
||||||
elseif property == "actionCounter2" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x19)
|
|
||||||
elseif property == "actionCounter3" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x1a)
|
|
||||||
elseif property == "animCounter" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x1b)
|
|
||||||
elseif property == "parentId" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x1c)
|
|
||||||
elseif property == "npcType" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x1d)
|
|
||||||
elseif property == "life" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x1e)
|
|
||||||
elseif property == "flagNum" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x1f)
|
|
||||||
elseif property == "eventNum" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x20)
|
|
||||||
elseif property == "direction" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x21)
|
|
||||||
elseif property == "rawDirection" then
|
|
||||||
return __doukutsu_rs:npcCommand(rawget(self, "id"), 0x22)
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__newindex = function(self, property, val)
|
|
||||||
if property == "x" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x110, val)
|
|
||||||
elseif property == "y" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x111, val)
|
|
||||||
elseif property == "velX" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x112, val)
|
|
||||||
elseif property == "velY" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x113, val)
|
|
||||||
elseif property == "velX2" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x114, val)
|
|
||||||
elseif property == "velY2" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x115, val)
|
|
||||||
elseif property == "actionNum" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x116, val)
|
|
||||||
elseif property == "animNum" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x117, val)
|
|
||||||
elseif property == "actionCounter" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x118, val)
|
|
||||||
elseif property == "actionCounter2" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x119, val)
|
|
||||||
elseif property == "actionCounter3" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x11a, val)
|
|
||||||
elseif property == "animCounter" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x11b, val)
|
|
||||||
elseif property == "parentId" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x11c, val)
|
|
||||||
elseif property == "npcType" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x11d, val)
|
|
||||||
elseif property == "life" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x11e, val)
|
|
||||||
elseif property == "flagNum" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x11f, val)
|
|
||||||
elseif property == "eventNum" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x120, val)
|
|
||||||
elseif property == "direction" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x121, val)
|
|
||||||
elseif property == "rawDirection" then
|
|
||||||
__doukutsu_rs:npcCommand(rawget(self, "id"), 0x121, val) -- <- not a typo
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
return npc_ref
|
|
||||||
end
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._playerRef0 = __doukutsu_rs_runtime_dont_touch._createPlayerRef(0)
|
|
||||||
__doukutsu_rs_runtime_dont_touch._playerRef1 = __doukutsu_rs_runtime_dont_touch._createPlayerRef(1)
|
|
||||||
|
|
||||||
doukutsu.rs = {}
|
|
||||||
setmetatable(doukutsu.rs, {
|
|
||||||
__index = function(self, property)
|
|
||||||
if property == "lightingMode" then
|
|
||||||
return __doukutsu_rs:stageCommand(0x01)
|
|
||||||
elseif property == "lightingEnabled" then
|
|
||||||
return __doukutsu_rs:stageCommand(0x02)
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__newindex = function(self, property, val)
|
|
||||||
if property == "lightingMode" then
|
|
||||||
__doukutsu_rs:stageCommand(0x101, val)
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
setmetatable(doukutsu, {
|
|
||||||
__index = function(self, property)
|
|
||||||
if property == "currentStage" then
|
|
||||||
local v = __doukutsu_rs:stageCommand(0x03)
|
|
||||||
if v == nil then
|
|
||||||
v = -1
|
|
||||||
end
|
|
||||||
|
|
||||||
return v
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
doukutsu.player = __doukutsu_rs_runtime_dont_touch._playerRef0
|
|
||||||
|
|
||||||
function doukutsu.playSfx(id)
|
|
||||||
__doukutsu_rs:playSfx(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function doukutsu.playSfxLoop(id)
|
|
||||||
__doukutsu_rs:playSfxLoop(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function doukutsu.playSong(id, fadeout)
|
|
||||||
__doukutsu_rs:playSong(id, fadeout)
|
|
||||||
end
|
|
||||||
|
|
||||||
function doukutsu.players()
|
|
||||||
return { __doukutsu_rs_runtime_dont_touch._playerRef0, __doukutsu_rs_runtime_dont_touch._playerRef1 }
|
|
||||||
end
|
|
||||||
|
|
||||||
function doukutsu.setSetting(key, value)
|
|
||||||
assert(type(key) == "string", "key must be a string.")
|
|
||||||
|
|
||||||
local id = __doukutsu_rs_runtime_dont_touch._known_settings[key]
|
|
||||||
if id ~= nil then
|
|
||||||
__doukutsu_rs:setEngineConstant(id, value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function doukutsu.setNPCHandler(npc_type, handler)
|
|
||||||
assert(type(npc_type) == "number", "npc type must be an integer.")
|
|
||||||
|
|
||||||
__doukutsu_rs_runtime_dont_touch._registeredNPCHooks[npc_type] = handler
|
|
||||||
end
|
|
||||||
|
|
||||||
function doukutsu.on(event, handler)
|
|
||||||
assert(type(event) == "string", "event type must be a string.")
|
|
||||||
assert(type(handler) == "function", "event handler must be a function.")
|
|
||||||
|
|
||||||
if __doukutsu_rs_runtime_dont_touch._registered[event] == nil then
|
|
||||||
error("Unknown event: " .. event)
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(__doukutsu_rs_runtime_dont_touch._registered[event], handler)
|
|
||||||
|
|
||||||
return handler
|
|
||||||
end
|
|
||||||
|
|
||||||
function doukutsu.removeHandler(event, handler)
|
|
||||||
assert(type(event) == "string", "event type must be a string.")
|
|
||||||
assert(type(handler) == "function", "event handler must be a function.")
|
|
||||||
|
|
||||||
if __doukutsu_rs_runtime_dont_touch._registered[event] == nil then
|
|
||||||
error("Unknown event: " .. event)
|
|
||||||
end
|
|
||||||
|
|
||||||
local index = -1
|
|
||||||
for i, h in pairs(__doukutsu_rs_runtime_dont_touch._registered[event]) do
|
|
||||||
if handler == h then
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if index ~= -1 then
|
|
||||||
table.remove(__doukutsu_rs_runtime_dont_touch._registered[event], index)
|
|
||||||
end
|
|
||||||
|
|
||||||
return handler
|
|
||||||
end
|
|
||||||
|
|
||||||
ModCS.Color = { r = 0, g = 0, b = 0 }
|
|
||||||
function ModCS.Color:_new(o)
|
|
||||||
o = o or {}
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
self.r = 0
|
|
||||||
self.g = 0
|
|
||||||
self.b = 0
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
ModCS.Color.Create = function(color)
|
|
||||||
return ModCS.Color:_new(nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Color:Set(r, g, b)
|
|
||||||
self.r = tonumber(r) or 0
|
|
||||||
self.g = tonumber(g) or 0
|
|
||||||
self.b = tonumber(b) or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Color:Box()
|
|
||||||
-- stub
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Mod.SetName(name)
|
|
||||||
-- stub
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Mod.SetAuthor(name)
|
|
||||||
-- stub
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Mod.SetVersion(v1, v2, v3, v4)
|
|
||||||
-- stub
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Mod.SetOpening(stage_id, event_id, ticks)
|
|
||||||
__doukutsu_rs:setEngineConstant(0x1000, event_id)
|
|
||||||
__doukutsu_rs:setEngineConstant(0x1001, stage_id)
|
|
||||||
-- todo ticks
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Mod.SetStart(stage_id, pos_x, pos_y, event_id)
|
|
||||||
__doukutsu_rs:setEngineConstant(0x1003, event_id)
|
|
||||||
__doukutsu_rs:setEngineConstant(0x1004, stage_id)
|
|
||||||
__doukutsu_rs:setEngineConstant(0x1005, pos_x, pos_y)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Flag.Set(id)
|
|
||||||
__doukutsu_rs:setFlag(id, True)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Flag.Unset(id)
|
|
||||||
__doukutsu_rs:setFlag(id, False)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Flag.Get(id)
|
|
||||||
return __doukutsu_rs:getFlag(id) or False
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.SkipFlag.Set(id)
|
|
||||||
__doukutsu_rs:setSkipFlag(id, True)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.SkipFlag.Unset(id)
|
|
||||||
__doukutsu_rs:setSkipFlag(id, False)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.SkipFlag.Get(id)
|
|
||||||
return __doukutsu_rs:getSkipFlag(id) or False
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Organya.Play(id)
|
|
||||||
__doukutsu_rs:playSong(id, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Sound.Play(id, loop)
|
|
||||||
if loop then
|
|
||||||
__doukutsu_rs:playSfxLoop(id)
|
|
||||||
else
|
|
||||||
__doukutsu_rs:playSfx(id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ModCS.Player.AddMaxLife(life)
|
|
||||||
-- stub
|
|
||||||
end
|
|
309
src/game/scripting/lua/doukutsu.d.ts
vendored
309
src/game/scripting/lua/doukutsu.d.ts
vendored
|
@ -1,309 +0,0 @@
|
||||||
declare type EventHandler<T> = (this: void, param: T) => void;
|
|
||||||
|
|
||||||
declare interface NPC {
|
|
||||||
/**
|
|
||||||
* The ID of NPC, equivalent to offset in NPC list of current scene.
|
|
||||||
*/
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type ID of NPC.
|
|
||||||
*/
|
|
||||||
npcType: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position of NPC in X axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
x: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position of NPC in Y axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
y: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Velocity of NPC in X axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
velX: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Velocity of NPC in Y axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
velY: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alternate velocity of NPC in X axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
velX2: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alternate velocity of NPC in Y axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
velY2: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current action id (the one that can be set with <ANP) of NPC (16-bit short integer).
|
|
||||||
*/
|
|
||||||
actionNum: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* First internal counter of the NPC (unsigned 16-bit short integer).
|
|
||||||
*/
|
|
||||||
actionCounter: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Second internal counter of the NPC (unsigned 16-bit short integer).
|
|
||||||
*/
|
|
||||||
actionCounter2: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Third internal counter of the NPC (unsigned 16-bit short integer).
|
|
||||||
*/
|
|
||||||
actionCounter3: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Health of the NPC.
|
|
||||||
*/
|
|
||||||
life: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag attached to this NPC.
|
|
||||||
*/
|
|
||||||
flagNum: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event attached to this NPC.
|
|
||||||
*/
|
|
||||||
eventNum: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ID of the parent NPC.
|
|
||||||
*/
|
|
||||||
parentId: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Direction of the NPC. Constrained to 0-4, use rawDirection to store values outside that range there.
|
|
||||||
*/
|
|
||||||
direction: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raw direction of the NPC, non-constrained and may have different value than direction field.
|
|
||||||
* Used by certain vanilla NPCs that use the direction field for different purposes.
|
|
||||||
*/
|
|
||||||
rawDirection: number;
|
|
||||||
|
|
||||||
hitCeiling(): boolean;
|
|
||||||
|
|
||||||
hitFloor(): boolean;
|
|
||||||
|
|
||||||
hitLeftWall(): boolean;
|
|
||||||
|
|
||||||
hitRightWall(): boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick a random integer from given range using RNG bound to this NPC.
|
|
||||||
* @param min minimum value.
|
|
||||||
* @param max maximum value.
|
|
||||||
*/
|
|
||||||
random(min: number, max: number): number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a reference to parent NPC, if present.
|
|
||||||
*/
|
|
||||||
parentNPC(): NPC | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a reference to closest player, non-nullable.
|
|
||||||
*/
|
|
||||||
closestPlayer(): DoukutsuPlayer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal counter used as index of animation frame. (unsigned 16-bit short integer)
|
|
||||||
*/
|
|
||||||
animNum: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal counter used by animation functions to count ticks between frames (unsigned 16-bit short integer).
|
|
||||||
*/
|
|
||||||
animCounter: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sprite bounds of the NPC.
|
|
||||||
*/
|
|
||||||
getAnimRect(): [number, number, number, number];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sprite bounds to specified rectangle.
|
|
||||||
* @param rect [left, top, right, bottom] rectangle.
|
|
||||||
*/
|
|
||||||
setAnimRect(rect: [number, number, number, number]): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sprite bounds to specified rectangle.
|
|
||||||
* @param left left bound
|
|
||||||
* @param top top bound
|
|
||||||
* @param right right bound
|
|
||||||
* @param bottom bottom bound
|
|
||||||
*/
|
|
||||||
setAnimRect(left: number, top: number, right: number, bottom: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an in-game player.
|
|
||||||
*/
|
|
||||||
declare interface DoukutsuPlayer {
|
|
||||||
/**
|
|
||||||
* The ID of player.
|
|
||||||
*/
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current position of player in X axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
x: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current position of player in Y axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
y: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current velocity of player in X axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
velX: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current velocity of player in Y axis (as floating point, not internal fixed point representation).
|
|
||||||
*/
|
|
||||||
velY: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Damages the player. Has no effect when invincibility is enabled.
|
|
||||||
* @param value number of health points to subtract.
|
|
||||||
*/
|
|
||||||
damage(value: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface DoukutsuRSApi {
|
|
||||||
/**
|
|
||||||
* Lighting mode of current stage.
|
|
||||||
* "none" - no lighting, similar to vanilla.
|
|
||||||
* "backgroundOnly" - lighting only affects background layer, similar to Switch version.
|
|
||||||
* "ambient" - lighting affects everything.
|
|
||||||
*/
|
|
||||||
lightingMode: "none" | "backgroundOnly" | "ambient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This property is true if lighting is enabled in settings.
|
|
||||||
*/
|
|
||||||
readonly lightingEnabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare namespace doukutsu {
|
|
||||||
/**
|
|
||||||
* A reference to main locally controlled player.
|
|
||||||
* In multiplayer context it refers to player who hosts the game.
|
|
||||||
*/
|
|
||||||
const player: DoukutsuPlayer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper property for doukutsu-rs specific APIs.
|
|
||||||
*/
|
|
||||||
const rs: DoukutsuRSApi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of current stage, read-only. Set to -1 if in menu.
|
|
||||||
*/
|
|
||||||
const currentStage: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays a sound effect with specified ID.
|
|
||||||
*/
|
|
||||||
function playSfx(id: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays a looping sound effect with specified ID.
|
|
||||||
*/
|
|
||||||
function playSfxLoop(id: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes current music to one with specified ID.
|
|
||||||
* If ID equals 0, the music is stopped.
|
|
||||||
* If ID equals 0 and fadeout is true, the music is faded out.
|
|
||||||
*/
|
|
||||||
function playMusic(id: number, fadeout: boolean = false): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of a certain TSC flag.
|
|
||||||
* @param id the flag number
|
|
||||||
*/
|
|
||||||
function getFlag(id: number): boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of a certain TSC flag.
|
|
||||||
* @param id the flag number
|
|
||||||
* * @param value the flag value
|
|
||||||
*/
|
|
||||||
function setFlag(id: number, value: boolean): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of a certain skip flag.
|
|
||||||
* @param id the flag number
|
|
||||||
*/
|
|
||||||
function getSkipFlag(id: number): boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of a certain skip flag.
|
|
||||||
* @param id the flag number
|
|
||||||
* @param value the flag value
|
|
||||||
*/
|
|
||||||
function setSkipFlag(id: number, value: boolean): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of players currently in game.
|
|
||||||
*/
|
|
||||||
function players(): DoukutsuPlayer[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a reference to NPC by it's ID (index in table).s
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
function getNPC(id: number): NPC;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an implementation-defined game setting.
|
|
||||||
* @param name
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
function setSetting(name: string, value: any): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an implementation-defined stage parameter.
|
|
||||||
* @param name
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
function setStageParam(name: string, value: any): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the handler override for specified NPC type. Passing a null removes the handler.
|
|
||||||
* @param npcType
|
|
||||||
* @param handler
|
|
||||||
*/
|
|
||||||
function setNPCHandler(npcType: number, handler: (this: void, npc: NPC) => void | null): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers an event handler called after all scripts are loaded.
|
|
||||||
* @param event event name
|
|
||||||
* @param handler event handler procedure
|
|
||||||
*/
|
|
||||||
function on(event: "init", handler: EventHandler<void>): EventHandler<void>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers an event handler called on each tick.
|
|
||||||
* @param event event name
|
|
||||||
* @param handler event handler procedure
|
|
||||||
*/
|
|
||||||
function on(event: "tick", handler: EventHandler<DoukutsuStage>): EventHandler<DoukutsuStage>;
|
|
||||||
|
|
||||||
function on<T>(event: string, handler: EventHandler<T>): EventHandler<T>;
|
|
||||||
}
|
|
|
@ -1,595 +0,0 @@
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
use lua_ffi::{c_int, LuaObject, State};
|
|
||||||
use lua_ffi::c_str;
|
|
||||||
use lua_ffi::ffi::luaL_Reg;
|
|
||||||
use lua_ffi::lua_method;
|
|
||||||
|
|
||||||
use crate::common::{Direction, Rect};
|
|
||||||
use crate::framework::filesystem;
|
|
||||||
use crate::game::scripting::lua::{check_status, DRS_RUNTIME_GLOBAL, LuaScriptingState};
|
|
||||||
use crate::game::scripting::tsc::text_script::TextScriptEncoding;
|
|
||||||
use crate::scene::game_scene::LightingMode;
|
|
||||||
use crate::util::rng::RNG;
|
|
||||||
|
|
||||||
pub struct Doukutsu {
|
|
||||||
pub ptr: *mut LuaScriptingState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl Doukutsu {
|
|
||||||
pub fn new(ptr: *mut LuaScriptingState) -> Doukutsu {
|
|
||||||
Doukutsu { ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_play_sfx(&self, state: &mut State) -> c_int {
|
|
||||||
if let Some(index) = state.to_int(2) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
game_state.sound_manager.play_sfx(index as u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_play_sfx_loop(&self, state: &mut State) -> c_int {
|
|
||||||
if let Some(index) = state.to_int(2) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
game_state.sound_manager.loop_sfx(index as u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_play_song(&self, state: &mut State) -> c_int {
|
|
||||||
if let Some(index) = state.to_int(2) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
let ctx = &mut (*(*self.ptr).ctx_ptr);
|
|
||||||
|
|
||||||
let fadeout = if let Some(fadeout_flag) = state.to_bool(3) { fadeout_flag } else { false };
|
|
||||||
|
|
||||||
let _ =
|
|
||||||
game_state.sound_manager.play_song(index as usize, &game_state.constants, &game_state.settings, ctx, fadeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_set_setting(&self, state: &mut State) -> c_int {
|
|
||||||
if let Some(index) = state.to_int(2) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
state.push(game_state.get_flag(index.max(0) as usize));
|
|
||||||
} else {
|
|
||||||
state.push_nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_get_flag(&self, state: &mut State) -> c_int {
|
|
||||||
if let Some(index) = state.to_int(2) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
state.push(game_state.get_flag(index.max(0) as usize));
|
|
||||||
} else {
|
|
||||||
state.push_nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_set_flag(&self, state: &mut State) -> c_int {
|
|
||||||
let flag_id = state.to_int(2);
|
|
||||||
let flag_val = state.to_bool(3);
|
|
||||||
|
|
||||||
if let (Some(flag_id), Some(flag_val)) = (flag_id, flag_val) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
game_state.set_flag(flag_id.max(0) as usize, flag_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_get_skip_flag(&self, state: &mut State) -> c_int {
|
|
||||||
if let Some(index) = state.to_int(2) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
state.push(game_state.get_skip_flag(index.max(0) as usize));
|
|
||||||
} else {
|
|
||||||
state.push_nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_set_skip_flag(&self, state: &mut State) -> c_int {
|
|
||||||
let flag_id = state.to_int(2);
|
|
||||||
let flag_val = state.to_bool(3);
|
|
||||||
|
|
||||||
if let (Some(flag_id), Some(flag_val)) = (flag_id, flag_val) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
game_state.set_skip_flag(flag_id.max(0) as usize, flag_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_set_engine_constant(&self, state: &mut State) -> c_int {
|
|
||||||
if let Some(constant_id) = state.to_int(2) {
|
|
||||||
let game_state = &mut (*(*self.ptr).state_ptr);
|
|
||||||
|
|
||||||
match constant_id {
|
|
||||||
0x1000 => {
|
|
||||||
// intro event
|
|
||||||
if let Some(intro_event) = state.to_int(3) {
|
|
||||||
game_state.constants.game.intro_event = intro_event as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x1001 => {
|
|
||||||
// intro stage
|
|
||||||
if let Some(intro_stage) = state.to_int(3) {
|
|
||||||
game_state.constants.game.intro_stage = intro_stage as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x1002 => {
|
|
||||||
// intro pos
|
|
||||||
if let (Some(intro_x), Some(intro_y)) = (state.to_int(3), state.to_int(4)) {
|
|
||||||
game_state.constants.game.intro_player_pos = (intro_x as i16, intro_y as i16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x1003 => {
|
|
||||||
// ng event
|
|
||||||
if let Some(ng_event) = state.to_int(3) {
|
|
||||||
game_state.constants.game.new_game_event = ng_event as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x1004 => {
|
|
||||||
// ng stage
|
|
||||||
if let Some(ng_stage) = state.to_int(3) {
|
|
||||||
game_state.constants.game.new_game_stage = ng_stage as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x1005 => {
|
|
||||||
// ng pos
|
|
||||||
if let (Some(ng_x), Some(ng_y)) = (state.to_int(3), state.to_int(4)) {
|
|
||||||
game_state.constants.game.new_game_player_pos = (ng_x as i16, ng_y as i16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x1100 => {
|
|
||||||
// window height
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
0x1101 => {
|
|
||||||
// window width
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
0x1102 => {
|
|
||||||
// window title
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
0x2000 => {
|
|
||||||
// font scale
|
|
||||||
if let Some(font_scale) = state.to_float(3) {
|
|
||||||
if font_scale > 0.0 {
|
|
||||||
game_state.font.scale(font_scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x3000 => {
|
|
||||||
// tsc encoding
|
|
||||||
if let Some(encoding) = state.to_str(3) {
|
|
||||||
let enc = TextScriptEncoding::from(encoding.clone());
|
|
||||||
|
|
||||||
if TextScriptEncoding::invalid_encoding(enc, &game_state) {
|
|
||||||
log::warn!("{} encoding is invalid", encoding.clone());
|
|
||||||
} else {
|
|
||||||
game_state.constants.textscript.encoding = enc;
|
|
||||||
log::debug!("{} encoding is set", encoding.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x3001 => {
|
|
||||||
// tsc encrypted
|
|
||||||
if let Some(encrypted) = state.to_bool(3) {
|
|
||||||
game_state.constants.textscript.encrypted = encrypted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_npc_command(&self, state: &mut State) -> c_int {
|
|
||||||
if (*self.ptr).game_scene.is_null() {
|
|
||||||
state.push_nil();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (Some(npc_id), Some(param_type)) = (state.to_int(2), state.to_int(3)) {
|
|
||||||
let game_scene = &mut *(*self.ptr).game_scene;
|
|
||||||
|
|
||||||
let npc = match game_scene.npc_list.get_npc(npc_id as usize) {
|
|
||||||
Some(npc) => npc,
|
|
||||||
None => {
|
|
||||||
state.push_nil();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match param_type {
|
|
||||||
0x0e => state.push(npc.cond.0 as i32),
|
|
||||||
0x0f => state.push(npc.flags.0),
|
|
||||||
0x10 => state.push(npc.x as f32 / 512.0),
|
|
||||||
0x11 => state.push(npc.y as f32 / 512.0),
|
|
||||||
0x12 => state.push(npc.vel_x as f32 / 512.0),
|
|
||||||
0x13 => state.push(npc.vel_y as f32 / 512.0),
|
|
||||||
0x14 => state.push(npc.vel_x2 as f32 / 512.0),
|
|
||||||
0x15 => state.push(npc.vel_y2 as f32 / 512.0),
|
|
||||||
0x16 => state.push(npc.action_num as i32),
|
|
||||||
0x17 => state.push(npc.anim_num as i32),
|
|
||||||
0x18 => state.push(npc.action_counter as i32),
|
|
||||||
0x19 => state.push(npc.action_counter2 as i32),
|
|
||||||
0x1a => state.push(npc.action_counter3 as i32),
|
|
||||||
0x1b => state.push(npc.anim_counter as i32),
|
|
||||||
0x1c => state.push(npc.parent_id as i32),
|
|
||||||
0x1d => state.push(npc.npc_type as i32),
|
|
||||||
0x1e => state.push(npc.life as i32),
|
|
||||||
0x1f => state.push(npc.flag_num as i32),
|
|
||||||
0x20 => state.push(npc.event_num as i32),
|
|
||||||
0x21 => state.push(npc.direction as i32),
|
|
||||||
0x22 => state.push(npc.tsc_direction as i32),
|
|
||||||
0x10e => {
|
|
||||||
if let Some(v) = state.to_uint(4) {
|
|
||||||
npc.cond.0 = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x10f => {
|
|
||||||
if let Some(v) = state.to_uint(4) {
|
|
||||||
npc.flags.0 = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x110 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set x
|
|
||||||
npc.x = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x111 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set y
|
|
||||||
npc.y = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x112 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set vel x
|
|
||||||
npc.vel_x = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x113 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set vel y
|
|
||||||
npc.vel_y = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x114 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set vel x 2
|
|
||||||
npc.vel_x2 = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x115 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set vel y 2
|
|
||||||
npc.vel_y2 = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x116 => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.action_num = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x117 => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.anim_num = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x118 => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.action_counter = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x119 => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.action_counter2 = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x11a => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.action_counter3 = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x11b => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.anim_counter = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x11c => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.parent_id = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x11d => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.npc_type = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x11e => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.life = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x11f => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.flag_num = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x120 => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.event_num = v as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x121 | 0x122 => {
|
|
||||||
if let Some(v) = state.to_int(4) {
|
|
||||||
npc.direction = Direction::from_int_facing(v as _).unwrap_or(Direction::Left);
|
|
||||||
npc.tsc_direction = v as _;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x200 => {
|
|
||||||
// get player idx
|
|
||||||
let index = npc.get_closest_player_idx_mut(&[&mut game_scene.player1, &mut game_scene.player2]);
|
|
||||||
state.push(index as i32);
|
|
||||||
}
|
|
||||||
0x201 => {
|
|
||||||
// random
|
|
||||||
if let (Some(min), Some(max)) = (state.to_int(4), state.to_int(5)) {
|
|
||||||
if max < min {
|
|
||||||
state.error("max < min");
|
|
||||||
} else {
|
|
||||||
state.push(npc.rng.range(min..max));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.error("Invalid parameters supplied.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x202 => {
|
|
||||||
// get anim rect
|
|
||||||
state.push(npc.anim_rect.left as i32);
|
|
||||||
state.push(npc.anim_rect.top as i32);
|
|
||||||
state.push(npc.anim_rect.right as i32);
|
|
||||||
state.push(npc.anim_rect.bottom as i32);
|
|
||||||
}
|
|
||||||
0x203 => {
|
|
||||||
// set anim rect
|
|
||||||
if let (Some(l), Some(t), Some(r), Some(b)) =
|
|
||||||
(state.to_int(4), state.to_int(5), state.to_int(6), state.to_int(7))
|
|
||||||
{
|
|
||||||
npc.anim_rect = Rect { left: l as u16, top: t as u16, right: r as u16, bottom: b as u16 };
|
|
||||||
} else {
|
|
||||||
state.error("Invalid parameters supplied.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => state.push_nil(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.push_nil()
|
|
||||||
}
|
|
||||||
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_player_command(&self, state: &mut State) -> c_int {
|
|
||||||
if (*self.ptr).game_scene.is_null() {
|
|
||||||
state.push_nil();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (Some(player_id), Some(param_type)) = (state.to_int(2), state.to_int(3)) {
|
|
||||||
let game_scene = &mut *(*self.ptr).game_scene;
|
|
||||||
|
|
||||||
let player = match player_id {
|
|
||||||
0 => &mut game_scene.player1,
|
|
||||||
1 => &mut game_scene.player2,
|
|
||||||
_ => {
|
|
||||||
state.push_nil();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match param_type {
|
|
||||||
0x0e => state.push(player.cond.0 as u32),
|
|
||||||
0x0f => state.push(player.flags.0),
|
|
||||||
0x10 => state.push(player.x as f32 / 512.0),
|
|
||||||
0x11 => state.push(player.y as f32 / 512.0),
|
|
||||||
0x12 => state.push(player.vel_x as f32 / 512.0),
|
|
||||||
0x13 => state.push(player.vel_y as f32 / 512.0),
|
|
||||||
0x110 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set x
|
|
||||||
player.x = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x111 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set y
|
|
||||||
player.y = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x112 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set vel x
|
|
||||||
player.vel_x = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x113 => {
|
|
||||||
if let Some(v) = state.to_float(4) {
|
|
||||||
// set vel y
|
|
||||||
player.vel_y = (v * 512.0) as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x200 => {
|
|
||||||
if let Some(points) = state.to_int(4) {
|
|
||||||
player.damage(points, &mut (*(*self.ptr).state_ptr), &game_scene.npc_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.push_nil();
|
|
||||||
}
|
|
||||||
_ => state.push_nil(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.push_nil()
|
|
||||||
}
|
|
||||||
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_stage_command(&self, state: &mut State) -> c_int {
|
|
||||||
if (*self.ptr).game_scene.is_null() {
|
|
||||||
state.push_nil();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(param_type) = state.to_int(2) {
|
|
||||||
let game_scene = &mut *(*self.ptr).game_scene;
|
|
||||||
let game_state = &mut *(*self.ptr).state_ptr;
|
|
||||||
|
|
||||||
match param_type {
|
|
||||||
0x01 => state.push(match game_scene.lighting_mode {
|
|
||||||
LightingMode::None => "none",
|
|
||||||
LightingMode::BackgroundOnly => "backgroundOnly",
|
|
||||||
LightingMode::Ambient => "ambient",
|
|
||||||
}),
|
|
||||||
0x02 => state.push(game_state.settings.shader_effects),
|
|
||||||
0x03 => state.push(game_scene.stage_id as u32),
|
|
||||||
0x101 => {
|
|
||||||
if let Some(v) = state.to_str(3) {
|
|
||||||
game_scene.lighting_mode = match v {
|
|
||||||
"none" => LightingMode::None,
|
|
||||||
"backgroundOnly" => LightingMode::BackgroundOnly,
|
|
||||||
"ambient" => LightingMode::Ambient,
|
|
||||||
_ => game_scene.lighting_mode,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
state.push_nil();
|
|
||||||
}
|
|
||||||
_ => state.push_nil(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.push_nil()
|
|
||||||
}
|
|
||||||
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn lua_load_script(&mut self, state: &mut State) -> c_int {
|
|
||||||
let lua_state = &mut (*self.ptr);
|
|
||||||
|
|
||||||
if let Some(name) = state.to_str(2) {
|
|
||||||
let name = name.to_string();
|
|
||||||
|
|
||||||
let ctx = &mut (*(*self.ptr).ctx_ptr);
|
|
||||||
|
|
||||||
let path = format!("/Scripts/{}.lua", name);
|
|
||||||
let lua_vfs_path = format!("@/Scripts/{}.lua", name);
|
|
||||||
|
|
||||||
fn raise_error(name: &str, state: &mut State, err: &str) {
|
|
||||||
let error_msg = format!("module '{}' not found: {}", name, err);
|
|
||||||
state.error(&error_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
match filesystem::open(ctx, &path) {
|
|
||||||
Ok(mut file) => {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
if let Err(res) = file.read_to_end(&mut buf) {
|
|
||||||
raise_error(&name, state, &res.to_string());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = state.load_buffer(&buf, &lua_vfs_path);
|
|
||||||
if let Err(err) = check_status(res, state) {
|
|
||||||
raise_error(&name, state, &err.to_string());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return match state.pcall(0, 1, 0) {
|
|
||||||
Ok(_) => 1,
|
|
||||||
Err((_, err)) => {
|
|
||||||
raise_error(&name, state, &err);
|
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
raise_error(&name, state, &err.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaObject for Doukutsu {
|
|
||||||
fn name() -> *const i8 {
|
|
||||||
c_str!("doukutsu-rs-internal")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lua_fns() -> Vec<luaL_Reg> {
|
|
||||||
vec![
|
|
||||||
lua_method!("playSfx", Doukutsu, Doukutsu::lua_play_sfx),
|
|
||||||
lua_method!("playSong", Doukutsu, Doukutsu::lua_play_song),
|
|
||||||
lua_method!("getFlag", Doukutsu, Doukutsu::lua_get_flag),
|
|
||||||
lua_method!("setFlag", Doukutsu, Doukutsu::lua_set_flag),
|
|
||||||
lua_method!("getSkipFlag", Doukutsu, Doukutsu::lua_get_skip_flag),
|
|
||||||
lua_method!("setSkipFlag", Doukutsu, Doukutsu::lua_set_skip_flag),
|
|
||||||
lua_method!("setEngineConstant", Doukutsu, Doukutsu::lua_set_engine_constant),
|
|
||||||
lua_method!("playerCommand", Doukutsu, Doukutsu::lua_player_command),
|
|
||||||
lua_method!("npcCommand", Doukutsu, Doukutsu::lua_npc_command),
|
|
||||||
lua_method!("stageCommand", Doukutsu, Doukutsu::lua_stage_command),
|
|
||||||
lua_method!("loadScript", Doukutsu, Doukutsu::lua_load_script),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaScriptingState {
|
|
||||||
pub fn try_run_npc_hook(&mut self, npc_id: u16, npc_type: u16) -> bool {
|
|
||||||
let mut result = false;
|
|
||||||
|
|
||||||
if let Some(state) = &mut self.state {
|
|
||||||
state.get_global(DRS_RUNTIME_GLOBAL);
|
|
||||||
state.get_field(-1, "_tryNPCHook");
|
|
||||||
|
|
||||||
state.push(npc_id as i32);
|
|
||||||
state.push(npc_type as i32);
|
|
||||||
|
|
||||||
if let Err((_, err)) = state.pcall(2, 1, 0) {
|
|
||||||
log::error!("npc_hook error: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(val) = state.to_bool(-1) {
|
|
||||||
result = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.pop(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,232 +0,0 @@
|
||||||
use std::io::Read;
|
|
||||||
use std::ptr::null_mut;
|
|
||||||
|
|
||||||
use lua_ffi::{c_int, State, ThreadStatus};
|
|
||||||
use lua_ffi::ffi::lua_State;
|
|
||||||
use lua_ffi::lua_fn;
|
|
||||||
use lua_ffi::types::LuaValue;
|
|
||||||
|
|
||||||
use crate::common::Rect;
|
|
||||||
use crate::framework::context::Context;
|
|
||||||
use crate::framework::error::{GameError, GameResult};
|
|
||||||
use crate::framework::filesystem;
|
|
||||||
use crate::framework::filesystem::File;
|
|
||||||
use crate::game::scripting::lua::doukutsu::Doukutsu;
|
|
||||||
use crate::game::shared_game_state::SharedGameState;
|
|
||||||
use crate::scene::game_scene::GameScene;
|
|
||||||
|
|
||||||
mod doukutsu;
|
|
||||||
mod scene;
|
|
||||||
|
|
||||||
pub struct LuaScriptingState {
|
|
||||||
state: Option<State>,
|
|
||||||
state_ptr: *mut SharedGameState,
|
|
||||||
ctx_ptr: *mut Context,
|
|
||||||
game_scene: *mut GameScene,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) static DRS_API_GLOBAL: &str = "__doukutsu_rs";
|
|
||||||
pub(crate) static DRS_RUNTIME_GLOBAL: &str = "__doukutsu_rs_runtime_dont_touch";
|
|
||||||
|
|
||||||
static BOOT_SCRIPT: &str = include_str!("boot.lua");
|
|
||||||
|
|
||||||
pub(crate) fn check_status(status: ThreadStatus, state: &mut State) -> GameResult {
|
|
||||||
match status {
|
|
||||||
ThreadStatus::Ok | ThreadStatus::Yield => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let error = state.to_str(-1).unwrap_or("???");
|
|
||||||
match status {
|
|
||||||
ThreadStatus::RuntimeError => Err(GameError::EventLoopError(format!("Lua Runtime Error: {}", error))),
|
|
||||||
ThreadStatus::SyntaxError => Err(GameError::EventLoopError(format!("Lua Syntax Error: {}", error))),
|
|
||||||
ThreadStatus::MemoryError => Err(GameError::EventLoopError(format!("Lua Memory Error: {}", error))),
|
|
||||||
ThreadStatus::MsgHandlerError => {
|
|
||||||
Err(GameError::EventLoopError(format!("Lua Message Handler Error: {}", error)))
|
|
||||||
}
|
|
||||||
ThreadStatus::FileError => Err(GameError::EventLoopError(format!("Lua File Error: {}", error))),
|
|
||||||
ThreadStatus::Unknown => Err(GameError::EventLoopError(format!("Unknown Error: {}", error))),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print(state: &mut State) -> c_int {
|
|
||||||
if let Some(msg) = state.to_str(1) {
|
|
||||||
log::info!("[Lua] {}", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaScriptingState {
|
|
||||||
pub fn new() -> LuaScriptingState {
|
|
||||||
LuaScriptingState { state: None, state_ptr: null_mut(), ctx_ptr: null_mut(), game_scene: null_mut() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_refs(&mut self, state: *mut SharedGameState, ctx: *mut Context) {
|
|
||||||
self.state_ptr = state;
|
|
||||||
self.ctx_ptr = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_game_scene(&mut self, game_scene: *mut GameScene) {
|
|
||||||
self.game_scene = game_scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_script(state: &mut State, path: &str, mut script: File) -> bool {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let res = script.read_to_end(&mut buf);
|
|
||||||
|
|
||||||
if let Err(err) = res {
|
|
||||||
log::warn!("Error reading script {}: {}", path, err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = format!("@{}", path);
|
|
||||||
let res = state.load_buffer(&buf, &name);
|
|
||||||
let res = check_status(res, state);
|
|
||||||
if let Err(err) = res {
|
|
||||||
log::warn!("Error loading script {}: {}", path, err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.get_global(DRS_RUNTIME_GLOBAL);
|
|
||||||
state.get_field(-1, "_initializeScript");
|
|
||||||
state.push_value(-3);
|
|
||||||
|
|
||||||
let res = state.pcall(1, 0, 0);
|
|
||||||
if let Err((_, err)) = res {
|
|
||||||
log::warn!("Error evaluating script {}: {}", path, err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("Successfully loaded Lua script: {}", path);
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reload_scripts(&mut self, ctx: &mut Context) -> GameResult {
|
|
||||||
let mut state = State::new();
|
|
||||||
state.open_libs();
|
|
||||||
|
|
||||||
state.push(lua_fn!(print));
|
|
||||||
state.set_global("print");
|
|
||||||
|
|
||||||
state.push(Doukutsu { ptr: self as *mut LuaScriptingState });
|
|
||||||
state.set_global(DRS_API_GLOBAL);
|
|
||||||
|
|
||||||
log::info!("Initializing Lua scripting engine...");
|
|
||||||
let res = state.do_string(BOOT_SCRIPT);
|
|
||||||
check_status(res, &mut state)?;
|
|
||||||
|
|
||||||
if filesystem::exists(ctx, "/drs-scripts/") {
|
|
||||||
let mut script_count = 0;
|
|
||||||
let files = filesystem::read_dir(ctx, "/drs-scripts/")?
|
|
||||||
.filter(|f| f.to_string_lossy().to_lowercase().ends_with(".lua"));
|
|
||||||
|
|
||||||
for file in files {
|
|
||||||
let path = file.clone();
|
|
||||||
|
|
||||||
match filesystem::open(ctx, file) {
|
|
||||||
Ok(script) => {
|
|
||||||
if LuaScriptingState::load_script(&mut state, &path.to_string_lossy(), script) {
|
|
||||||
script_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
log::warn!("Error opening script {:?}: {}", path, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if script_count > 0 {
|
|
||||||
log::info!("{} Lua scripts have been loaded.", script_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let modcs_path = "/Scripts/main.lua";
|
|
||||||
if filesystem::exists(ctx, modcs_path) {
|
|
||||||
log::info!("Loading ModCS main script...");
|
|
||||||
|
|
||||||
match filesystem::open(ctx, modcs_path) {
|
|
||||||
Ok(script) => {
|
|
||||||
if !LuaScriptingState::load_script(&mut state, modcs_path, script) {
|
|
||||||
log::warn!("Error loading ModCS main script.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
log::warn!("Error opening script {:?}: {}", modcs_path, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.state = Some(state);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaValue for Rect<u16> {
|
|
||||||
fn push_val(self, l: *mut lua_State) {
|
|
||||||
unsafe {
|
|
||||||
lua_ffi::ffi::lua_newtable(l);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.left as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 1);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.top as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 2);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.right as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 3);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaValue for Rect<i16> {
|
|
||||||
fn push_val(self, l: *mut lua_State) {
|
|
||||||
unsafe {
|
|
||||||
lua_ffi::ffi::lua_newtable(l);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.left as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 1);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.top as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 2);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.right as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 3);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaValue for Rect<i32> {
|
|
||||||
fn push_val(self, l: *mut lua_State) {
|
|
||||||
unsafe {
|
|
||||||
lua_ffi::ffi::lua_newtable(l);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.left as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 1);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.top as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 2);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.right as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 3);
|
|
||||||
lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaValue for Rect<f32> {
|
|
||||||
fn push_val(self, l: *mut lua_State) {
|
|
||||||
unsafe {
|
|
||||||
lua_ffi::ffi::lua_newtable(l);
|
|
||||||
lua_ffi::ffi::lua_pushnumber(l, self.left as f64);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 1);
|
|
||||||
lua_ffi::ffi::lua_pushnumber(l, self.top as f64);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 2);
|
|
||||||
lua_ffi::ffi::lua_pushnumber(l, self.right as f64);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 3);
|
|
||||||
lua_ffi::ffi::lua_pushnumber(l, self.bottom as f64);
|
|
||||||
lua_ffi::ffi::lua_rawseti(l, -2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
use lua_ffi::{c_int, LuaObject, State};
|
|
||||||
use lua_ffi::c_str;
|
|
||||||
use lua_ffi::ffi::luaL_Reg;
|
|
||||||
use lua_ffi::lua_method;
|
|
||||||
|
|
||||||
use crate::game::scripting::lua::{DRS_RUNTIME_GLOBAL, LuaScriptingState};
|
|
||||||
use crate::scene::game_scene::GameScene;
|
|
||||||
|
|
||||||
pub struct LuaGameScene {
|
|
||||||
valid_reference: bool,
|
|
||||||
ptr: *mut GameScene,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaGameScene {
|
|
||||||
unsafe fn lua_get_tick(&self, state: &mut State) -> c_int {
|
|
||||||
state.push((*self.ptr).tick as u32);
|
|
||||||
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn new(ptr: *mut GameScene) -> LuaGameScene {
|
|
||||||
LuaGameScene { valid_reference: true, ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LuaGameScene {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.valid_reference = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaObject for LuaGameScene {
|
|
||||||
fn name() -> *const i8 {
|
|
||||||
c_str!("GameScene")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lua_fns() -> Vec<luaL_Reg> {
|
|
||||||
vec![lua_method!("tick", LuaGameScene, LuaGameScene::lua_get_tick)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaScriptingState {
|
|
||||||
pub fn scene_tick(&mut self) {
|
|
||||||
if let Some(state) = &mut self.state {
|
|
||||||
let val = LuaGameScene::new(self.game_scene);
|
|
||||||
|
|
||||||
state.get_global(DRS_RUNTIME_GLOBAL);
|
|
||||||
state.get_field(-1, "_handlers");
|
|
||||||
state.get_field(-1, "tick");
|
|
||||||
|
|
||||||
state.push(val);
|
|
||||||
if let Err((_, err)) = state.pcall(1, 0, 0) {
|
|
||||||
println!("scene_tick error: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.pop(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"doukutsu.d.ts"
|
|
||||||
],
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es6",
|
|
||||||
"lib": [
|
|
||||||
"es2018"
|
|
||||||
],
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"strictNullChecks": true,
|
|
||||||
"strictFunctionTypes": true,
|
|
||||||
"baseUrl": "./",
|
|
||||||
"typeRoots": [
|
|
||||||
"./"
|
|
||||||
],
|
|
||||||
"types": [],
|
|
||||||
"noEmit": true,
|
|
||||||
"forceConsistentCasingInFileNames": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1 @@
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
pub mod lua;
|
|
||||||
pub mod tsc;
|
pub mod tsc;
|
||||||
|
|
|
@ -18,8 +18,6 @@ use crate::game::caret::{Caret, CaretType};
|
||||||
use crate::game::npc::NPCTable;
|
use crate::game::npc::NPCTable;
|
||||||
use crate::game::player::TargetPlayer;
|
use crate::game::player::TargetPlayer;
|
||||||
use crate::game::profile::GameProfile;
|
use crate::game::profile::GameProfile;
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
use crate::game::scripting::lua::LuaScriptingState;
|
|
||||||
use crate::game::scripting::tsc::credit_script::{CreditScript, CreditScriptVM};
|
use crate::game::scripting::tsc::credit_script::{CreditScript, CreditScriptVM};
|
||||||
use crate::game::scripting::tsc::text_script::{
|
use crate::game::scripting::tsc::text_script::{
|
||||||
ScriptMode, TextScript, TextScriptEncoding, TextScriptExecutionState, TextScriptVM,
|
ScriptMode, TextScript, TextScriptEncoding, TextScriptExecutionState, TextScriptVM,
|
||||||
|
@ -334,8 +332,6 @@ pub struct SharedGameState {
|
||||||
pub constants: EngineConstants,
|
pub constants: EngineConstants,
|
||||||
pub font: BMFont,
|
pub font: BMFont,
|
||||||
pub texture_set: TextureSet,
|
pub texture_set: TextureSet,
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
pub lua: LuaScriptingState,
|
|
||||||
pub sound_manager: SoundManager,
|
pub sound_manager: SoundManager,
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
pub save_slot: usize,
|
pub save_slot: usize,
|
||||||
|
@ -493,8 +489,6 @@ impl SharedGameState {
|
||||||
constants,
|
constants,
|
||||||
font,
|
font,
|
||||||
texture_set: TextureSet::new(),
|
texture_set: TextureSet::new(),
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
lua: LuaScriptingState::new(),
|
|
||||||
sound_manager,
|
sound_manager,
|
||||||
settings,
|
settings,
|
||||||
save_slot: 1,
|
save_slot: 1,
|
||||||
|
@ -613,8 +607,6 @@ impl SharedGameState {
|
||||||
|
|
||||||
pub fn start_new_game(&mut self, ctx: &mut Context) -> GameResult {
|
pub fn start_new_game(&mut self, ctx: &mut Context) -> GameResult {
|
||||||
self.reset();
|
self.reset();
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
self.lua.reload_scripts(ctx)?;
|
|
||||||
|
|
||||||
#[cfg(feature = "discord-rpc")]
|
#[cfg(feature = "discord-rpc")]
|
||||||
self.discord_rpc.update_difficulty(self.difficulty)?;
|
self.discord_rpc.update_difficulty(self.difficulty)?;
|
||||||
|
@ -638,9 +630,6 @@ impl SharedGameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_intro(&mut self, ctx: &mut Context) -> GameResult {
|
pub fn start_intro(&mut self, ctx: &mut Context) -> GameResult {
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
self.lua.reload_scripts(ctx)?;
|
|
||||||
|
|
||||||
let start_stage_id = self.constants.game.intro_stage as usize;
|
let start_stage_id = self.constants.game.intro_stage as usize;
|
||||||
|
|
||||||
if self.stages.len() < start_stage_id {
|
if self.stages.len() < start_stage_id {
|
||||||
|
@ -695,9 +684,6 @@ impl SharedGameState {
|
||||||
|
|
||||||
profile.apply(self, &mut next_scene, ctx);
|
profile.apply(self, &mut next_scene, ctx);
|
||||||
|
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
self.lua.reload_scripts(ctx)?;
|
|
||||||
|
|
||||||
#[cfg(feature = "discord-rpc")]
|
#[cfg(feature = "discord-rpc")]
|
||||||
self.discord_rpc.update_difficulty(self.difficulty)?;
|
self.discord_rpc.update_difficulty(self.difficulty)?;
|
||||||
|
|
||||||
|
|
|
@ -198,17 +198,6 @@ impl LiveDebugger {
|
||||||
self.flags_visible = !self.flags_visible;
|
self.flags_visible = !self.flags_visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
{
|
|
||||||
ui.same_line();
|
|
||||||
if ui.button("Reload Lua Scripts") {
|
|
||||||
if let Err(err) = state.lua.reload_scripts(ctx) {
|
|
||||||
log::error!("Error reloading scripts: {:?}", err);
|
|
||||||
self.error = Some(ImString::new(err.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if game_scene.player2.cond.alive() {
|
if game_scene.player2.cond.alive() {
|
||||||
if ui.button("Drop Player 2") {
|
if ui.button("Drop Player 2") {
|
||||||
game_scene.drop_player2();
|
game_scene.drop_player2();
|
||||||
|
|
|
@ -1665,8 +1665,6 @@ impl Scene for GameScene {
|
||||||
)?);
|
)?);
|
||||||
state.textscript_vm.suspend = false;
|
state.textscript_vm.suspend = false;
|
||||||
state.tile_size = self.stage.map.tile_size;
|
state.tile_size = self.stage.map.tile_size;
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
state.lua.set_game_scene(self as *mut _);
|
|
||||||
|
|
||||||
self.player1.controller = state.settings.create_player1_controller();
|
self.player1.controller = state.settings.create_player1_controller();
|
||||||
self.player2.controller = state.settings.create_player2_controller();
|
self.player2.controller = state.settings.create_player2_controller();
|
||||||
|
@ -1890,9 +1888,6 @@ impl Scene for GameScene {
|
||||||
self.flash.tick(state, ())?;
|
self.flash.tick(state, ())?;
|
||||||
self.text_boxes.tick(state, ())?;
|
self.text_boxes.tick(state, ())?;
|
||||||
|
|
||||||
#[cfg(feature = "scripting-lua")]
|
|
||||||
state.lua.scene_tick();
|
|
||||||
|
|
||||||
if state.control_flags.tick_world() {
|
if state.control_flags.tick_world() {
|
||||||
self.tick = self.tick.wrapping_add(1);
|
self.tick = self.tick.wrapping_add(1);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue