doukutsu-rs/src/scripting/boot.lua

483 lines
16 KiB
Lua

-- 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._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)
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.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)
__doukutsu_rs:playSong(id)
end
function doukutsu.players()
return { __doukutsu_rs_runtime_dont_touch._playerRef0, __doukutsu_rs_runtime_dont_touch._playerRef1 }
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)
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