-- UMF Package generated with:
-- build.lua -s ../../physgun/umf.lua core entities vector tool util/visual.lua util/config.lua
--
local __RUNLATER = {} local UMF_RUNLATER = function(code) __RUNLATER[#__RUNLATER + 1] = code end
local __UMFLOADED = {["src/util/debug.lua"]=true,["src/core/hook.lua"]=true,["src/util/detouring.lua"]=true,["src/core/hooks_base.lua"]=true,["src/core/hooks_extra.lua"]=true,["src/util/registry.lua"]=true,["src/core/console_backend.lua"]=true,["src/core/_index.lua"]=true,["src/util/meta.lua"]=true,["src/vector/quat.lua"]=true,["src/vector/transform.lua"]=true,["src/vector/vector.lua"]=true,["src/entities/entity.lua"]=true,["src/entities/body.lua"]=true,["src/entities/joint.lua"]=true,["src/entities/light.lua"]=true,["src/entities/location.lua"]=true,["src/entities/player.lua"]=true,["src/entities/screen.lua"]=true,["src/entities/shape.lua"]=true,["src/entities/trigger.lua"]=true,["src/entities/vehicle.lua"]=true,["src/util/xml.lua"]=true,["src/animation/armature.lua"]=true,["src/tool/tool.lua"]=true,["src/util/visual.lua"]=true,["src/util/config.lua"]=true,} local UMF_SOFTREQUIRE = function(name) return __UMFLOADED[name] end
(function() util = util or {} function util.current_line( level ) level = (level or 0) + 3 local _, line = pcall( error, "-", level ) if line == "-" then _, line = pcall( error, "-", level + 1 ) if line == "-" then return end line = "[C]:?" else line = line:sub( 1, -4 ) end return line end function util.stacktrace( start ) start = (start or 0) + 3 local stack, last = {}, nil for i = start, 32 do local _, line = pcall( error, "-", i ) if line == "-" then if last == "-" then break end else if last == "-" then stack[#stack + 1] = "[C]:?" end stack[#stack + 1] = line:sub( 1, -4 ) end last = line end return stack end end)();
(function() if hook then return end local hook_table = {} local hook_compiled = {} local function recompile( event ) local hooks = {} for k, v in pairs( hook_table[event] ) do hooks[#hooks + 1] = v end hook_compiled[event] = hooks end hook = { table = hook_table } function hook.add( event, identifier, func ) assert( type( event ) == "string", "Event must be a string" ) if func then assert( identifier ~= nil, "Identifier must not be nil" ) assert( type( func ) == "function", "Callback must be a function" ) else assert( type( identifier ) == "function", "Callback must be a function" ) end hook_table[event] = hook_table[event] or {} hook_table[event][identifier] = func or identifier recompile( event ) return identifier end function hook.remove( event, identifier ) assert( type( event ) == "string", "Event must be a string" ) assert( identifier ~= nil, "Identifier must not be nil" ) if hook_table[event] then hook_table[event][identifier] = nil if next( hook_table[event] ) == nil then hook_table[event] = nil hook_compiled[event] = nil else recompile( event ) end end end function hook.run( event, ... ) local hooks = hook_compiled[event] if not hooks then return end for i = 1, #hooks do local a, b, c, d, e = hooks[i]( ... ) if a ~= nil then return a, b, c, d, e end end end function hook.saferun( event, ... ) local hooks = hook_compiled[event] if not hooks then return end for i = 1, #hooks do local s, a, b, c, d, e = softassert( pcall( hooks[i], ... ) ) if s and a ~= nil then return a, b, c, d, e end end end function hook.saferun_debug( event, ... ) local hooks = hook_compiled[event] if not hooks then return end local args = { ... } for i = 1, #hooks do local s, a, b, c, d, e = xpcall( function() return hooks[i]( unpack( args ) ) end, function( err ) warning( err, 2 ) end ) if s and a ~= nil then return a, b, c, d, e end end end function hook.used( event ) return hook_table[event] end end)();
(function() if DETOUR then return end local original = {} local function call_original( name, ... ) local fn = original[name] if fn then return fn( ... ) end end local detoured = {} function DETOUR( name, generator ) original[name] = original[name] or rawget( _G, name ) detoured[name] = generator( function( ... ) return call_original( name, ... ) end ) rawset( _G, name, nil ) end setmetatable( _G, { __index = detoured, __newindex = function( self, k, v ) if detoured[k] then original[k] = v else rawset( self, k, v ) end end, } ) end)();
(function() UMF_RUNLATER "UpdateQuickloadPatch()" local hook = hook local function checkoriginal( b, ... ) if not b then printerror( ... ) return end return ... end local function simple_detour( name ) local event = "base." .. name DETOUR( name, function( original ) return function( ... ) hook.saferun( event, ... ) return checkoriginal( pcall( original, ... ) ) end end ) end local detours = { "init", "tick", "update", "render", "postUpdate", } for i = 1, #detours do simple_detour( detours[i] ) end function shoulddraw( kind ) return hook.saferun( "api.shoulddraw", kind ) ~= false end DETOUR( "draw", function( original ) return function( dt ) if shoulddraw( "all" ) then hook.saferun( "base.predraw", dt ) if shoulddraw( "original" ) then checkoriginal( pcall( original, dt ) ) end hook.saferun( "base.draw", dt ) end end end ) DETOUR( "Command", function( original ) return function( cmd, ... ) hook.saferun( "base.precmd", cmd, { ... } ) local a, b, c, d, e, f = original( cmd, ... ) hook.saferun( "base.postcmd", cmd, { ... }, { a, b, c, d, e, f } ) end end ) local savedtypes = { ["function"] = true, ["userdata"] = true, ["thread"] = true } local saved local function searchtable(t, bck) if bck[t] then return end bck[t] = true local rt, dosave = {}, false for k, v in pairs(t) do local vt = type(v) if vt == "table" then local st = searchtable(v, bck) if st then dosave = true rt[k] = st end elseif savedtypes[vt] then dosave = true rt[k] = v end end bck[t] = false if dosave then return rt end end local function restoretable(t, dst) if not t then return end for k, v in pairs(t) do if type(v) == "table" then dst[k] = dst[k] or {} restoretable(v, dst[k]) else dst[k] = v end end end function UpdateQuickloadPatch() saved = searchtable(_G, {}) end DETOUR( "handleCommand", function( original ) return function( command, ... ) if command == "quickload" then restoretable(saved, _G) end hook.saferun( "base.command." .. command, ... ) return original( command, ... ) end end ) hook.add( "base.tick", "api.firsttick", function() hook.remove( "base.tick", "api.firsttick" ) hook.saferun( "api.firsttick" ) if type( firsttick ) == "function" then firsttick() end end ) end)();
(function() function IsPlayerInVehicle() return GetBool( "game.player.usevehicle" ) end local tool = GetString( "game.player.tool" ) local invehicle = IsPlayerInVehicle() local keyboardkeys = { "esc", "up", "down", "left", "right", "space", "interact", "return" } for i = 97, 97 + 25 do keyboardkeys[#keyboardkeys + 1] = string.char( i ) end local function checkkeys( func, mousehook, keyhook ) if hook.used( keyhook ) and func( "any" ) then for i = 1, #keyboardkeys do if func( keyboardkeys[i] ) then hook.saferun( keyhook, keyboardkeys[i] ) end end end if hook.used( mousehook ) then if func( "lmb" ) then hook.saferun( mousehook, "lmb" ) end if func( "rmb" ) then hook.saferun( mousehook, "rmb" ) end end end local mousekeys = { "lmb", "rmb", "mmb" } local logicalinputs = { "up", "down", "left", "right", "interact", "flashlight", "jump", "crouch", "usetool", "grab", "handbrake", "map", "pause", "vehicleraise", "vehiclelower", "vehicleaction", "camerax", "cameray", } local heldkeys = {} hook.add( "base.tick", "api.default_hooks", function() if InputLastPressedKey then if hook.used( "api.mouse.pressed" ) or hook.used( "api.mouse.released" ) then for i = 1, #mousekeys do local k = mousekeys[i] if InputPressed( k ) then hook.saferun( "api.mouse.pressed", k ) elseif InputReleased( k ) then hook.saferun( "api.mouse.released", k ) end end end if hook.used( "api.input.pressed" ) or hook.used( "api.input.released" ) then for i = 1, #logicalinputs do local k = logicalinputs[i] if InputPressed( k ) then hook.saferun( "api.input.pressed", k ) elseif InputReleased( k ) then hook.saferun( "api.input.released", k ) end end end if hook.used( "api.key.pressed" ) or hook.used( "api.key.released" ) then local lastkey = InputLastPressedKey() if lastkey ~= "" then heldkeys[lastkey] = true hook.saferun( "api.key.pressed", lastkey ) end for key in pairs( heldkeys ) do if not InputDown( key ) then heldkeys[key] = nil hook.saferun( "api.key.released", key ) break end end end if hook.used( "api.mouse.wheel" ) then local wheel = InputValue( "mousewheel" ) if wheel ~= 0 then hook.saferun( "api.mouse.wheel", wheel ) end end if hook.used( "api.mouse.move" ) then local mousedx = InputValue( "mousedx" ) local mousedy = InputValue( "mousedy" ) if mousedx ~= 0 or mousedy ~= 0 then hook.saferun( "api.mouse.move", mousedx, mousedy ) end end if hook.used( "api.input.camera" ) then local camerax = InputValue( "camerax" ) local cameray = InputValue( "cameray" ) if camerax ~= 0 or cameray ~= 0 then hook.saferun( "api.input.camera", camerax, cameray ) end end elseif InputPressed then checkkeys( InputPressed, "api.mouse.pressed", "api.key.pressed" ) checkkeys( InputReleased, "api.mouse.released", "api.key.released" ) local wheel = InputValue( "mousewheel" ) if wheel ~= 0 then hook.saferun( "api.mouse.wheel", wheel ) end local mousedx = InputValue( "mousedx" ) local mousedy = InputValue( "mousedy" ) if mousedx ~= 0 or mousedy ~= 0 then hook.saferun( "api.mouse.move", mousedx, mousedy ) end end local n_invehicle = IsPlayerInVehicle() if invehicle ~= n_invehicle then hook.saferun( n_invehicle and "api.player.enter_vehicle" or "api.player.exit_vehicle", n_invehicle and GetPlayerVehicle() ) invehicle = n_invehicle end local n_tool = GetString( "game.player.tool" ) if tool ~= n_tool then hook.saferun( "api.player.switch_tool", n_tool, tool ) tool = n_tool end end ) end)();
(function() local coreloaded = UMF_SOFTREQUIRE "src/core/_index.lua" util = util or {} do local serialize_any, serialize_table serialize_table = function( val, bck ) if bck[val] then return "nil" end bck[val] = true local entries = {} for k, v in pairs( val ) do entries[#entries + 1] = string.format( "[%s] = %s", serialize_any( k, bck ), serialize_any( v, bck ) ) end return string.format( "{%s}", table.concat( entries, "," ) ) end serialize_any = function( val, bck ) local vtype = type( val ) if vtype == "table" then return serialize_table( val, bck ) elseif vtype == "string" then return string.format( "%q", val ) elseif vtype == "function" or vtype == "userdata" then return string.format( "nil ", tostring( val ) ) else return tostring( val ) end end function util.serialize( ... ) local result = {} for i = 1, select( "#", ... ) do result[i] = serialize_any( select( i, ... ), {} ) end return table.concat( result, "," ) end end function util.unserialize( dt ) local fn = loadstring( "return " .. dt ) if fn then setfenv( fn, {} ) return fn() end end do local function serialize_any( val, bck ) local vtype = type( val ) if vtype == "table" then if bck[val] then return "{}" end bck[val] = true local len = 0 for k, v in pairs( val ) do len = len + 1 end local rt = {} if len == #val then for i = 1, #val do rt[i] = serialize_any( val[i], bck ) end return string.format( "[%s]", table.concat( rt, "," ) ) else for k, v in pairs( val ) do if type( k ) == "string" or type( k ) == "number" then rt[#rt + 1] = string.format( "%s: %s", serialize_any( k, bck ), serialize_any( v, bck ) ) end end return string.format( "{%s}", table.concat( rt, "," ) ) end elseif vtype == "string" then return string.format( "%q", val ) elseif vtype == "function" or vtype == "userdata" or vtype == "nil" then return "null" else return tostring( val ) end end function util.serializeJSON( val ) return serialize_any( val, {} ) end end function util.shared_buffer( name, max ) max = max or 64 return { _pos_name = name .. ".position", _list_name = name .. ".list.", push = function( self, text ) local cpos = GetInt( self._pos_name ) SetString( self._list_name .. (cpos % max), text ) SetInt( self._pos_name, cpos + 1 ) end, len = function( self ) return math.min( GetInt( self._pos_name ), max ) end, pos = function( self ) return GetInt( self._pos_name ) end, get = function( self, index ) local pos = GetInt( self._pos_name ) local len = math.min( pos, max ) if index >= len then return end return GetString( self._list_name .. (pos + index - len) % max ) end, iterator = function( self ) local pos = GetInt( self._pos_name ) local len = math.min( pos, max ) return function( _, i ) i = (i or 0) + 1 if i >= len then return end return i, GetString( self._list_name .. (pos + i - len) % max ) end end, get_g = function( self, index ) return GetString( self._list_name .. (index % max) ) end, clear = function( self ) SetInt( self._pos_name, 0 ) ClearKey( self._list_name:sub( 1, -2 ) ) end, } end if coreloaded then function util.shared_channel( name, max, local_realm ) max = max or 64 local channel = { _buffer = util.shared_buffer( name, max ), _offset = 0, _hooks = {}, _ready_count = 0, _ready = {}, broadcast = function( self, ... ) return self:send( "", ... ) end, send = function( self, realm, ... ) self._buffer:push( string.format( ",%s,;%s", (type( realm ) == "table" and table.concat( realm, "," ) or tostring( realm )), util.serialize( ... ) ) ) end, listen = function( self, callback ) if self._ready[callback] ~= nil then return end self._hooks[#self._hooks + 1] = callback self:ready( callback ) return callback end, unlisten = function( self, callback ) self:unready( callback ) self._ready[callback] = nil for i = 1, #self._hooks do if self._hooks[i] == callback then table.remove( self._hooks, i ) return true end end end, ready = function( self, callback ) if not self._ready[callback] then self._ready_count = self._ready_count + 1 self._ready[callback] = true end end, unready = function( self, callback ) if self._ready[callback] then self._ready_count = self._ready_count - 1 self._ready[callback] = false end end, } local_realm = "," .. (local_realm or "unknown") .. "," local function receive( ... ) for i = 1, #channel._hooks do local f = channel._hooks[i] if channel._ready[f] then f( channel, ... ) end end end hook.add( "base.tick", name, function( dt ) if channel._ready_count > 0 then local last_pos = channel._buffer:pos() if last_pos > channel._offset then for i = math.max( channel._offset, last_pos - max ), last_pos - 1 do local message = channel._buffer:get_g( i ) local start = message:find( ";", 1, true ) local realms = message:sub( 1, start - 1 ) if realms == ",," or realms:find( local_realm, 1, true ) then receive( util.unserialize( message:sub( start + 1 ) ) ) if channel._ready_count <= 0 then channel._offset = i + 1 return end end end channel._offset = last_pos end end end ) return channel end function util.async_channel( channel ) local listener = { _channel = channel, _waiter = nil, read = function( self ) self._waiter = coroutine.running() if not self._waiter then error( "async_channel:read() can only be used in a coroutine" ) end self._channel:ready( self._handler ) return coroutine.yield() end, close = function( self ) if self._handler then self._channel:unlisten( self._handler ) end end, } listener._handler = listener._channel:listen( function( _, ... ) if listener._waiter then local co = listener._waiter listener._waiter = nil listener._channel:unready( listener._handler ) return coroutine.resume( co, ... ) end end ) listener._channel:unready( listener._handler ) return listener end end do local gets, sets = {}, {} function util.register_unserializer( type, callback ) gets[type] = function( key ) return callback( GetString( key ) ) end end if coreloaded then hook.add( "api.newmeta", "api.createunserializer", function( name, meta ) gets[name] = function( key ) return setmetatable( {}, meta ):__unserialize( GetString( key ) ) end sets[name] = function( key, value ) return SetString( key, meta.__serialize( value ) ) end end ) end function util.shared_table( name, base ) return setmetatable( base or {}, { __index = function( self, k ) local key = tostring( k ) local newtypekey = string.format( "%s.%s._type", name, key ) local newformat = HasKey( newtypekey ) local vtype = GetString( newformat and newtypekey or string.format( "%s.%s.type", name, key ) ) if vtype == "" then return end return gets[vtype]( string.format( newformat and "%s.%s" or "%s.%s.val", name, key ) ) end, __newindex = function( self, k, v ) local vtype = type( v ) local handler = sets[vtype] if not handler then return end local key = tostring( k ) if vtype == "table" then local meta = getmetatable( v ) if meta and meta.__serialize and meta.__type then vtype = meta.__type v = meta.__serialize( v ) handler = sets.string end end SetString( string.format( "%s.%s._type", name, key ), vtype ) handler( string.format( "%s.%s", name, key ), v ) end, } ) end function util.structured_table( name, base ) local function generate( base ) local root = {} local keys = {} for k, v in pairs( base ) do local key = name .. "." .. tostring( k ) if type( v ) == "table" then if #v == 0 then root[k] = util.structured_table( key, v ) else keys[k] = { type = v[1], key = key, default = v[2] } end elseif type( v ) == "string" then keys[k] = { type = v, key = key } else root[k] = v end end return setmetatable( root, { __index = function( self, k ) local entry = keys[k] if entry and gets[entry.type] then if HasKey( entry.key ) then return gets[entry.type]( entry.key ) else return entry.default end end end, __newindex = function( self, k, v ) local entry = keys[k] if entry and sets[entry.type] then if v == nil then ClearKey( entry.key ) else sets[entry.type]( entry.key, v ) end end end, } ) end if type( base ) == "table" then return generate( base ) end return generate end gets.number = GetFloat gets.integer = GetInt gets.boolean = GetBool gets.string = GetString gets.table = util.shared_table sets["nil"] = ClearKey sets.number = SetFloat sets.integer = SetInt sets.boolean = SetBool sets.string = SetString sets.table = function( key, val ) local tab = util.shared_table( key ) for k, v in pairs( val ) do tab[k] = v end end end end)();
(function() local console_buffer = util.shared_buffer( "game.console", 128 ) local function maketext( ... ) local text = "" local len = select( "#", ... ) for i = 1, len do local s = tostring( select( i, ... ) ) if i < len then s = s .. string.rep( " ", 8 - #s % 8 ) end text = text .. s end return text end _OLDPRINT = _OLDPRINT or print function printcolor( r, g, b, ... ) local text = maketext( ... ) console_buffer:push( string.format( "%f;%f;%f;%s", r, g, b, text ) ) if PRINTTOSCREEN then DebugPrint( text ) end return _OLDPRINT( ... ) end function print( ... ) printcolor( 1, 1, 1, ... ) end function printinfo( ... ) printcolor( 0, .6, 1, ... ) end function warning( msg, level ) printcolor( 1, .7, 0, "[WARNING] " .. tostring( msg ) .. "\n " .. table.concat( util.stacktrace( level or 1 ), "\n " ) ) end printwarning = warning function printerror( ... ) printcolor( 1, .2, 0, ... ) end function clearconsole() console_buffer:clear() end function softassert( b, ... ) if not b then printerror( ... ) end return b, ... end function assert( b, msg, ... ) if not b then local m = msg or "Assertion failed" warning( m ) return error( m, ... ) end return b, msg, ... end end)();
(function() GLOBAL_CHANNEL = util.shared_channel( "game.umf_global_channel", 128 ) end)();
(function() local coreloaded = UMF_SOFTREQUIRE "src/core/_index.lua" local registered_meta = {} local reverse_meta = {} function global_metatable( name, parent, usecomputed ) local meta = registered_meta[name] if meta then if not parent and not usecomputed then return meta end else meta = {} meta.__index = meta meta.__type = name registered_meta[name] = meta reverse_meta[meta] = name if coreloaded then hook.saferun( "api.newmeta", name, meta ) end end local newindex = rawset if usecomputed then local computed = {} meta._C = computed meta.__index = function( self, k ) local c = computed[k] if c then return c( self ) end return meta[k] end meta.__newindex = function( self, k, v ) local c = computed[k] if c then return c( self, true, v ) end return newindex( self, k, v ) end end if parent then local parent_meta = global_metatable( parent ) if parent_meta.__newindex then newindex = parent_meta.__newindex if not meta.__newindex then meta.__newindex = newindex end end setmetatable( meta, { __index = parent_meta.__index } ) end return meta end function find_global_metatable( name ) if not name then return end if type( name ) == "table" then return reverse_meta[name] end return registered_meta[name] end local function findmeta( src, found ) if found[src] then return end found[src] = true local res for k, v in pairs( src ) do if type( v ) == "table" then local dt local m = getmetatable( v ) if m then local name = reverse_meta[m] if name then dt = {} dt[1] = name end end local sub = findmeta( v, found ) if sub then dt = dt or {} dt[2] = sub end if dt then res = res or {} res[k] = dt end end end return res end _ORIG_setmetatable = _ORIG_setmetatable or setmetatable function setmetatable(t, meta) if not meta or reverse_meta[meta] then rawset(t, "__UMF_GLOBAL_METATYPE", meta and reverse_meta[meta]) end return _ORIG_setmetatable(t, meta) end function instantiate_global_metatable( name, base ) local t = base or {} t.__UMF_GLOBAL_METATYPE = name local meta = find_global_metatable( name ) if meta then setmetatable( t, meta ) if meta._C then for k, f in pairs( meta._C ) do local v = rawget( t, k ) if v ~= nil then rawset( t, k, nil ) f( t, v ) end end end end return t end local function restoremeta( t, explored ) if explored[t] then return end explored[t] = true for _, v in pairs( t ) do if type( v ) == "table" then local meta_type = rawget( v, "__UMF_GLOBAL_METATYPE" ) if meta_type then setmetatable( v, global_metatable( meta_type ) ) end restoremeta( v, explored ) end end end function restore_global_metatables() restoremeta( _G, {} ) end if coreloaded then hook.add( "base.command.quickload", "api.metatables.restore", function() restore_global_metatables() end ) end end)();
(function() local vector_meta = global_metatable( "vector" ) local quat_meta quat_meta = global_metatable( "quaternion" ) function IsQuaternion( q ) return type( q ) == "table" and type( q[1] ) == "number" and type( q[2] ) == "number" and type( q[3] ) == "number" and type( q[4] ) == "number" end function MakeQuaternion( q ) return instantiate_global_metatable( "quaternion", q ) end function Quaternion( i, j, k, r ) if IsQuaternion( i ) then i, j, k, r = i[1], i[2], i[3], i[4] end return MakeQuaternion { i or 0, j or 0, k or 0, r or 1 } end function quat_meta:__unserialize( data ) local i, j, k, r = data:match( "([-0-9.]*);([-0-9.]*);([-0-9.]*);([-0-9.]*)" ) self[1] = tonumber( i ) self[2] = tonumber( j ) self[3] = tonumber( k ) self[4] = tonumber( r ) return self end function quat_meta:__serialize() return string.format("%f;%f;%f;%f", self[1], self[2], self[3], self[4]) end QUAT_ZERO = Quaternion() function quat_meta:Clone() return MakeQuaternion { self[1], self[2], self[3], self[4] } end local QuatStr = QuatStr function quat_meta:__tostring() return QuatStr( self ) end function quat_meta:__unm() return MakeQuaternion { -self[1], -self[2], -self[3], -self[4] } end function quat_meta:Conjugate() return MakeQuaternion { -self[1], -self[2], -self[3], self[4] } end function quat_meta:Invert() local l = quat_meta.LengthSquare( self ) return MakeQuaternion { -self[1] / l, -self[2] / l, -self[3] / l, self[4] / l } end function quat_meta:Add( o ) if IsQuaternion( o ) then self[1] = self[1] + o[1] self[2] = self[2] + o[2] self[3] = self[3] + o[3] self[4] = self[4] + o[4] else self[1] = self[1] + o self[2] = self[2] + o self[3] = self[3] + o self[4] = self[4] + o end return self end function quat_meta.__add( a, b ) if not IsQuaternion( a ) then a, b = b, a end return quat_meta.Add( quat_meta.Clone( a ), b ) end function quat_meta:Sub( o ) if IsQuaternion( o ) then self[1] = self[1] - o[1] self[2] = self[2] - o[2] self[3] = self[3] - o[3] self[4] = self[4] - o[4] else self[1] = self[1] - o self[2] = self[2] - o self[3] = self[3] - o self[4] = self[4] - o end return self end function quat_meta.__sub( a, b ) if not IsQuaternion( a ) then a, b = b, a end return quat_meta.Sub( quat_meta.Clone( a ), b ) end function quat_meta:Mul( o ) local i1, j1, k1, r1 = self[1], self[2], self[3], self[4] local i2, j2, k2, r2 = o[1], o[2], o[3], o[4] self[1] = j1 * k2 - k1 * j2 + r1 * i2 + i1 * r2 self[2] = k1 * i2 - i1 * k2 + r1 * j2 + j1 * r2 self[3] = i1 * j2 - j1 * i2 + r1 * k2 + k1 * r2 self[4] = r1 * r2 - i1 * i2 - j1 * j2 - k1 * k2 return self end function quat_meta.__mul( a, b ) if not IsQuaternion( a ) then a, b = b, a end if type( b ) == "number" then return Quaternion( a[1] * b, a[2] * b, a[3] * b, a[4] * b ) end if IsVector( b ) then return vector_meta.__mul( b, a ) end if IsTransformation( b ) then return Transformation( vector_meta.Mul( vector_meta.Clone( b.pos ), a ), QuatRotateQuat( b.rot, a ) ) end return MakeQuaternion( QuatRotateQuat( a, b ) ) end function quat_meta:Div( o ) if IsQuaternion( o ) then quat_meta.Mul( self, { -o[1], -o[2], -o[3], o[4] } ) else self[1] = self[1] / o self[2] = self[2] / o self[3] = self[3] / o self[4] = self[4] / o end return self end function quat_meta.__div( a, b ) return quat_meta.Div( quat_meta.Clone( a ), b ) end function quat_meta.__eq( a, b ) return a[1] == b[1] and a[2] == b[2] and a[3] == b[3] and a[4] == b[4] end function quat_meta:LengthSquare() return self[1] ^ 2 + self[2] ^ 2 + self[3] ^ 2 + self[4] ^ 2 end function quat_meta:Length() return math.sqrt( quat_meta.LengthSquare( self ) ) end local QuatSlerp = QuatSlerp function quat_meta:Slerp( o, n ) return MakeQuaternion( QuatSlerp( self, o, n ) ) end function quat_meta:Left() local x, y, z, s = self[1], self[2], self[3], self[4] return Vector( 1 - (y ^ 2 + z ^ 2) * 2, (z * s + x * y) * 2, (x * z - y * s) * 2 ) end function quat_meta:Up() local x, y, z, s = self[1], self[2], self[3], self[4] return Vector( (y * x - z * s) * 2, 1 - (z ^ 2 + x ^ 2) * 2, (x * s + y * z) * 2 ) end function quat_meta:Forward() local x, y, z, s = self[1], self[2], self[3], self[4] return Vector( (y * s + z * x) * 2, (z * y - x * s) * 2, 1 - (x ^ 2 + y ^ 2) * 2 ) end function quat_meta:ToEuler() if GetQuatEuler then return GetQuatEuler( self ) end local x, y, z, w = self[1], self[2], self[3], self[4] local bank, heading, attitude local s = 2 * x * y + 2 * z * w if s >= 1 then heading = 2 * math.atan2( x, w ) bank = 0 attitude = math.pi / 2 elseif s <= -1 then heading = -2 * math.atan2( x, w ) bank = 0 attitude = math.pi / -2 else bank = math.atan2( 2 * x * w - 2 * y * z, 1 - 2 * x ^ 2 - 2 * z ^ 2 ) heading = math.atan2( 2 * y * w - 2 * x * z, 1 - 2 * y ^ 2 - 2 * z ^ 2 ) attitude = math.asin( s ) end return math.deg( bank ), math.deg( heading ), math.deg( attitude ) end function quat_meta:ToAxisAngle() local iw = math.sqrt( 1 - self[4] ^ 2 ) if iw <= 0 then return Vector( 0, 0, 0 ), 0 end return Vector( self[1] / iw, self[2] / iw, self[3] / iw ), math.deg( math.asin( iw ) * 2 ) end function quat_meta:Approach( dest, rate ) local dot = self[1] * dest[1] + self[2] * dest[2] + self[3] * dest[3] + self[4] * dest[4] if dot >= 1 then return self end local corr_rate = rate / math.acos( 2 * dot ^ 2 - 1 ) if corr_rate >= 1 then return MakeQuaternion( dest ) end return MakeQuaternion( QuatSlerp( self, dest, corr_rate ) ) end end)();
(function() local vector_meta = global_metatable( "vector" ) local quat_meta = global_metatable( "quaternion" ) local transform_meta transform_meta = global_metatable( "transformation" ) function IsTransformation( t ) return type( t ) == "table" and t.pos and t.rot end function MakeTransformation( t ) instantiate_global_metatable( "vector", t.pos ) instantiate_global_metatable( "quaternion", t.rot ) return instantiate_global_metatable( "transformation", t ) end function Transformation( pos, rot ) return MakeTransformation { pos = pos or { 0, 0, 0 }, rot = rot or { 0, 0, 0, 1 } } end function transform_meta:__unserialize( data ) local x, y, z, i, j, k, r = data:match( "([-0-9.]*);([-0-9.]*);([-0-9.]*);([-0-9.]*);([-0-9.]*);([-0-9.]*);([-0-9.]*)" ) self.pos = Vector( tonumber( x ), tonumber( y ), tonumber( z ) ) self.rot = Quaternion( tonumber( i ), tonumber( j ), tonumber( k ), tonumber( r ) ) return self end function transform_meta:__serialize() return string.format("%f;%f;%f;%f;%f;%f;%f", self.pos[1], self.pos[2], self.pos[3], self.rot[1], self.rot[2], self.rot[3], self.rot[4]) end function transform_meta:Clone() return MakeTransformation { pos = vector_meta.Clone( self.pos ), rot = quat_meta.Clone( self.rot ) } end local TransformStr = TransformStr function transform_meta:__tostring() return TransformStr( self ) end local TransformToLocalPoint = TransformToLocalPoint local TransformToLocalTransform = TransformToLocalTransform local TransformToLocalVec = TransformToLocalVec local TransformToParentPoint = TransformToParentPoint local TransformToParentTransform = TransformToParentTransform local TransformToParentVec = TransformToParentVec function transform_meta.__add( a, b ) if not IsTransformation( b ) then if IsVector( b ) then b = Transformation( b, QUAT_ZERO ) elseif IsQuaternion( b ) then b = Transformation( VEC_ZERO, b ) end end return MakeTransformation( TransformToParentTransform( a, b ) ) end function transform_meta:ToLocal( o ) if IsTransformation( o ) then return MakeTransformation( TransformToLocalTransform( self, o ) ) elseif IsQuaternion( o ) then return MakeQuaternion( TransformToLocalTransform( self, Transform( {}, o ) ).rot ) else return MakeVector( TransformToLocalPoint( self, o ) ) end end function transform_meta:ToLocalDir( o ) return MakeVector( TransformToLocalVec( self, o ) ) end function transform_meta:ToGlobal( o ) if IsTransformation( o ) then return MakeTransformation( TransformToParentTransform( self, o ) ) elseif IsQuaternion( o ) then return MakeQuaternion( TransformToParentTransform( self, Transform( {}, o ) ).rot ) else return MakeVector( TransformToParentPoint( self, o ) ) end end function transform_meta:ToGlobalDir( o ) return MakeVector( TransformToParentVec( self, o ) ) end function transform_meta:Raycast( dist, mul, radius, rejectTransparent ) local dir = TransformToParentVec( self, VEC_FORWARD ) if mul then vector_meta.Mul( dir, mul ) end local hit, dist2, normal, shape = QueryRaycast( self.pos, dir, dist, radius, rejectTransparent ) return { hit = hit, dist = dist2, normal = hit and MakeVector( normal ), shape = hit and Shape and Shape( shape ) or shape, hitpos = vector_meta.__add( self.pos, vector_meta.Mul( dir, hit and dist2 or dist ) ), } end end)();
(function() local quat_meta = global_metatable( "quaternion" ) local vector_meta vector_meta = global_metatable( "vector" ) function IsVector( v ) return type( v ) == "table" and type( v[1] ) == "number" and type( v[2] ) == "number" and type( v[3] ) == "number" and not v[4] end function MakeVector( v ) return instantiate_global_metatable( "vector", v ) end function Vector( x, y, z ) if IsVector( x ) then x, y, z = x[1], x[2], x[3] end return MakeVector { x or 0, y or 0, z or 0 } end function vector_meta:__unserialize( data ) local x, y, z = data:match( "([-0-9.]*);([-0-9.]*);([-0-9.]*)" ) self[1] = tonumber( x ) self[2] = tonumber( y ) self[3] = tonumber( z ) return self end function vector_meta:__serialize() return string.format("%f;%f;%f", self[1], self[2], self[3]) end VEC_ZERO = Vector() VEC_FORWARD = Vector( 0, 0, 1 ) VEC_UP = Vector( 0, 1, 0 ) VEC_LEFT = Vector( 1, 0, 0 ) function vector_meta:Clone() return MakeVector { self[1], self[2], self[3] } end local VecStr = VecStr function vector_meta:__tostring() return VecStr( self ) end function vector_meta:__unm() return MakeVector { -self[1], -self[2], -self[3] } end function vector_meta:Add( o ) if IsVector( o ) then self[1] = self[1] + o[1] self[2] = self[2] + o[2] self[3] = self[3] + o[3] else self[1] = self[1] + o self[2] = self[2] + o self[3] = self[3] + o end return self end function vector_meta.__add( a, b ) if not IsVector( a ) then a, b = b, a end if IsTransformation( b ) then return Transformation( vector_meta.Add( vector_meta.Clone( a ), b.pos ), quat_meta.Clone( b.rot ) ) end return vector_meta.Add( vector_meta.Clone( a ), b ) end function vector_meta:Sub( o ) if IsVector( o ) then self[1] = self[1] - o[1] self[2] = self[2] - o[2] self[3] = self[3] - o[3] else self[1] = self[1] - o self[2] = self[2] - o self[3] = self[3] - o end return self end function vector_meta.__sub( a, b ) if not IsVector( a ) then a, b = b, a end return vector_meta.Sub( vector_meta.Clone( a ), b ) end function vector_meta:Mul( o ) if IsVector( o ) then self[1] = self[1] * o[1] self[2] = self[2] * o[2] self[3] = self[3] * o[3] elseif IsQuaternion( o ) then local x1, y1, z1 = self[1], self[2], self[3] local x2, y2, z2, s = o[1], o[2], o[3], o[4] local x3 = y2 * z1 - z2 * y1 local y3 = z2 * x1 - x2 * z1 local z3 = x2 * y1 - y2 * x1 self[1] = x1 + (x3 * s + y2 * z3 - z2 * y3) * 2 self[2] = y1 + (y3 * s + z2 * x3 - x2 * z3) * 2 self[3] = z1 + (z3 * s + x2 * y3 - y2 * x3) * 2 else self[1] = self[1] * o self[2] = self[2] * o self[3] = self[3] * o end return self end function vector_meta.__mul( a, b ) if not IsVector( a ) then a, b = b, a end return vector_meta.Mul( vector_meta.Clone( a ), b ) end function vector_meta:Div( o ) self[1] = self[1] / o self[2] = self[2] / o self[3] = self[3] / o return self end function vector_meta.__div( a, b ) return vector_meta.Div( vector_meta.Clone( a ), b ) end function vector_meta:Mod( o ) self[1] = self[1] % o self[2] = self[2] % o self[3] = self[3] % o return self end function vector_meta.__mod( a, b ) return vector_meta.Mod( vector_meta.Clone( a ), b ) end function vector_meta:Pow( o ) self[1] = self[1] ^ o self[2] = self[2] ^ o self[3] = self[3] ^ o return self end function vector_meta.__pow( a, b ) return vector_meta.Pow( vector_meta.Clone( a ), b ) end function vector_meta.__eq( a, b ) return a[1] == b[1] and a[2] == b[2] and a[3] == b[3] end function vector_meta.__lt( a, b ) return a[1] < b[1] or (a[1] == b[1] and (a[2] < b[2] or (a[2] == b[2] and (a[3] < b[3])))) end function vector_meta.__le( a, b ) return a[1] < b[1] or (a[1] == b[1] and (a[2] < b[2] or (a[2] == b[2] and (a[3] <= b[3])))) end local VecDot = VecDot function vector_meta:Dot( b ) return VecDot( self, b ) end local VecCross = VecCross function vector_meta:Cross( b ) return MakeVector( VecCross( self, b ) ) end local VecLength = VecLength function vector_meta:Length() return VecLength( self ) end function vector_meta:Volume() return math.abs( self[1] * self[2] * self[3] ) end local VecLerp = VecLerp function vector_meta:Lerp( o, n ) return MakeVector( VecLerp( self, o, n ) ) end local VecNormalize = VecNormalize function vector_meta:Normalized() return MakeVector( VecNormalize( self ) ) end function vector_meta:Normalize() return vector_meta.Div( self, vector_meta.Length( self ) ) end function vector_meta:DistSquare( o ) return (self[1] - o[1]) ^ 2 + (self[2] - o[2]) ^ 2 + (self[3] - o[3]) ^ 2 end function vector_meta:Distance( o ) return math.sqrt( vector_meta.DistSquare( self, o ) ) end function vector_meta:LookAt( o ) return MakeQuaternion( QuatLookAt( self, o ) ) end function vector_meta:ToQuaternion( target_up ) local forward = VecScale( self, -1 / VecLength( self ) ) local right = VecNormalize( VecCross( target_up or Vec( 0, 1, 0 ), forward ) ) local up = VecCross( forward, right ) local m00, m01, m02 = right[1], right[2], right[3] local m10, m11, m12 = up[1], up[2], up[3] local m20, m21, m22 = forward[1], forward[2], forward[3] if m22 < 0 then if m00 > m11 then local t = 1 + m00 - m11 - m22 local t2 = 0.5 / math.sqrt( t ) return Quaternion( t2 * t, t2 * (m01 + m10), t2 * (m20 + m02), t2 * (m12 - m21) ) else local t = 1 - m00 + m11 - m22 local t2 = 0.5 / math.sqrt( t ) return Quaternion( t2 * (m01 + m10), t2 * t, t2 * (m12 + m21), t2 * (m20 - m02) ) end else if m00 < -m11 then local t = 1 - m00 - m11 + m22 local t2 = 0.5 / math.sqrt( t ) return Quaternion( t2 * (m20 + m02), t2 * (m12 + m21), t2 * t, t2 * (m01 - m10) ) else local t = 1 + m00 + m11 + m22 local t2 = 0.5 / math.sqrt( t ) return Quaternion( t2 * (m12 - m21), t2 * (m20 - m02), t2 * (m01 - m10), t2 * t ) end end end function vector_meta:Approach( dest, rate ) local dist = vector_meta.Distance( self, dest ) if dist < rate then return MakeVector( dest ) end return vector_meta.Lerp( self, dest, rate / dist ) end function vector_meta:Min( ... ) local n = vector_meta.Clone( self ) for i = 1, select( "#", ... ) do local o = select( i, ... ) if type( o ) == "number" then n[1] = math.min( n[1], o ) n[2] = math.min( n[2], o ) n[3] = math.min( n[3], o ) else n[1] = math.min( n[1], o[1] ) n[2] = math.min( n[2], o[2] ) n[3] = math.min( n[3], o[3] ) end end return n end function vector_meta:Max( ... ) local n = vector_meta.Clone( self ) for i = 1, select( "#", ... ) do local o = select( i, ... ) if type( o ) == "number" then n[1] = math.max( n[1], o ) n[2] = math.max( n[2], o ) n[3] = math.max( n[3], o ) else n[1] = math.max( n[1], o[1] ) n[2] = math.max( n[2], o[2] ) n[3] = math.max( n[3], o[3] ) end end return n end function vector_meta:Clamp( min, max ) if type( min ) == "number" then return Vector( math.max( math.min( self[1], max ), min ), math.max( math.min( self[2], max ), min ), math.max( math.min( self[3], max ), min ) ) else return Vector( math.max( math.min( self[1], max[1] ), min[1] ), math.max( math.min( self[2], max[2] ), min[2] ), math.max( math.min( self[3], max[3] ), min[3] ) ) end end end)();
(function() local entity_meta = global_metatable( "entity" ) local properties = {} local DATA_KEY = {} local TAGS_KEY = {} function entity_meta:__index( k ) if properties[k] then return properties[k]( self, false ) end if entity_meta[k] then return entity_meta[k] end local entdata = rawget( self, DATA_KEY ) if not entdata then entdata = util.shared_table( "game.umf.entdata." .. rawget( self, "handle" ) ) rawset( self, DATA_KEY, entdata ) end return entdata[k] end function entity_meta:__newindex( k, v ) if properties[k] then return properties[k]( self, true, v ) end local entdata = rawget( self, DATA_KEY ) if not entdata then entdata = util.shared_table( "game.umf.entdata." .. rawget( self, "handle" ) ) rawset( self, DATA_KEY, entdata ) end entdata[k] = v end function properties:description( set, val ) if set then SetDescription( self.handle, val ) else return GetDescription( self.handle ) end end local tags_meta = { __index = function( self, k ) if HasTag( self.__handle, k ) then return GetTagValue( self.__handle, k ) end end, __newindex = function( self, k, v ) if v == nil then RemoveTag( self.__handle, k ) else SetTag( self.__handle, k, tostring( v ) ) end end } function properties:tags( set ) if set then error( "cannot set tags key" ) end local enttags = rawget( self, TAGS_KEY ) if not enttags then enttags = setmetatable( { __handle = self.handle }, tags_meta ) rawset( self, TAGS_KEY, enttags ) end return enttags end function GetEntityHandle( e ) if IsEntity( e ) then return e.handle end return e end function IsValid( e ) if type( e ) == "table" and e.IsValid then return e:IsValid() end return false end function IsEntity( e ) return type( e ) == "table" and type( e.handle ) == "number" end function Entity( handle ) if type( handle ) == "number" and handle ~= 0 then local type = GetEntityType and GetEntityType( handle ) return instantiate_global_metatable( type or "entity", { handle = handle, type = type or "unknown" } ) end return handle end function entity_meta:__unserialize( data ) rawset( self, "handle", tonumber( data ) ) return self end function entity_meta:__serialize() return tostring( self.handle ) end function entity_meta:__tostring() return string.format( "Entity[%d]", self.handle ) end function entity_meta:GetType() return rawget( self, "type" ) or "unknown" end local IsHandleValid = IsHandleValid function entity_meta:IsValid() return IsHandleValid( self.handle ) end local SetTag = SetTag function entity_meta:SetTag( tag, value ) assert( self:IsValid() ) return SetTag( self.handle, tag, value ) end local SetDescription = SetDescription function entity_meta:SetDescription( description ) assert( self:IsValid() ) return SetDescription( self.handle, description ) end local RemoveTag = RemoveTag function entity_meta:RemoveTag( tag ) assert( self:IsValid() ) return RemoveTag( self.handle, tag ) end local HasTag = HasTag function entity_meta:HasTag( tag ) assert( self:IsValid() ) return HasTag( self.handle, tag ) end local GetTagValue = GetTagValue function entity_meta:GetTagValue( tag ) assert( self:IsValid() ) return GetTagValue( self.handle, tag ) end local GetDescription = GetDescription function entity_meta:GetDescription() assert( self:IsValid() ) return GetDescription( self.handle ) end local Delete = Delete function entity_meta:Delete() return Delete( self.handle ) end end)();
(function() local body_meta = global_metatable( "body", "entity", true ) function IsBody( e ) return IsEntity( e ) and e.type == "body" end function Body( handle ) if handle ~= 0 then return instantiate_global_metatable( "body", { handle = handle, type = "body" } ) end end function FindBodyByTag( tag, global ) return Body( FindBody( tag, global ) ) end function FindBodiesByTag( tag, global ) local t = FindBodies( tag, global ) for i = 1, #t do t[i] = Body( t[i] ) end return t end function body_meta:__tostring() return string.format( "Body[%d]", self.handle ) end function body_meta:ApplyImpulse( pos, vel ) assert( self:IsValid() ) return ApplyBodyImpulse( self.handle, pos, vel ) end function body_meta:ApplyLocalImpulse( pos, vel ) local transform = self:GetTransform() return self:ApplyImpulse( transform:ToGlobal( pos ), transform:ToGlobalDir( vel ) ) end function body_meta:DrawOutline( r, ... ) assert( self:IsValid() ) return DrawBodyOutline( self.handle, r, ... ) end function body_meta:DrawHighlight( amount ) assert( self:IsValid() ) return DrawBodyHighlight( self.handle, amount ) end function body_meta:SetTransform( tr ) assert( self:IsValid() ) return SetBodyTransform( self.handle, tr ) end function body_meta:SetActive( bool ) assert( self:IsValid() ) return SetBodyActive( self.handle, bool ) end function body_meta:SetDynamic( bool ) assert( self:IsValid() ) return SetBodyDynamic( self.handle, bool ) end function body_meta:SetVelocity( vel ) assert( self:IsValid() ) return SetBodyVelocity( self.handle, vel ) end function body_meta:SetAngularVelocity( avel ) assert( self:IsValid() ) return SetBodyAngularVelocity( self.handle, avel ) end function body_meta:GetTransform() assert( self:IsValid() ) return MakeTransformation( GetBodyTransform( self.handle ) ) end function body_meta:GetMass() assert( self:IsValid() ) return GetBodyMass( self.handle ) end function body_meta:GetVelocity() assert( self:IsValid() ) return MakeVector( GetBodyVelocity( self.handle ) ) end function body_meta:GetVelocityAtPos( pos ) assert( self:IsValid() ) return MakeVector( GetBodyVelocityAtPos( self.handle, pos ) ) end function body_meta:GetAngularVelocity() assert( self:IsValid() ) return MakeVector( GetBodyAngularVelocity( self.handle ) ) end function body_meta:GetShapes() assert( self:IsValid() ) local shapes = GetBodyShapes( self.handle ) for i = 1, #shapes do shapes[i] = Shape( shapes[i] ) end return shapes end function body_meta:GetVehicle() assert( self:IsValid() ) return Vehicle( GetBodyVehicle( self.handle ) ) end function body_meta:GetWorldBounds() assert( self:IsValid() ) local min, max = GetBodyBounds( self.handle ) return MakeVector( min ), MakeVector( max ) end function body_meta:GetLocalCenterOfMass() assert( self:IsValid() ) return MakeVector( GetBodyCenterOfMass( self.handle ) ) end function body_meta:GetWorldCenterOfMass() return self:GetTransform():ToGlobal( self:GetLocalCenterOfMass() ) end function body_meta:GetClosestPoint( origin ) local hit, point, normal, shape = GetBodyClosestPoint( self.handle, origin ) if not hit then return false end return hit, MakeVector( point ), MakeVector( normal ), Shape( shape ) end function body_meta:GetJointedBodies() local list = GetJointedBodies( self.handle ) for i = 1, #list do list[i] = Body( list[i] ) end return list end function body_meta:IsActive() assert( self:IsValid() ) return IsBodyActive( self.handle ) end function body_meta:IsDynamic() assert( self:IsValid() ) return IsBodyDynamic( self.handle ) end function body_meta:IsVisible( maxdist ) assert( self:IsValid() ) return IsBodyVisible( self.handle, maxdist ) end function body_meta:IsBroken() return not self:IsValid() or IsBodyBroken( self.handle ) end function body_meta:IsJointedToStatic() assert( self:IsValid() ) return IsBodyJointedToStatic( self.handle ) end function body_meta._C:transform( setter, val ) if setter then self:SetTransform( val ) else return self:GetTransform() end end function body_meta._C:velocity( setter, val ) if setter then self:SetVelocity( val ) else return self:GetVelocity() end end function body_meta._C:angularVelocity( setter, val ) if setter then self:SetAngularVelocity( val ) else return self:GetAngularVelocity() end end function body_meta._C:active( setter, val ) if setter then self:SetActive( val ) else return self:IsActive() end end function body_meta._C:dynamic( setter, val ) if setter then self:SetDynamic( val ) else return self:IsDynamic() end end function body_meta._C:broken( setter ) assert(not setter, "cannot set broken") return self:IsBroken() end function body_meta._C:mass( setter ) assert(not setter, "cannot set mass") return self:GetMass() end function body_meta._C:shapes( setter ) assert(not setter, "cannot set shapes") return self:GetShapes() end function body_meta._C:vehicle( setter ) assert(not setter, "cannot set vehicle") return self:GetVehicle() end end)();
(function() local joint_meta = global_metatable( "joint", "entity", true ) function IsJoint( e ) return IsEntity( e ) and e.type == "joint" end function Joint( handle ) if handle ~= 0 then return instantiate_global_metatable( "joint", { handle = handle, type = "joint" } ) end end function FindJointByTag( tag, global ) return Joint( FindJoint( tag, global ) ) end function FindJointsByTag( tag, global ) local t = FindJoints( tag, global ) for i = 1, #t do t[i] = Joint( t[i] ) end return t end function joint_meta:__tostring() return string.format( "Joint[%d]", self.handle ) end function joint_meta:DetachFromShape( shape ) local shapeHandle = GetEntityHandle( shape ) DetachJointFromShape( self.handle, shapeHandle ) end function joint_meta:SetMotor( velocity, strength ) assert( self:IsValid() ) return SetJointMotor( self.handle, velocity, strength ) end function joint_meta:SetMotorTarget( target, maxVel, strength ) assert( self:IsValid() ) return SetJointMotorTarget( self.handle, target, maxVel, strength ) end function joint_meta:GetJointType() assert( self:IsValid() ) return GetJointType( self.handle ) end function joint_meta:GetOtherShape( shape ) assert( self:IsValid() ) local shapeHandle = GetEntityHandle( shape ) local otherShape = Shape( GetJointOtherShape( self.handle, shapeHandle ) ) return otherShape end function joint_meta:GetLimits() assert( self:IsValid() ) return GetJointLimits( self.handle ) end function joint_meta:GetMovement() assert( self:IsValid() ) return GetJointMovement( self.handle ) end function joint_meta:IsBroken() return not self:IsValid() or IsJointBroken( self.handle ) end function joint_meta._C:jointType( setter ) assert(not setter, "cannot set jointType") return self:GetType() end function joint_meta._C:broken( setter ) assert(not setter, "cannot set broken") return self:IsBroken() end end)();
(function() local light_meta = global_metatable( "light", "entity", true ) function IsLight( e ) return IsEntity( e ) and e.type == "light" end function Light( handle ) if handle ~= 0 then return instantiate_global_metatable( "light", { handle = handle, type = "light" } ) end end function FindLightByTag( tag, global ) return Light( FindLight( tag, global ) ) end function FindLightsByTag( tag, global ) local t = FindLights( tag, global ) for i = 1, #t do t[i] = Light( t[i] ) end return t end function light_meta:__tostring() return string.format( "Light[%d]", self.handle ) end function light_meta:SetEnabled( enabled ) assert( self:IsValid() ) return SetLightEnabled( self.handle, enabled ) end function light_meta:SetColor( r, g, b ) assert( self:IsValid() ) return SetLightColor( self.handle, r, g, b ) end function light_meta:SetIntensity( intensity ) assert( self:IsValid() ) return SetLightIntensity( self.handle, intensity ) end function light_meta:GetTransform() assert( self:IsValid() ) return MakeTransformation( GetLightTransform( self.handle ) ) end function light_meta:GetShape() assert( self:IsValid() ) local shape = Shape( GetLightShape( self.handle ) ) return shape end function light_meta:IsActive() assert( self:IsValid() ) return IsLightActive( self.handle ) end function light_meta:IsPointAffectedByLight( point ) assert( self:IsValid() ) return IsPointAffectedByLight( self.handle, point ) end function light_meta._C:enabled( setter, val ) if setter then self:SetEnabled( val ) else return self:IsActive() end end function light_meta._C:color( setter, val ) assert(setter, "cannot get color") return self:SetColor( val[1], val[2], val[3] ) end function light_meta._C:intensity( setter, val ) assert(setter, "cannot get intensity") return self:SetIntensity( val ) end function light_meta._C:transform( setter ) assert(not setter, "cannot set transform") return self:GetTransform() end function light_meta._C:shape( setter ) assert(not setter, "cannot set shape") return self:GetShape() end end)();
(function() local location_meta = global_metatable( "location", "entity", true ) function IsLocation( e ) return IsEntity( e ) and e.type == "location" end function Location( handle ) if handle ~= 0 then return instantiate_global_metatable( "location", { handle = handle, type = "location" } ) end end function FindLocationByTag( tag, global ) return Location( FindLocation( tag, global ) ) end function FindLocationsByTag( tag, global ) local t = FindLocations( tag, global ) for i = 1, #t do t[i] = Location( t[i] ) end return t end function location_meta:__tostring() return string.format( "Location[%d]", self.handle ) end function location_meta:GetTransform() assert( self:IsValid() ) return MakeTransformation( GetLocationTransform( self.handle ) ) end function location_meta._C:transform( setter ) assert(not setter, "cannot set transform") return self:GetTransform() end end)();
(function() local player_meta player_meta = global_metatable( "player" ) PLAYER = instantiate_global_metatable( "player" ) function player_meta:__unserialize( data ) return self end function player_meta:__serialize() return "" end function player_meta:__tostring() return string.format( "Player" ) end function player_meta:GetType() return "player" end function player_meta:Respawn() return RespawnPlayer() end function player_meta:ReleaseGrab() ReleasePlayerGrab() end function player_meta:SetTransform( transform, includePitch ) return SetPlayerTransform( transform, includePitch ) end function player_meta:SetCamera( transform ) return SetCameraTransform( transform ) end function player_meta:SetFov( degrees ) return SetCameraFov( degrees ) end function player_meta:SetDof( distance, amount ) return SetCameraDof( distance, amount ) end function player_meta:SetSpawnTransform( transform ) return SetPlayerSpawnTransform( transform ) end function player_meta:SetVehicle( handle ) local vehicleHandle = GetEntityHandle( handle ) return SetPlayerVehicle( vehicleHandle ) end function player_meta:SetVelocity( velocity ) return SetPlayerVelocity( velocity ) end function player_meta:SetScreen( handle ) local screenHandle = GetEntityHandle( handle ) return SetPlayerScreen( screenHandle ) end function player_meta:SetHealth( health ) return SetPlayerHealth( health ) end function player_meta:SetGroundVelocity(vel) SetPlayerGroundVelocity(vel) end function player_meta:GetTransform( includePitch ) return MakeTransformation( GetPlayerTransform( includePitch ) ) end function player_meta:GetPlayerCamera() return MakeTransformation( GetPlayerCameraTransform() ) end function player_meta:GetCamera() return MakeTransformation( GetCameraTransform() ) end function player_meta:GetVelocity() return MakeVector( GetPlayerVelocity() ) end function player_meta:GetVehicle() return Vehicle( GetPlayerVehicle() ) end function player_meta:GetGrabShape() return Shape( GetPlayerGrabShape() ) end function player_meta:GetGrabBody() return Body( GetPlayerGrabBody() ) end function player_meta:GetPickShape() return Shape( GetPlayerPickShape() ) end function player_meta:GetPickBody() return Body( GetPlayerPickBody() ) end function player_meta:GetInteractShape() return Shape( GetPlayerInteractShape() ) end function player_meta:GetInteractBody() return Body( GetPlayerInteractBody() ) end function player_meta:GetScreen() return Screen( GetPlayerScreen() ) end function player_meta:GetHealth() return GetPlayerHealth() end end)();
(function() local screen_meta screen_meta = global_metatable( "screen", "entity", true ) function IsScreen( e ) return IsEntity( e ) and e.type == "screen" end function Screen( handle ) if handle ~= 0 then return instantiate_global_metatable( "screen", { handle = handle, type = "screen" } ) end end function FindScreenByTag( tag, global ) return Screen( FindScreen( tag, global ) ) end function FindScreensByTag( tag, global ) local t = FindScreens( tag, global ) for i = 1, #t do t[i] = Screen( t[i] ) end return t end function screen_meta:__tostring() return string.format( "Screen[%d]", self.handle ) end function screen_meta:SetEnabled( enabled ) assert( self:IsValid() ) return SetScreenEnabled( self.handle, enabled ) end function screen_meta:GetShape() assert( self:IsValid() ) local shape = Shape( GetScreenShape( self.handle ) ) return shape end function screen_meta:IsEnabled() assert( self:IsValid() ) return IsScreenEnabled( self.handle ) end function screen_meta._C:enabled( setter, val ) if setter then self:SetEnabled( val ) else return self:IsEnabled() end end function screen_meta._C:shape( setter ) assert(not setter, "cannot set shape") return self:GetShape() end end)();
(function() local shape_meta shape_meta = global_metatable( "shape", "entity", true ) function IsShape( e ) return IsEntity( e ) and e.type == "shape" end function Shape( handle ) if handle ~= 0 then return instantiate_global_metatable( "shape", { handle = handle, type = "shape" } ) end end function FindShapeByTag( tag, global ) return Shape( FindShape( tag, global ) ) end function FindShapesByTag( tag, global ) local t = FindShapes( tag, global ) for i = 1, #t do t[i] = Shape( t[i] ) end return t end function shape_meta:__tostring() return string.format( "Shape[%d]", self.handle ) end function shape_meta:DrawOutline( r, ... ) assert( self:IsValid() ) return DrawShapeOutline( self.handle, r, ... ) end function shape_meta:DrawHighlight( amount ) assert( self:IsValid() ) return DrawShapeHighlight( self.handle, amount ) end function shape_meta:SetLocalTransform( transform ) assert( self:IsValid() ) return SetShapeLocalTransform( self.handle, transform ) end function shape_meta:SetEmissiveScale( scale ) assert( self:IsValid() ) return SetShapeEmissiveScale( self.handle, scale ) end function shape_meta:SetCollisionFilter( layer, mask ) SetShapeCollisionFilter( self.handle, layer or 1, mask or 255 ) end function shape_meta:GetLocalTransform() assert( self:IsValid() ) return MakeTransformation( GetShapeLocalTransform( self.handle ) ) end function shape_meta:GetWorldTransform() assert( self:IsValid() ) return MakeTransformation( GetShapeWorldTransform( self.handle ) ) end function shape_meta:GetBody() assert( self:IsValid() ) return Body( GetShapeBody( self.handle ) ) end function shape_meta:GetJoints() assert( self:IsValid() ) local joints = GetShapeJoints( self.handle ) for i = 1, #joints do joints[i] = Joint( joints[i] ) end return joints end function shape_meta:GetLights() assert( self:IsValid() ) local lights = GetShapeLights( self.handle ) for i = 1, #lights do lights[i] = Light( lights[i] ) end return lights end function shape_meta:GetWorldBounds() assert( self:IsValid() ) local min, max = GetShapeBounds( self.handle ) return MakeVector( min ), MakeVector( max ) end function shape_meta:GetMaterialAtPos( pos ) assert( self:IsValid() ) return GetShapeMaterialAtPosition( self.handle, pos ) end function shape_meta:GetSize() assert( self:IsValid() ) return GetShapeSize( self.handle ) end function shape_meta:GetVoxelCount() assert( self:IsValid() ) return GetShapeVoxelCount( self.handle ) end function shape_meta:GetClosestPoint( origin ) local hit, point, normal = GetShapeClosestPoint( self.handle, origin ) if not hit then return false end return hit, MakeVector( point ), MakeVector( normal ) end function shape_meta:GetTouching() local min, max = self:GetWorldBounds() local potential = QueryAabbShapes( min - { 0.1, 0.1, 0.1 }, max + { 0.1, 0.1, 0.1 } ) local found = {} for i = 1, #potential do if potential[i] ~= self.handle and self:IsTouching( potential[i] ) then found[#found+1] = Shape( potential[i] ) end end return found end function shape_meta:IsVisible( maxDist, rejectTransparent ) assert( self:IsValid() ) return IsShapeVisible( self.handle, maxDist, rejectTransparent ) end function shape_meta:IsBroken() return not self:IsValid() or IsShapeBroken( self.handle ) end function shape_meta:IsTouching( shape ) assert( self:IsValid() ) local shapeHandle = GetEntityHandle( shape ) return IsShapeTouching( self.handle, shapeHandle ) end function shape_meta._C:transform( setter, val ) if setter then self:SetLocalTransform( val ) else return self:GetLocalTransform() end end function shape_meta._C:emissive( setter, val ) assert(setter, "cannot get emissive") self:SetEmissiveScale( val ) end function shape_meta._C:body( setter ) assert(not setter, "cannot set body") return self:GetBody() end function shape_meta._C:joints( setter ) assert(not setter, "cannot set joints") return self:GetJoints() end function shape_meta._C:lights( setter ) assert(not setter, "cannot set lights") return self:GetLights() end function shape_meta._C:size( setter ) assert(not setter, "cannot set size") return Vector( self:GetSize() ) end function shape_meta._C:broken( setter ) assert(not setter, "cannot set broken") return self:IsBroken() end end)();
(function() local trigger_meta trigger_meta = global_metatable( "trigger", "entity", true ) function IsTrigger( e ) return IsEntity( e ) and e.type == "trigger" end function Trigger( handle ) if handle ~= 0 then return instantiate_global_metatable( "trigger", { handle = handle, type = "trigger" } ) end end function FindTriggerByTag( tag, global ) return Trigger( FindTrigger( tag, global ) ) end function FindTriggersByTag( tag, global ) local t = FindTriggers( tag, global ) for i = 1, #t do t[i] = Trigger( t[i] ) end return t end function trigger_meta:__tostring() return string.format( "Trigger[%d]", self.handle ) end function trigger_meta:SetTransform( transform ) assert( self:IsValid() ) return SetTriggerTransform( self.handle, transform ) end function trigger_meta:GetTransform() assert( self:IsValid() ) return MakeTransformation( GetTriggerTransform( self.handle ) ) end function trigger_meta:GetDistance( origin ) return GetTriggerDistance( self.handle, origin ) end function trigger_meta:GetClosestPoint( origin ) return MakeVector( GetTriggerClosestPoint( self.handle, origin ) ) end function trigger_meta:GetWorldBounds() assert( self:IsValid() ) local min, max = GetTriggerBounds( self.handle ) return MakeVector( min ), MakeVector( max ) end function trigger_meta:IsBodyInTrigger( handle ) assert( self:IsValid() ) local bodyHandle = GetEntityHandle( handle ) return IsBodyInTrigger( self.handle, bodyHandle ) end function trigger_meta:IsVehicleInTrigger( handle ) assert( self:IsValid() ) local vehicleHandle = GetEntityHandle( handle ) return IsVehicleInTrigger( self.handle, vehicleHandle ) end function trigger_meta:IsShapeInTrigger( handle ) assert( self:IsValid() ) local shapeHandle = GetEntityHandle( handle ) return IsShapeInTrigger( self.handle, shapeHandle ) end function trigger_meta:IsPointInTrigger( point ) assert( self:IsValid() ) return IsPointInTrigger( self.handle, point ) end function trigger_meta:IsEmpty( demolision ) assert( self:IsValid() ) local empty, highpoint = IsTriggerEmpty( self.handle, demolision ) return empty, highpoint and MakeVector( highpoint ) end function trigger_meta._C:transform( setter, val ) if setter then self:SetTransform( val ) else return self:GetTransform() end end end)();
(function() local vehicle_meta = global_metatable( "vehicle", "entity", true ) function IsVehicle( e ) return IsEntity( e ) and e.type == "vehicle" end function Vehicle( handle ) if handle ~= 0 then return instantiate_global_metatable( "vehicle", { handle = handle, type = "vehicle" } ) end end function FindVehicleByTag( tag, global ) return Vehicle( FindVehicle( tag, global ) ) end function FindVehiclesByTag( tag, global ) local t = FindVehicles( tag, global ) for i = 1, #t do t[i] = Vehicle( t[i] ) end return t end function vehicle_meta:__tostring() return string.format( "Vehicle[%d]", self.handle ) end function vehicle_meta:Drive( drive, steering, handbrake ) assert( self:IsValid() ) return DriveVehicle( self.handle, drive, steering, handbrake ) end function vehicle_meta:GetTransform() assert( self:IsValid() ) return MakeTransformation( GetVehicleTransform( self.handle ) ) end function vehicle_meta:GetBody() assert( self:IsValid() ) local body = Body( GetVehicleBody( self.handle ) ) return body end function vehicle_meta:GetHealth() assert( self:IsValid() ) return GetVehicleHealth( self.handle ) end function vehicle_meta:GetDriverPos() assert( self:IsValid() ) return MakeVector( GetVehicleDriverPos( self.handle ) ) end function vehicle_meta:GetGlobalDriverPos() return self:GetTransform():ToGlobal( self:GetDriverPos() ) end function vehicle_meta._C:transform( setter ) assert(not setter, "cannot set transform") return self:GetTransform() end function vehicle_meta._C:body( setter ) assert(not setter, "cannot set body") return self:GetBody() end function vehicle_meta._C:health( setter ) assert(not setter, "cannot set health") return self:GetHealth() end end)();
(function() local xml_meta xml_meta = global_metatable( "xmlnode" ) XMLTag = function( type ) return function( attributes ) return instantiate_global_metatable( "xmlnode", { type = type, attributes = attributes } ) end end ParseXML = function( xml ) local pos = 1 local function skipw() local next = xml:find( "[^ \t\n]", pos ) if not next then return false end pos = next return true end local function expect( pattern, noskip ) if not noskip then if not skipw() then return false end end local s, e = xml:find( pattern, pos ) if not s then return false end local pre = pos pos = e + 1 return xml:match( pattern, pre ) end local readtag, readattribute, readstring local rt = { n = "\n", t = "\t", r = "\r", ["0"] = "\0", ["\\"] = "\\", ["\""] = "\"" } readstring = function() local c = expect( "^([\"'])" ) if not c then return false end local start = pos while true do local s = assert( xml:find( "[\\" .. c .. "]", pos ), "Invalid string" ) if xml:sub( s, s ) == "\\" then pos = s + 2 else pos = s + 1 break end end return xml:sub( start, pos - 2 ):gsub( "\\(.)", rt ) end readattribute = function() local name = expect( "^([%d%w_]+)" ) if not name then return false end if expect( "^=" ) then return name, assert( readstring() ) else return name, "1" end end readtag = function() local save = pos if not expect( "^<" ) then return false end local type = expect( "^([%d%w_]+)" ) if not type then pos = save return false end skipw() local attributes = {} repeat local attr, val = readattribute() if attr then attributes[attr] = val end until not attr local children = {} if not expect( "^/>" ) then assert( expect( "^>" ) ) repeat local child = readtag() if child then children[#children + 1] = child end until not child assert( expect( "^</" ) and expect( "^" .. type ) and expect( "^>" ) ) end return XMLTag( type )( attributes )( children ) end return readtag() end function xml_meta:__call( children ) self.children = children return self end function xml_meta:Render() local attr = "" if self.attributes then for name, val in pairs( self.attributes ) do attr = string.format( "%s %s=%q", attr, name, val ) end end local children = {} if self.children then for i = 1, #self.children do children[i] = self.children[i]:Render() end end return string.format( "<%s%s>%s</%s>", self.type, attr, table.concat( children, "" ), self.type ) end end)();
(function() local armature_meta armature_meta = global_metatable( "armature" ) function LoadArmatureFromXML( xml, parts, scale ) scale = scale or 1 local dt = ParseXML( xml ) assert( (dt.type == "prefab" and dt.children[1] and dt.children[1].type == "group") or dt.type == "group", "Invalid Tool XML" ) local shapes = {} local offsets = {} for i = 1, #parts do if type( parts[i] ) == "string" then shapes[i] = parts[i] else shapes[i] = parts[i][1] local v = parts[i][2] offsets[parts[i][1]] = Vec( math.floor( v[1] / 2 ) / 10, 0, -math.floor( v[2] / 2 ) / 10 ) end end local function parseVec( str ) if not str then return Vec( 0, 0, 0 ) end local x, y, z = str:match( "([%d.-]+) ([%d.-]+) ([%d.-]+)" ) return Vec( tonumber( x ), tonumber( y ), tonumber( z ) ) end local function parseTransform( attr ) local pos, angv = parseVec( attr.pos ), parseVec( attr.rot ) return Transform( Vec( pos[1], pos[2], pos[3] ), QuatEuler( angv[1], angv[2], angv[3] ) ) end local function translatebone( node, isLocation ) local t = { name = node.attributes.name, transform = parseTransform( node.attributes ) } local sub = t if not isLocation then t.name = "__FIXED_" .. node.attributes.name t[1] = { name = node.attributes.name } sub = t[1] end sub.shapes = {} for i = 1, #node.children do local child = node.children[i] if child.type == "vox" then local name = child.attributes.object local tr = parseTransform( child.attributes ) local s = child.attributes.scale and tonumber( child.attributes.scale ) or 1 local shape = { id = name, attributes = child.attributes, } if offsets[name] then shape.transform = TransformToParentTransform( tr, Transform( VecScale( offsets[name], -s ), QuatEuler( -90, 0, 0 ) ) ) else shape.transform = tr shape.reoffset_scale = s end table.insert( sub.shapes, shape ) elseif child.type == "group" then sub[#sub + 1] = translatebone( child ) elseif child.type == "location" then sub[#sub + 1] = translatebone( child, true ) end end return t end local bones = translatebone( dt.type == "prefab" and dt.children[1] or dt )[1] bones.transform = Transform( Vec(), QuatEuler( 0, 0, 0 ) ) bones.name = "root" local arm = Armature { shapes = shapes, scale = scale, bones = bones } arm:ComputeBones() return arm, dt end function Armature( definition ) local ids = {} for i, name in ipairs( definition.shapes ) do ids[name] = #definition.shapes - i + 1 end local armature = { root = definition.bones, refs = {}, scale = definition.scale, __noquickload = function() end, dirty = true, } local function dobone( b ) if b.name then armature.refs[b.name] = b end b.transform = b.transform or Transform() b.shapes = b.shapes or {} b.dirty = true for i = 1, #b.shapes do local shape = b.shapes[i] shape.num = ids[shape.id] shape.transform.pos = VecScale( shape.transform.pos, definition.scale or 1 ) end b.children = {} for i = 1, #b do b.children[i] = dobone( b[i] ) end return b end dobone( armature.root ) return instantiate_global_metatable( "armature", armature ) end local function computebone( bone, transform, scale, dirty ) dirty = dirty or bone.dirty or bone.jiggle_transform if dirty or not bone.gr_transform then bone.gr_transform = TransformToParentTransform( transform, bone.transform ) if bone.jiggle_transform then bone.gr_transform = TransformToParentTransform( bone.gr_transform, bone.jiggle_transform ) end bone.g_transform = Transform( VecScale( bone.gr_transform.pos, scale ), bone.gr_transform.rot ) bone.dirty = false end for i = 1, #bone.children do computebone( bone.children[i], bone.gr_transform, scale, dirty ) end end function armature_meta:ComputeBones() computebone( self.root, Transform(), self.scale or 1 ) self.dirty = false end local function applybone( shapes, bone ) for i = 1, #bone.shapes do local offset = bone.shapes[i] local handle = GetEntityHandle and GetEntityHandle( shapes[offset.num] ) or shapes[offset.num] SetShapeLocalTransform( handle, TransformToParentTransform( bone.g_transform, offset.transform ) ) end for i = 1, #bone.children do applybone( shapes, bone.children[i] ) end end function armature_meta:Apply( shapes ) if self.dirty or self.jiggle then self:ComputeBones() end applybone( shapes, self.root ) end local function getshapetransforms( offsets, bone ) for i = 1, #bone.shapes do local shape = bone.shapes[i] shape.global_transform = TransformToParentTransform( bone.g_transform, shape.transform ) offsets[#offsets + 1] = shape end for i = 1, #bone.children do getshapetransforms( offsets, bone.children[i] ) end end function armature_meta:GetShapeTransforms() if self.dirty or self.jiggle then self:ComputeBones() end local offsets = {} getshapetransforms( offsets, self.root ) return offsets end function armature_meta:SetBoneTransform( bone, transform ) local b = self.refs[bone] if not b then return end self.dirty = true b.dirty = true b.transform = transform end function armature_meta:GetBoneTransform( bone ) local b = self.refs[bone] if not b then return Transformation() end return b.transform end function armature_meta:GetBoneGlobalTransform( bone ) local b = self.refs[bone] if not b then return Transformation() end if self.dirty then self:ComputeBones() end return b.g_transform end function armature_meta:SetBoneJiggle( bone, jiggle, constraint ) local b = self.refs[bone] if not b then return end self.dirty = true if jiggle > 0 then self.jiggle = true end b.jiggle = math.atan( jiggle ) / math.pi * 2 b.jiggle_constraint = constraint end function armature_meta:GetBoneJiggle( bone ) local b = self.refs[bone] if not b then return 0 end return b.jiggle, b.jiggle_constraint end function armature_meta:ResetJiggle() for _, b in pairs( self.refs ) do b.jiggle_transform = nil end self.dirty = true end local function updatebone( bone, current_transform, prev_transform, dt, gravity ) local current_transform_local = TransformToParentTransform( current_transform, bone.transform ) local prev_transform_local = TransformToParentTransform( prev_transform, bone.old_transform or bone.transform ) bone.old_transform = bone.transform if bone.jiggle then prev_transform_local = TransformToParentTransform( prev_transform_local, bone.jiggle_transform or Transform() ) local local_diff = TransformToLocalTransform( current_transform_local, prev_transform_local ) local target = TransformToParentPoint( local_diff, Vec( 0, 0, -2 / dt ) ) if bone.jiggle_constraint and bone.jiggle_constraint.gravity then target = VecAdd( target, TransformToLocalVec( current_transform_local, VecScale( gravity, bone.jiggle_constraint.gravity ) ) ) end local lookat = QuatLookAt( Vec(), target ) bone.jiggle_transform = Transform( Vec(), QuatSlerp( lookat, QuatEuler( 0, 0, 0 ), 1 - bone.jiggle ) ) current_transform_local = TransformToParentTransform( current_transform_local, bone.jiggle_transform ) end for i = 1, #bone.children do updatebone( bone.children[i], current_transform_local, prev_transform_local, dt, gravity ) end end function armature_meta:UpdatePhysics( diff, dt, gravity ) dt = dt or 0.01666 updatebone( self.root, Transform(), Transform( VecScale( diff.pos, 1 / dt ), diff.rot ), dt, gravity or Vec( 0, -10, 0 ) ) end local function DebugAxis( tr, s ) s = s or 1 DebugLine( tr.pos, TransformToParentPoint( tr, Vec( 1 * s, 0, 0 ) ), 1, 0, 0 ) DebugLine( tr.pos, TransformToParentPoint( tr, Vec( 0, 1 * s, 0 ) ), 0, 1, 0 ) DebugLine( tr.pos, TransformToParentPoint( tr, Vec( 0, 0, 1 * s ) ), 0, 0, 1 ) end function armature_meta:DrawDebug( transform ) transform = transform or Transform() DebugAxis( transform, 0.05 ) for k, v in pairs( self.refs ) do local r = TransformToParentTransform( transform, v.g_transform ) local g = v.name:find( "^__FIXED_" ) and 1 or 0 for i = 1, #v.children do DebugLine( r.pos, TransformToParentTransform( transform, v.children[i].g_transform ).pos, 1, 1 - g, g, .4 ) end for i = 1, #v.shapes do local offset = v.shapes[i] local p = TransformToParentTransform( transform, TransformToParentTransform( v.g_transform, offset.tr ) ) DebugAxis( p, 0.03 ) DebugLine( r.pos, p.pos, 0, 1, 1, .4 ) end end end end)();
(function() local UMF_tools = {} local post_init = false local previous local current_hook local current_hook_updated local transform_info = {} local function offset_shape( editor_transform, scale, x, y ) local vec = VecScale( Vec( -math.floor( x / 2 ), 0, math.floor( y / 2 ) ), scale / 10 ) return TransformToParentTransform( editor_transform, Transform( vec, QuatEuler( -90, 0, 0 ) ) ) end local function parseVec( str ) if not str then return Vec( 0, 0, 0 ) end local x, y, z = str:match( "([%d.-]+) ([%d.-]+) ([%d.-]+)" ) return Vec( tonumber( x ), tonumber( y ), tonumber( z ) ) end local function parseTransform( attr ) local pos, angv = parseVec( attr.pos ), parseVec( attr.rot ) return Transform( Vec( pos[1], pos[2], pos[3] ), QuatEuler( angv[1], angv[2], angv[3] ) ) end local id_counter = 0 local function translatebone( node, isLocation, modelinfo ) modelinfo = modelinfo or { vox = {} } local t = { name = node.attributes.name, transform = parseTransform( node.attributes ) } local sub = t if not isLocation then t.name = "__FIXED_" .. (node.attributes.name or "UNKNOWN") t[1] = { name = node.attributes.name } sub = t[1] end sub.shapes = {} for i = 1, #node.children do local child = node.children[i] if child.type == "vox" then id_counter = id_counter + 1 child.attributes.tags = (child.attributes.tags or "") .. " __UMF_TOOL_SHAPE_ID=" .. id_counter child.id = id_counter table.insert( sub.shapes, { id = id_counter, attributes = child.attributes, transform = Transform(), editor_transform = parseTransform( child.attributes ), scale = child.attributes.scale and tonumber( child.attributes.scale ) or 1, } ) table.insert( modelinfo.vox, child ) elseif child.type == "voxbox" then id_counter = id_counter + 1 child.attributes.tags = (child.attributes.tags or "") .. " __UMF_TOOL_SHAPE_ID=" .. id_counter table.insert( sub.shapes, { id = id_counter, attributes = child.attributes, transform = parseTransform( child.attributes ), scale = child.attributes.scale and tonumber( child.attributes.scale ) or 1, } ) child.id = id_counter elseif child.type == "group" then sub[#sub + 1] = translatebone( child, false, modelinfo ) elseif child.type == "location" then sub[#sub + 1] = translatebone( child, true, modelinfo ) end end return t, modelinfo end local function load_xml( xml ) local dt = ParseXML( xml ) local root = dt.type == "prefab" and dt.children[1] or dt assert( root and root.type == "group", "Invalid Tool XML" ) local root_bone, modelinfo = translatebone( root ) root_bone.name = "root" root_bone.transform = Transform() local armature = Armature { shapes = {}, bones = root_bone } armature:ComputeBones() armature.spawnable = root:Render() armature.modelinfo = modelinfo return armature, root end local allowed_types = { screen = true, light = true } local function attach_armature( armature, tool_body ) if not armature or not armature.spawnable then return end local tool_shapes = GetBodyShapes( tool_body ) for i = 1, #tool_shapes do Delete( tool_shapes[i] ) end armature.shapes = {} local spawned = Spawn( armature.spawnable, Transform( Vec( 10000, 10000, 10000 ) ), true, false ) local last local removelist = {} for i = 1, #spawned do local etype = GetEntityType( spawned[i] ) if etype == "shape" then local id = tonumber( GetTagValue( spawned[i], "__UMF_TOOL_SHAPE_ID" ) ) if id then armature.shapes[id] = spawned[i] if HasTag( spawned[i], "collider" ) and not last then last = spawned[i] else SetShapeBody( spawned[i], tool_body ) end else removelist[#removelist + 1] = i end elseif not allowed_types[etype] then removelist[#removelist + 1] = i end end if last then SetShapeBody( last, tool_body ) end local shapes_data = armature:GetShapeTransforms() for i = 1, #shapes_data do local data = shapes_data[i] if data.editor_transform then local shape = armature.shapes[data.id] data.transform = offset_shape( data.editor_transform, data.scale, GetShapeSize( shape ) ) end end for i = 1, #removelist do Delete( spawned[removelist[i]] ) end end local function detach_armature( armature ) if not armature or not armature.shapes then return end for key, shape in pairs( armature.shapes ) do Delete( shape ) end armature.shapes = nil end local function apply_armature( armature ) if not armature then return end local shapes_data = armature:GetShapeTransforms() for i = 1, #shapes_data do SetShapeLocalTransform( armature.shapes[shapes_data[i].id], shapes_data[i].global_transform ) end end local tool_meta tool_meta = global_metatable( "tool", nil, true ) function tool_meta._C:ammo( setter, val ) local key = "game.tool." .. self.id .. ".ammo" local keystr = key .. ".display" if setter then if type( val ) == "number" then SetFloat( key, val ) ClearKey( keystr ) else SetFloat( key, 0 ) SetString( key .. ".display", tostring( val or "" ) ) end elseif HasKey( keystr ) then return GetString( keystr ) else return GetFloat( key ) end end function tool_meta._C:enabled( setter, val ) local key = "game.tool." .. self.id .. ".enabled" if setter then SetBool( key, val ) else return GetBool( key ) end end function tool_meta:DrawInWorld( transform ) SetToolTransform( TransformToLocalTransform( GetPlayerCameraTransform(), transform ) ) end local function scale_transform( tr, s ) return Transform( VecLerp( Vec(), tr.pos, s ), QuatSlerp( Quat( 0, 0, 0, 1 ), tr.rot, s ) ) end local function get_transform_info( key ) local t = transform_info[current_hook] if not t then t = {} transform_info[current_hook] = t end if not current_hook_updated then local current = GetBodyTransform( GetToolBody() ) t.old = t.current or current t.olddt = t.currentdt or 1 t.current = current t.currentdt = GetTimeStep() t.diff = TransformToLocalTransform( t.current, t.old ) local rdiff = scale_transform( TransformToLocalTransform( t.old, t.current ), t.currentdt / t.olddt ) t.fix = TransformToParentTransform( t.current, rdiff ) current_hook_updated = true end return key and t[key] or t end function tool_meta:GetTransform( predicted ) return MakeTransformation( get_transform_info( predicted and "fix" or "current" ) ) end function tool_meta:GetTransformDelta() return MakeTransformation( get_transform_info( "diff" ) ) end function tool_meta:GetBoneGlobalTransform( bone, nopredicted ) if not self.armature then return Transformation( Vec(), Quat() ) end return self:GetTransform( not nopredicted ):ToGlobal( self.armature:GetBoneGlobalTransform( bone ) ) end function tool_meta:DrawDebug( nobones, nobounds, nopredicted ) if not self.armature then return end local ptr = self:GetTransform( not nopredicted ) if not nobones then self.armature:DrawDebug( ptr ) end if not nobounds then local shapes_data = self.armature:GetShapeTransforms() for i = 1, #shapes_data do local data = shapes_data[i] local shape = self.armature.shapes[data.id] visual.drawbox( ptr:ToGlobal( data.global_transform ), Vec( 0, 0, 0 ), VecScale( Vec( GetShapeSize( shape ) ), data.scale / 10 ), { r = 1, g = 1, b = 1, a = .2, writeZ = false } ) end end end function tool_meta:Register() local enabledKey = "game.tool." .. self.id .. ".enabled" if not self.noregister and not GetBool( enabledKey ) then RegisterTool( self.id, self.printname or self.id, self.model or "", self.group or 6 ) if not HasKey( enabledKey ) then SetBool( enabledKey, true ) end end end function tool_meta:Emit( event, ... ) if event then local handler = rawget( self, event ) if handler then return softassert( pcall( handler, self, ... ) ) end end return true end local function previous_tool( force ) return previous and (force or GetBool( "game.player.canusetool" )) and UMF_tools[previous] end local function active_tool( force ) return (force or GetBool( "game.player.canusetool" )) and UMF_tools[GetString( "game.player.tool" )] end hook.add( "base.init", "api.tool_loader", function() for _, tool in pairs( UMF_tools ) do tool:Register() if tool.xml and not tool.armature then tool.armature = load_xml( tool.xml ) end end post_init = true end ) hook.add( "base.command.quickload", "api.tool_loader", function() for _, tool in pairs( UMF_tools ) do if tool.xml then tool.armature = load_xml( tool.xml ) end end end ) hook.add( "api.mouse.wheel", "api.tool_loader", function( ds ) local tool = previous_tool() if tool then tool:Emit( "MouseWheel", ds ) end end ) hook.add( "base.update", "api.tool_loader", function( dt ) current_hook = "update" current_hook_updated = false local tool = active_tool() if tool then if tool.armature then tool.armature:UpdatePhysics( tool:GetTransformDelta(), GetTimeStep(), TransformToLocalVec( tool:GetTransform(), Vec( 0, -10, 0 ) ) ) end tool:Emit( "Update", dt ) end end ) hook.add( "base.tick", "api.tool_loader", function( dt ) current_hook = "tick" current_hook_updated = false local cur = GetString( "game.player.tool" ) local prevtool = previous_tool( true ) if prevtool then local _, dolock = prevtool:Emit( "ShouldLockMouseWheel" ) if dolock ~= nil then SetBool( "game.input.locktool", dolock ) if previous ~= cur and dolock then SetString( "game.player.tool", previous ) cur = previous end end if previous ~= cur then prevtool:Emit( "Holster" ) detach_armature( prevtool.armature ) prevtool._BODY = nil prevtool._SHAPES = nil end end local tool = UMF_tools[cur] if tool then local body = GetToolBody() if (GetPlayerVehicle() ~= 0 or GetBool( "game.map.enabled" )) and tool._BODY then tool:Emit( "Holster" ) detach_armature( tool.armature ) tool._BODY = nil tool._SHAPES = nil return end if body == 0 then return end if previous == cur and (not tool._BODY or tool._BODY.handle ~= body) then tool._BODY = Body( body ) attach_armature( tool.armature, body ) tool:Emit( "SetupModel", body, GetBodyShapes( body ) ) tool._SHAPES = tool._BODY:GetShapes() tool:Emit( "Deploy" ) if tool.armature then tool.armature:ResetJiggle() end end if IsValid( tool._BODY ) then if previous == cur then tool:Emit( "Animate", tool._BODY, tool._SHAPES ) end if tool.armature then apply_armature( tool.armature ) end end if HasKey( "game.tool." .. tool.id .. ".ammo.display" ) then SetInt( "game.tool." .. tool.id .. ".ammo", 0 ) end tool:Emit( "Tick", dt ) end previous = cur end ) hook.add( "api.firsttick", "api.tool_loader", function() for _, tool in pairs( UMF_tools ) do tool:Emit( "Initialize" ) end end ) hook.add( "base.draw", "api.tool_loader", function( dt ) current_hook = "draw" current_hook_updated = false local tool = active_tool() if tool then tool:Emit( "Draw", dt ) end end ) hook.add( "base.postUpdate", "api.tool_loader", function( dt ) current_hook = "postUpdate" current_hook_updated = false local tool = active_tool() if tool then tool:Emit( "PostUpdate", dt ) end end ) hook.add( "base.render", "api.tool_loader", function( dt ) current_hook = "render" current_hook_updated = false local tool = active_tool() if tool then tool:Emit( "Render", dt ) end end ) hook.add( "api.mouse.pressed", "api.tool_loader", function( button ) local tool = active_tool() if tool then tool:Emit( button == "lmb" and "LeftClick" or button == "rmb" and "RightClick" ) tool:Emit( "MousePressed", button ) end end ) hook.add( "api.mouse.released", "api.tool_loader", function( button ) local tool = active_tool() if tool then tool:Emit( button == "lmb" and "LeftClickReleased" or button == "rmb" and "RightClickReleased" ) tool:Emit( "MouseReleased", button ) end end ) function RegisterToolUMF( id, tool, immediateRegister ) tool.id = id UMF_tools[id] = tool if type( tool.model ) == "string" then if tool.model:match( "^[\r\n\t ]*<" ) then tool.xml = tool.model tool.model = "vox/tool/wire.vox" end elseif type( tool.model ) == "table" and tool.model.prefab then tool.xml = tool.model.prefab tool.model = "vox/tool/wire.vox" end if not tool.group and HasKey( "game.tool." .. id .. ".skin.group" ) then tool.group = GetInt( "game.tool." .. id .. ".skin.group" ) end instantiate_global_metatable( "tool", tool ) if post_init or immediateRegister then tool:Register() end return tool end end)();
(function() visual = {} degreeToRadian = math.pi / 180 COLOR_WHITE = { r = 255 / 255, g = 255 / 255, b = 255 / 255, a = 255 / 255 } COLOR_BLACK = { r = 0, g = 0, b = 0, a = 255 / 255 } COLOR_RED = { r = 255 / 255, g = 0, b = 0, a = 255 / 255 } COLOR_ORANGE = { r = 255 / 255, g = 128 / 255, b = 0, a = 255 / 255 } COLOR_YELLOW = { r = 255 / 255, g = 255 / 255, b = 0, a = 255 / 255 } COLOR_GREEN = { r = 0, g = 255 / 255, b = 0, a = 255 / 255 } COLOR_CYAN = { r = 0, g = 255 / 255, b = 128 / 255, a = 255 / 255 } COLOR_AQUA = { r = 0, g = 255 / 255, b = 255 / 255, a = 255 / 255 } COLOR_BLUE = { r = 0, g = 0, b = 255 / 255, a = 255 / 255 } COLOR_VIOLET = { r = 128 / 255, g = 0, b = 255 / 255, a = 255 / 255 } COLOR_PINK = { r = 255 / 255, g = 0, b = 255 / 255, a = 255 / 255 } if DrawSprite then function visual.huergb( p, q, t ) if t < 0 then t = t + 1 end if t > 1 then t = t - 1 end if t < 1 / 6 then return p + (q - p) * 6 * t end if t < 1 / 2 then return q end if t < 2 / 3 then return p + (q - p) * (2 / 3 - t) * 6 end return p end function visual.hslrgb( h, s, l ) local r, g, b if s == 0 then r = l g = l b = l else local huergb = visual.huergb local q = l < .5 and l * (1 + s) or l + s - l * s local p = 2 * l - q r = huergb( p, q, h + 1 / 3 ) g = huergb( p, q, h ) b = huergb( p, q, h - 1 / 3 ) end return Vec( r, g, b ) end function visual.drawsprite( sprite, source, radius, info ) local r, g, b, a local writeZ, additive = true, false local target = GetCameraTransform().pos local DrawFunction = DrawSprite radius = radius or 1 if info then r = info.r and info.r or 1 g = info.g and info.g or 1 b = info.b and info.b or 1 a = info.a and info.a or 1 target = info.target or target if info.writeZ ~= nil then writeZ = info.writeZ end if info.additive ~= nil then additive = info.additive end DrawFunction = info.DrawFunction ~= nil and info.DrawFunction or DrawFunction end DrawFunction( sprite, Transform( source, QuatLookAt( source, target ) ), radius, radius, r, g, b, a, writeZ, additive ) end function visual.drawsprites( sprites, sources, radius, info ) sprites = type( sprites ) ~= "table" and { sprites } or sprites for i = 1, #sprites do for j = 1, #sources do visual.drawsprite( sprites[i], sources[j], radius, info ) end end end function visual.drawline( sprite, source, destination, info ) local r, g, b, a local writeZ, additive = true, false local target = GetCameraTransform().pos local DrawFunction = DrawLine local width = 0.03 if info then r = info.r and info.r or 1 g = info.g and info.g or 1 b = info.b and info.b or 1 a = info.a and info.a or 1 width = info.width or width target = info.target or target if info.writeZ ~= nil then writeZ = info.writeZ end if info.additive ~= nil then additive = info.additive end DrawFunction = info.DrawFunction ~= nil and info.DrawFunction or (info.writeZ == false and DebugLine or DrawLine) end if sprite then local middle = VecScale( VecAdd( source, destination ), .5 ) local len = VecLength( VecSub( source, destination ) ) local transform = Transform( middle, QuatRotateQuat( QuatLookAt( source, destination ), QuatEuler( -90, 0, 0 ) ) ) local target_local = TransformToLocalPoint( transform, target ) target_local[2] = 0 local trlook = Transform( nil, QuatLookAt( target_local, nil ) ) if info and info.turn then trlook.rot = QuatRotateQuat(trlook.rot, QuatEuler(0,0,90)) width, len = len, width end local transform_fixed = TransformToParentTransform( transform, trlook ) DrawSprite( sprite, transform_fixed, width, len, r, g, b, a, writeZ, additive ) else DrawFunction( source, destination, r, g, b, a ); end end function visual.drawlines( sprites, sources, connect, info ) sprites = type( sprites ) ~= "table" and { sprites } or sprites for i = 1, #sprites do local sourceCount = #sources for j = 1, sourceCount - 1 do visual.drawline( sprites[i], sources[j], sources[j + 1], info ) end if connect then visual.drawline( sprites[i], sources[1], sources[sourceCount], info ) end end end function visual.drawaxis( transform, quat, radius, writeZ ) local DrawFunction = writeZ and DrawLine or DebugLine if not transform.pos then transform = Transform( transform, quat or QUAT_ZERO ) end radius = radius or 1 DrawFunction( transform.pos, TransformToParentPoint( transform, Vec( radius, 0, 0 ) ), 1, 0, 0 ) DrawFunction( transform.pos, TransformToParentPoint( transform, Vec( 0, radius, 0 ) ), 0, 1, 0 ) DrawFunction( transform.pos, TransformToParentPoint( transform, Vec( 0, 0, radius ) ), 0, 0, 1 ) end function visual.drawpolygon( transform, radius, rotation, sides, info ) sides = sides or 4 radius = radius or 1 local offset, interval = math.rad( rotation or 0 ), 2 * math.pi / sides local arc = false local r, g, b, a = 1, 1, 1, 1 local DrawFunction = DrawLine if info then r = info.r or r g = info.g or g b = info.b or b a = info.a or a if info.arc then arc = true interval = interval * info.arc / 360 end DrawFunction = info.DrawFunction or (info.writeZ == false and DebugLine or DrawLine) end local points = {} for i = 0, sides - 1 do points[i + 1] = TransformToParentPoint( transform, Vec( math.sin( offset + i * interval ) * radius, 0, math.cos( offset + i * interval ) * radius ) ) if i > 0 then DrawFunction( points[i], points[i + 1], r, g, b, a ) end end if arc then points[#points + 1] = TransformToParentPoint( transform, Vec( math.sin( offset + sides * interval ) * radius, 0, math.cos( offset + sides * interval ) * radius ) ) DrawFunction( points[#points - 1], points[#points], r, g, b, a ) else DrawFunction( points[#points], points[1], r, g, b, a ) end return points end function visual.drawbox( transform, min, max, info ) local r, g, b, a local DrawFunction = DrawLine local points = { TransformToParentPoint( transform, Vec( min[1], min[2], min[3] ) ), TransformToParentPoint( transform, Vec( max[1], min[2], min[3] ) ), TransformToParentPoint( transform, Vec( min[1], max[2], min[3] ) ), TransformToParentPoint( transform, Vec( max[1], max[2], min[3] ) ), TransformToParentPoint( transform, Vec( min[1], min[2], max[3] ) ), TransformToParentPoint( transform, Vec( max[1], min[2], max[3] ) ), TransformToParentPoint( transform, Vec( min[1], max[2], max[3] ) ), TransformToParentPoint( transform, Vec( max[1], max[2], max[3] ) ), } if info then r = info.r and info.r or 1 g = info.g and info.g or 1 b = info.b and info.b or 1 a = info.a and info.a or 1 DrawFunction = info.DrawFunction ~= nil and info.DrawFunction or (info.writeZ == false and DebugLine or DrawLine) end DrawFunction( points[1], points[2], r, g, b, a ) DrawFunction( points[1], points[3], r, g, b, a ) DrawFunction( points[1], points[5], r, g, b, a ) DrawFunction( points[4], points[3], r, g, b, a ) DrawFunction( points[4], points[2], r, g, b, a ) DrawFunction( points[4], points[8], r, g, b, a ) DrawFunction( points[6], points[5], r, g, b, a ) DrawFunction( points[6], points[8], r, g, b, a ) DrawFunction( points[6], points[2], r, g, b, a ) DrawFunction( points[7], points[8], r, g, b, a ) DrawFunction( points[7], points[5], r, g, b, a ) DrawFunction( points[7], points[3], r, g, b, a ) return points end function visual.drawprism( transform, radius, depth, rotation, sides, info ) local points = {} local iteration = 1 local pow, sqrt, sin, cos = math.pow, math.sqrt, math.sin, math.cos local r, g, b, a local DrawFunction = DrawLine radius = sqrt( 2 * pow( radius, 2 ) ) or sqrt( 2 ) depth = depth or 1 rotation = rotation or 0 sides = sides or 4 if info then r = info.r and info.r or 1 g = info.g and info.g or 1 b = info.b and info.b or 1 a = info.a and info.a or 1 DrawFunction = info.DrawFunction ~= nil and info.DrawFunction or (info.writeZ == false and DebugLine or DrawLine) end for v = 0, 360, 360 / sides do points[iteration] = TransformToParentPoint( transform, Vec( sin( (v + rotation) * degreeToRadian ) * radius, depth, cos( (v + rotation) * degreeToRadian ) * radius ) ) points[iteration + 1] = TransformToParentPoint( transform, Vec( sin( (v + rotation) * degreeToRadian ) * radius, -depth, cos( (v + rotation) * degreeToRadian ) * radius ) ) if iteration > 2 then DrawFunction( points[iteration], points[iteration + 1], r, g, b, a ) DrawFunction( points[iteration - 2], points[iteration], r, g, b, a ) DrawFunction( points[iteration - 1], points[iteration + 1], r, g, b, a ) end iteration = iteration + 2 end return points end function visual.drawsphere( transform, radius, rotation, samples, info ) local points = {} local sqrt, sin, cos = math.sqrt, math.sin, math.cos local r, g, b, a local DrawFunction = DrawLine radius = radius or 1 rotation = rotation or 0 samples = samples or 100 if info then r = info.r and info.r or 1 g = info.g and info.g or 1 b = info.b and info.b or 1 a = info.a and info.a or 1 DrawFunction = info.DrawFunction ~= nil and info.DrawFunction or (info.writeZ == false and DebugLine or DrawLine) end local points = {} for i = 0, samples do local y = 1 - (i / (samples - 1)) * 2 local rad = sqrt( 1 - y * y ) local theta = 2.399963229728653 * i local x = cos( theta ) * rad local z = sin( theta ) * rad local point = TransformToParentPoint( Transform( transform.pos, QuatRotateQuat( transform.rot, QuatEuler( 0, rotation, 0 ) ) ), Vec( x * radius, y * radius, z * radius ) ) DrawFunction( point, VecAdd( point, Vec( 0, .01, 0 ) ), r, g, b, a ) points[i + 1] = point end return points end function visual.drawwiresphere( transform, radius, points, info ) radius = radius or 1 points = points or 32 if not info or not info.nolines then local tr_r = TransformToParentTransform( transform, Transform( Vec(), QuatEuler( 90, 0, 0 ) ) ) local tr_f = TransformToParentTransform( transform, Transform( Vec(), QuatEuler( 0, 0, 90 ) ) ) visual.drawpolygon( transform, radius, 0, points, info ) visual.drawpolygon( tr_r, radius, 0, points, info ) visual.drawpolygon( tr_f, radius, 0, points, info ) end local cam = info and info.target or GetCameraTransform().pos local diff = VecSub( transform.pos, cam ) local len = VecLength( diff ) if len < radius then return end local a = math.pi / 2 - math.asin( radius / len ) local vtr = Transform( VecAdd( transform.pos, VecScale( diff, -math.cos( a ) / len ) ), QuatRotateQuat( QuatLookAt( transform.pos, cam ), QuatEuler( 90, 0, 0 ) ) ) visual.drawpolygon( vtr, radius * math.sin( a ), 0, points, info ) end end end)();
(function() local registryloaded = UMF_SOFTREQUIRE "src/util/registry.lua" if registryloaded then function OptionsKeys( def ) return util.structured_table( "savegame.mod", def ) end end OptionsMenu = setmetatable( {}, { __call = function( self, def ) def.title_size = def.title_size or 50 local pos = def.center or 0.5 local f = OptionsMenu.Group( def ) draw = function() UiPush() UiTranslate( UiWidth() * pos, 60 ) UiPush() local fw, fh = f() UiPop() UiTranslate( 0, fh + 20 ) UiFont( "regular.ttf", 30 ) UiAlign( "center top" ) UiButtonImageBox( "ui/common/box-outline-6.png", 6, 6 ) if UiTextButton( "Close" ) then Menu() end UiPop() end return f end, } ) function OptionsMenu.Group( def ) local elements = {} if def.title then elements[#elements + 1] = OptionsMenu.Text( def.title, { size = def.title_size or 40, pad_bottom = def.title_pad or 15, align = def.title_align or "center top", } ) end for i = 1, #def do elements[#elements + 1] = def[i] end local condition = def.condition return function() if condition and not condition() then return 0, 0 end local mw, mh = 0, 0 for i = 1, #elements do UiPush() local w, h = elements[i]() UiPop() UiTranslate( 0, h ) mh = mh + h mw = math.max( mw, w ) end return mw, mh end end function OptionsMenu.Columns( def ) local elements = {} for i = 1, #def do elements[#elements + 1] = def[i] end return function() local mw, mh = def.width or UiWidth(), 0 UiPush() UiTranslate(mw * ( - 0.5 + 0.5 / #elements ), 0) for i = 1, #elements do UiPush() local _, gh = elements[i]() UiPop() UiTranslate(mw * ( 1 / #elements ), 0) mh = math.max( mh, gh ) end UiPop() return mw, mh end end function OptionsMenu.Text( text, options ) options = options or {} local size = options.size or 30 local align = options.align or "left top" local offset = options.offset or (align:find( "left" ) and -400) or 0 local font = options.font or "regular.ttf" local padt = options.pad_top or 0 local padb = options.pad_bottom or 5 local condition = options.condition return function() if condition and not condition() then return 0, 0 end UiTranslate( offset, padt ) UiFont( font, size ) UiAlign( align ) UiWordWrap( 800 ) local tw, th = UiText( text ) return tw, th + padt + padb end end function OptionsMenu.Spacer( space, spacew, condition ) return function() if condition and not condition() then return 0, 0 end return spacew or 0, space end end local function getvalue( id, def, func ) local key = "savegame.mod." .. id if HasKey( key ) then return (func or GetString)( key ) else return def end end local function setvalue( id, val, func ) local key = "savegame.mod." .. id if val ~= nil then (func or SetString)( key, val ) else ClearKey( key ) end end function OptionsMenu.Keybind( def ) local text = def.name or def.id local size = def.size or 30 local padt = def.pad_top or 0 local padb = def.pad_bottom or 5 local allowmouse = def.allowmouse or false local value = string.upper( getvalue( def.id, def.default ) or "" ) if value == "" then value = "<none>" end local pressed = false local condition = def.condition return function() if condition and not condition() then return 0, 0 end UiTranslate( -4, padt ) UiFont( "regular.ttf", size ) local fheight = UiFontHeight() UiAlign( "right top" ) local lw, lh = UiText( text ) UiTranslate( 8, 0 ) UiAlign( "left top" ) UiColor( 1, 1, 0 ) local tempv = value if pressed then tempv = "<press a key>" local k = InputLastPressedKey() if k == "esc" then pressed = false elseif k ~= "" then value = string.upper( k ) tempv = value setvalue( def.id, k ) pressed = false end end local rw, rh = UiGetTextSize( tempv ) if allowmouse then local inrect = UiIsMouseInRect( rw, rh ) local mouse = InputPressed( "lmb" ) and "lmb" or InputPressed( "rmb" ) and "rmb" or InputPressed( "mmb" ) and "mmb" if inrect and mouse == "lmb" then pressed = not pressed elseif pressed and mouse then value = string.upper( mouse ) tempv = value rw, rh = UiGetTextSize( tempv ) setvalue( def.id, mouse ) pressed = false end UiTextButton( tempv ) elseif UiTextButton( tempv ) then pressed = not pressed end UiTranslate( rw, 0 ) if value ~= "<none>" then UiColor( 1, 0, 0 ) if UiTextButton( "x" ) then value = "<none>" setvalue( def.id, "" ) end UiTranslate( size * 0.8, 0 ) end if getvalue( def.id ) then UiColor( 0.5, 0.8, 1 ) if UiTextButton( "Reset" ) then value = def.default and string.upper( def.default ) or "<none>" setvalue( def.id ) end end return lw + 8 + rw, fheight + padt + padb end end function OptionsMenu.Slider( def ) local text = def.name or def.id local size = def.size or 30 local padt = def.pad_top or 0 local padb = def.pad_bottom or 5 local min = def.min or 0 local max = def.max or 100 local range = max - min local getter = def.getter or GetFloat local setter = def.setter or SetFloat local value = getvalue( def.id, def.default, getter ) local formatter = def.formatter local format = string.format( "%%.%df", math.max( 0, math.floor( math.log10( 1000 / range ) ) ) ) local step = def.step local condition = def.condition return function() if condition and not condition() then return 0, 0 end UiTranslate( -4, padt ) UiFont( "regular.ttf", size ) local fheight = UiFontHeight() UiAlign( "right top" ) local lw, lh = UiText( text ) UiTranslate( 16, lh / 2 ) UiAlign( "left middle" ) UiColor( 1, 1, 0.5 ) UiRect( 200, 2 ) UiTranslate( -8, 0 ) local prev = value value = UiSlider( "ui/common/dot.png", "x", (value - min) * 200 / range, 0, 200 ) * range / 200 + min if step then value = math.floor( value / step + 0.5 ) * step end UiTranslate( 216, 0 ) UiText( formatter and formatter( value ) or string.format( format, value ) ) if value ~= prev then setvalue( def.id, value, setter ) end return lw + 224, fheight + padt + padb end end function OptionsMenu.Toggle( def ) local text = def.name or def.id local size = def.size or 30 local padt = def.pad_top or 0 local padb = def.pad_bottom or 5 local value = getvalue( def.id, def.default, GetBool ) local condition = def.condition return function() if condition and not condition() then return 0, 0 end UiTranslate( -4, padt ) UiFont( "regular.ttf", size ) local fheight = UiFontHeight() UiAlign( "right top" ) local lw, lh = UiText( text ) UiTranslate( 8, 0 ) UiAlign( "left top" ) UiColor( 1, 1, 0 ) if UiTextButton( value and "Enabled" or "Disabled" ) then value = not value setvalue( def.id, value, SetBool ) end return lw + 100, fheight + padt + padb end end local opened_color function OptionsMenu.Color( def ) local text = def.name or def.id local size = def.size or 30 local padt = def.pad_top or 0 local padb = def.pad_bottom or 5 local value = getvalue( def.id, def.default, GetString ) local open = false local condition = def.condition return function() if condition and not condition() then return 0, 0 end UiTranslate( -4, padt ) UiFont( "regular.ttf", size ) local fheight = UiFontHeight() UiAlign( "right top" ) local lw, lh = UiText( text ) UiTranslate( 8, 0 ) UiAlign( "left top" ) local val = getvalue( def.id ) local r, g, b = (val or def.default):match("([%d.]+);([%d.]+);([%d.]+)") r, g, b = tonumber(r) or 1, tonumber(g) or 1, tonumber(b) or 1 UiPush() UiColor( r, g, b ) UiRect( size, size ) UiPop() if val then UiPush() UiTranslate( size + 8, 0 ) UiColor( 0.5, 0.8, 1 ) if UiTextButton( "Reset" ) then value = def.default setvalue( def.id ) end UiPop() end if UiBlankButton( size, size ) then if opened_color == def.id then opened_color = nil else opened_color = def.id end open = not open end if opened_color == def.id then fheight = fheight + size + 8 UiTranslate(-4, 6 + size * 1.5) UiAlign( "center middle" ) UiPush() UiTranslate(0, 0) UiScale(100/64, size/64) UiTranslate(-64 - 12*size/64, 0) UiColor( 1, 0, 0 ) UiImage("ui/common/hgradient-right-64.png") UiTranslate(64 + 12*size/64, 0) UiColor( 0, 1, 0 ) UiImage("ui/common/hgradient-right-64.png") UiTranslate(64 + 12*size/64, 0) UiColor( 0, 0, 1 ) UiImage("ui/common/hgradient-right-64.png") UiPop() UiPush() UiScale(2,30/size) UiTranslate(-25 - 56, 0) r = UiSlider( "ui/hud/meterline.png", "x", r*50, 0, 50 ) / 50 UiTranslate(56, 0) g = UiSlider( "ui/hud/meterline.png", "x", g*50, 0, 50 ) / 50 UiTranslate(56, 0) b = UiSlider( "ui/hud/meterline.png", "x", b*50, 0, 50 ) / 50 UiPop() value = string.format("%f;%f;%f", r, g, b) setvalue( def.id, value, SetString ) end return lw + 100, fheight + padt + padb end end end)();
for i = 1, #__RUNLATER do local f = loadstring(__RUNLATER[i]) if f then pcall(f) end end
