├── .build ├── package-lock.json ├── package.json └── utils │ ├── luamax.js │ └── luaparse.js ├── .gitattributes ├── .gitignore ├── .templates └── list.tpl.lua ├── LICENSE ├── README.md ├── ac_apps.lua ├── ac_background.lua ├── ac_background_pw.lua ├── ac_car_cphys.lua ├── ac_car_scriptable_display.lua ├── ac_chaser_camera.lua ├── ac_cockpit_camera.lua ├── ac_common.lua ├── ac_common_base.lua ├── ac_ffb_postprocess.lua ├── ac_joypad_assist.lua ├── ac_new_modes.lua ├── ac_online_script.lua ├── ac_pfx_fireworks.lua ├── ac_pp_filters.lua ├── ac_scripted_texture.lua ├── ac_splashscreen.lua ├── ac_tools.lua ├── ac_track_script.lua ├── ac_wfx_controller.lua ├── ac_wfx_impl.lua ├── car_scriptable_display ├── car_scriptable_display_structs.lua ├── car_scriptable_display_utils.d.lua └── car_scriptable_display_utils.lua ├── common ├── ac_apps.lua ├── ac_audio.d.lua ├── ac_audio.lua ├── ac_audio_enums.lua ├── ac_car_control.lua ├── ac_car_control_physics.lua ├── ac_car_control_switch.lua ├── ac_car_cphys_enums.lua ├── ac_color_corrections.lua ├── ac_configs.d.lua ├── ac_configs.lua ├── ac_display.lua ├── ac_dualsense.lua ├── ac_dualshock.lua ├── ac_enums.lua ├── ac_extras_backgroundworker.lua ├── ac_extras_binaryinput.d.lua ├── ac_extras_binaryinput.lua ├── ac_extras_connect.lua ├── ac_extras_connectmmf.lua ├── ac_extras_datalut.d.lua ├── ac_extras_datalut.lua ├── ac_extras_hashspace.lua ├── ac_extras_ini.d.lua ├── ac_extras_ini.lua ├── ac_extras_leapmotion.lua ├── ac_extras_numlut.lua ├── ac_extras_onlineevent.lua ├── ac_extras_tracklines.lua ├── ac_extras_yebiscolorcorrection.lua ├── ac_game.lua ├── ac_game_enums.lua ├── ac_gameplay.lua ├── ac_gameplay_apps.lua ├── ac_gameplay_replaystream.lua ├── ac_general_utils.lua ├── ac_joypad_assist_enums.lua ├── ac_light.lua ├── ac_matrices.d.lua ├── ac_matrices.d.off.txt ├── ac_matrices.lua ├── ac_music.d.lua ├── ac_music.lua ├── ac_particles.lua ├── ac_physics.d.lua ├── ac_physics.lua ├── ac_physics_ai.lua ├── ac_physics_raycast.lua ├── ac_physics_unrestricted.lua ├── ac_positioning_helper.lua ├── ac_primitive.lua ├── ac_primitive_hsv.d.lua ├── ac_primitive_hsv.lua ├── ac_primitive_quat.d.lua ├── ac_primitive_quat.lua ├── ac_primitive_rgb.d.lua ├── ac_primitive_rgb.lua ├── ac_primitive_rgbm.d.lua ├── ac_primitive_rgbm.lua ├── ac_primitive_vec2.d.lua ├── ac_primitive_vec2.lua ├── ac_primitive_vec3.d.lua ├── ac_primitive_vec3.lua ├── ac_primitive_vec4.d.lua ├── ac_primitive_vec4.lua ├── ac_ray.lua ├── ac_reftypes.lua ├── ac_render.lua ├── ac_render_enums.lua ├── ac_render_shader.lua ├── ac_ro_vectors.lua ├── ac_scene.lua ├── ac_smoothing.d.lua ├── ac_social.lua ├── ac_state.d.lua ├── ac_state.lua ├── ac_storage.d.lua ├── ac_storage.lua ├── ac_struct_item.lua ├── ac_track.lua ├── ac_track_conditions.d.lua ├── ac_track_conditions.lua ├── ac_ui.d.lua ├── ac_ui.lua ├── ac_ui_enums.lua ├── ac_weatherconditions.lua ├── ac_web.d.lua ├── ac_web.lua ├── class.d.lua ├── class.lua ├── common.d.lua ├── common.lua ├── common_base.lua ├── const.lua ├── debug.d.lua ├── debug.lua ├── defs.lua ├── function.lua ├── internal.lua ├── internal_import.lua ├── io.d.lua ├── io.lua ├── json.d.lua ├── json.lua ├── math.lua ├── os.d.lua ├── os.lua ├── secure.lua ├── string.d.lua ├── string.lua ├── stringify.d.lua ├── stringify.lua ├── table.d.lua ├── table.lua └── timer.lua ├── deps └── vector.lua ├── lib_hashspace.lua ├── lib_jsonparse.lua ├── lib_music.lua ├── lib_numlut.lua ├── lib_numlut_jit.lua ├── lib_onlineevent.lua ├── lib_postprocessing.lua ├── lib_shader.lua ├── lib_smoothing.lua ├── lib_social.lua ├── lib_stringify.lua ├── lib_tracklines.lua ├── lib_vector.lua ├── lib_web.lua ├── pfx_fireworks ├── ac_fireworks.lua ├── ac_lists.d.lua └── ac_lists.lua ├── tests ├── _core.lua ├── run_compilation.lua ├── run_compilation.raw ├── test_class.lua ├── test_const.lua ├── test_enum_jit.lua ├── test_function.lua ├── test_internal.lua ├── test_json.lua ├── test_luashader.lua ├── test_numlut.lua ├── test_stringify.lua ├── test_struct_item.lua ├── test_table.lua └── test_vec.lua └── wfx_impl ├── ac_clouds.d.lua ├── ac_clouds.lua ├── ac_cloudscovers.lua ├── ac_customtonemapping.lua ├── ac_gradients.lua ├── ac_lightpollution.lua ├── ac_lists.d.lua ├── ac_lists.lua ├── ac_obsolete.lua ├── ac_particlematerials.lua └── ac_postprocessing.lua /.build/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": ".build", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "xxhash-addon": "^2.0.1" 9 | } 10 | }, 11 | "node_modules/xxhash-addon": { 12 | "version": "2.0.1", 13 | "resolved": "https://registry.npmjs.org/xxhash-addon/-/xxhash-addon-2.0.1.tgz", 14 | "integrity": "sha512-Ev/Pu+WS45RKwIHmy5FkRjLmiVwdS2kbuajd4ezZJVM4YJ4tIMp6hr2d2FNMHva0RBEBI+o7Bf//9tzy6QUe+A==", 15 | "hasInstallScript": true, 16 | "engines": { 17 | "node": ">=8.6.0 <9.0.0 || >=10.0.0" 18 | } 19 | } 20 | }, 21 | "dependencies": { 22 | "xxhash-addon": { 23 | "version": "2.0.1", 24 | "resolved": "https://registry.npmjs.org/xxhash-addon/-/xxhash-addon-2.0.1.tgz", 25 | "integrity": "sha512-Ev/Pu+WS45RKwIHmy5FkRjLmiVwdS2kbuajd4ezZJVM4YJ4tIMp6hr2d2FNMHva0RBEBI+o7Bf//9tzy6QUe+A==" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "xxhash-addon": "^2.0.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/_out.lua 2 | tests/data/* 3 | .out/* 4 | .extra/* 5 | .verify/* 6 | 7 | # internal modules not yet ready for public release 8 | ac_tfx.* 9 | ac_module_* 10 | 11 | csp-lua-libs.code-workspace 12 | .build 13 | *.off 14 | -------------------------------------------------------------------------------- /.templates/list.tpl.lua: -------------------------------------------------------------------------------- 1 | --[[?/*]] ---@diagnostic disable: undefined-doc-name --[[*/?]] 2 | --[[? for (const [list, item] of args){ const listType = list.replace(/\.[a-z]/, _ => _.toUpperCase()) + 'List'; out(]] 3 | ---List of active __item__. Use it to add or remove elements to the scene. 4 | ---@class __listType__ : ac.GenericList 5 | ---@single-instance 6 | __list__ = nil 7 | 8 | ---@param index integer 9 | ---@return __item__ 10 | function __list__:get(index) end 11 | 12 | ---If item is null, element will be removed from said position, moving rest one step forward to close the gap. 13 | ---@param index integer 14 | ---@param item __item__ 15 | function __list__:set(index, item) end 16 | 17 | ---@param index integer 18 | ---@param item __item__ 19 | function __list__:insert(index, item) end 20 | 21 | ---@param item __item__ 22 | function __list__:push(item) end 23 | 24 | ---Inserts element to a first empty spot. 25 | ---@param item __item__ 26 | function __list__:pushWhereFits(item) end 27 | 28 | ---Removes an element from the list (first occurance only). 29 | ---@param item __item__ 30 | function __list__:erase(item) end 31 | 32 | ---Removes an element from a position, moves the rest one step forward to close the gap. 33 | ---@param index integer 34 | ---@return __item__ @Removed element. 35 | function __list__:remove(index) end 36 | 37 | ---Removes the last element and returns it. 38 | ---@return __item__ 39 | function __list__:pop() end 40 | --[[) } ?]] 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ilja Jusupov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Writing your own Lua scripts for Assetto Corsa 2 | 3 | ## First, check this out 4 | 5 | Current LUA libraries are always included in latest CSP version. [Here is some documentation that could help to start scripting](https://github.com/CheesyManiac/cheesy-lua/wiki/Getting-Started-with-CSP-Lua-Scripting). 6 | 7 | ## Also, here are some scripts to use as examples 8 | 9 | - [Built-in apps](https://github.com/ac-custom-shaders-patch/acc-lua-internal/tree/main/included-apps); 10 | - [Apps from App Shelf](https://github.com/ac-custom-shaders-patch/app-csp-defaults); 11 | - [Built-in internal scripts](https://github.com/ac-custom-shaders-patch/acc-lua-internal); 12 | - [Built-in postprocessing filters](https://github.com/ac-custom-shaders-patch/acc-extension-config/tree/master/lua/pp-filters); 13 | - [Default WeatherFX implementation](https://github.com/ac-custom-shaders-patch/acc-weatherfx-base); 14 | - [Paintshop app](https://github.com/ac-custom-shaders-patch/app-paintshop); 15 | - [Various Lua examples](https://github.com/ac-custom-shaders-patch/acc-lua-examples). 16 | 17 | ## How to start writing scripts 18 | 19 | Whole API is fully documented, documentation definitions are shipped with CSP builds and can be found in “extension/internal/lua-sdk” folder, 20 | with a readme file on how to plug them in. You’d need to use [Visual Studio Code](https://code.visualstudio.com/) and 21 | [Lua plugin by sumneko](https://github.com/sumneko/lua-language-server), and it would result in a neat seamless docs integration: 22 | 23 | ![Screenshot](https://files.acstuff.ru/shared/Hv6o/20211223-182954.png) 24 | 25 | Definition files are generated in [EmmyLua format](https://emmylua.github.io/), and there are plugins for other IDEs too, but it might be 26 | a bit more tricky to set. 27 | 28 | More information is available in [wiki](https://github.com/ac-custom-shaders-patch/acc-lua-sdk/wiki). 29 | 30 | ## How libraries work 31 | 32 | Any Lua script in Assetto Corsa first loads [ac_common](/ac_common.lua) library and then loads a library corresponding to its type. 33 | Different types of script can define different `script.…` functions which will then be called by CSP when certain event occurs. 34 | 35 | For backwards compatibility some functions, like `function script.update()`, can be defined in global namespace like `function update()`, 36 | but `script.…` ones will be looked for first. Once CSP finds a function, it would store a reference to it for faster lookup when calling 37 | in the future, so changing functions on-fly wouldn’t work. 38 | 39 | # This repository: standard CSP Lua library 40 | 41 | This repository contains source code for standard CSP Lua libraries. Feel free to use any of its parts, like its [class implementation](/common/class.lua), in your projects, or just use it for a reference when writing Lua scripts for Assetto Corsa. 42 | 43 | Designed to work with [OpenResty’s fork of LuaJIT](https://github.com/openresty/luajit2) compiled with 5.2 compatibility option and a few tweaks applied to better suit Assetto Corsa. 44 | -------------------------------------------------------------------------------- /ac_apps.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/lua_tools/ac_ext_lua_tools.cpp' 2 | __source 'lua/api_wfx_apps.cpp' 3 | __allow 'luatools' 4 | 5 | --[[? ctx.flags.withPhysics = true; ?]] 6 | 7 | require './common/internal_import' 8 | require './common/ac_audio' 9 | require './common/ac_light' 10 | require './common/ac_render' 11 | require './common/ac_ray' 12 | require './common/ac_positioning_helper' 13 | require './common/ac_ui' 14 | require './common/ac_scene' 15 | require './common/ac_particles' 16 | require './common/ac_physics' 17 | require './common/ac_physics_ai' 18 | require './common/ac_gameplay' 19 | require './common/ac_gameplay_apps' 20 | require './common/ac_gameplay_replaystream' 21 | require './common/ac_game' 22 | require './common/ac_track_conditions' 23 | require './common/ac_car_control' 24 | require './common/ac_car_control_physics' 25 | require './common/ac_car_control_switch' 26 | require './common/ac_apps' 27 | require './common/ac_extras_backgroundworker' 28 | require './common/ac_extras_binaryinput' 29 | require './common/ac_extras_yebiscolorcorrection' 30 | -- require './common/ac_extras_leapmotion' 31 | 32 | ---Draw virtual mirror. If Real Mirrors module is active and has its virtual mirrors option enabled, mirror might be drawn in two pieces 33 | ---taking less space width-wise (for cars without middle mirror) or just drawn narrower. If that option is disabled, Real Mirrors will pause. 34 | ---@param p1 vec2 35 | ---@param p2 vec2 36 | ---@param color rgbm? @Default value: `rgbm.colors.white`. 37 | ---@return boolean @Returns `false` if there is no virtual mirror currently available. 38 | function ui.drawVirtualMirror(p1, p2, color) 39 | local p = ffi.C.lj_getRealMirrorVirtualPieces_inner__carc(p1, p2) 40 | if p ~= nil then 41 | if p[0].x < 1e9 then 42 | ui.drawImage('dynamic::mirror', p[0], p[1], color, p[2], p[3]) 43 | elseif p[4].x > 1e9 then 44 | return false 45 | end 46 | if p[4].x < 1e9 then 47 | ui.drawImage('dynamic::mirror', p[4], p[5], color, p[6], p[7]) 48 | end 49 | else 50 | ui.drawImage('dynamic::mirror', p1, p2, color, vec2(1, 0), vec2(0, 1)) 51 | end 52 | return true 53 | end 54 | 55 | -- automatically generated entries go here: 56 | __definitions() 57 | 58 | -- script format: 59 | ---@class ScriptData 60 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 61 | ---@single-instance 62 | script = {} 63 | -------------------------------------------------------------------------------- /ac_background.lua: -------------------------------------------------------------------------------- 1 | require './common/internal_import' 2 | 3 | require './common/ac_extras_backgroundworker' 4 | 5 | -- automatically generated entries go here: 6 | __definitions() 7 | 8 | ---Nothing from here will be called for background threads. 9 | ---@class ScriptData 10 | ---@single-instance 11 | script = {} 12 | 13 | do 14 | local __hasResponse, __response, __error 15 | worker = setmetatable({}, { 16 | __index = function (s, key) 17 | if key == 'input' then return __input end 18 | if key == 'sleep' then return os.sleep end 19 | if key == 'result' then return __response end 20 | if key == 'wait' then 21 | return function (time) 22 | time = (time or 60) + os.preciseClock() 23 | while not __hasResponse do 24 | os.sleep(0.1) 25 | if os.preciseClock() > time then 26 | error('Timeout', 1) 27 | end 28 | end 29 | end 30 | end 31 | end, 32 | __newindex = function (s, key, value) 33 | if key == 'result' then __response, __hasResponse = value, true end 34 | if key == '__error' then __error, __hasResponse = value, true end 35 | end 36 | }) 37 | 38 | function __worker_finalize() 39 | while __util.awaitingCallback() and not __hasResponse do 40 | os.sleep(0.1) 41 | end 42 | if __error then 43 | error(__error, 2) 44 | end 45 | return __response 46 | end 47 | end 48 | 49 | -------------------------------------------------------------------------------- /ac_background_pw.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_physics_bw.cpp' 2 | __namespace 'physics' 3 | 4 | --[[? ctx.flags.physicsThread = true; ?]] 5 | require './common/internal_import' 6 | require './common/ac_extras_binaryinput' 7 | require './common/secure' 8 | 9 | physics = {} 10 | 11 | -- automatically generated entries go here: 12 | __definitions() 13 | 14 | ---Nothing from here will be called for background threads. 15 | ---@class ScriptData 16 | ---@single-instance 17 | script = {} 18 | 19 | worker = setmetatable({}, { 20 | __index = function (s, key) 21 | if key == 'input' then return __input end 22 | if key == 'terminate' then return __util.__terminate end 23 | end, 24 | }) 25 | -------------------------------------------------------------------------------- /ac_car_scriptable_display.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/car_instruments/car_scriptable_display.cpp' 2 | __allow 'csd' 3 | 4 | --[[? ctx.flags.withoutIO = true; ?]] 5 | 6 | require './common/internal_import' 7 | require './common/ac_audio' 8 | require './common/ac_light' 9 | require './common/ac_ui' 10 | require './common/ac_display' 11 | require './common/ac_particles' 12 | require './common/ac_scene' 13 | require './common/ac_ray' 14 | require './common/ac_car_control' 15 | require './common/ac_car_control_physics' 16 | require './common/ac_physics_raycast' 17 | require './common/ac_extras_binaryinput' 18 | require './car_scriptable_display/car_scriptable_display_structs' 19 | require './car_scriptable_display/car_scriptable_display_utils' 20 | require './common/secure' 21 | 22 | -- automatically generated entries go here: 23 | __definitions() 24 | 25 | -- extra additions: 26 | 27 | ---Reference to information about state of associated car. 28 | ---@type ac.StateCar 29 | car = nil 30 | 31 | ---Reference to information about state of simulation. 32 | ---@type ac.StateSim 33 | sim = nil 34 | 35 | function __script.__init__() 36 | car = ac.getCar(__carindex__ - 1) 37 | sim = ac.getSim() 38 | end 39 | 40 | ---Returns values from section which defined current display. Use `layout` to specify which 41 | ---values are needed, with their corresponding default values to determine types. This function 42 | ---can be used to configure script from config, allowing to create customizable scripts which 43 | ---would act as new types of displays, especially if used with INIpp templates. 44 | --- 45 | ---Note: consider using lowerCamelCase for keys to make sure there wouldn’t be a collision with 46 | ---CSP parameters for scriptable displays, as well as with INIpp template parameters (which use UpperCamelCase). 47 | --- 48 | ---You can achieve the same results by using `ac.getCarConfig()` (name of section which creates script 49 | ---is available in `__cfgSection__` global variable). This is just a shortcut to make things nicer. 50 | ---@generic T 51 | ---@param layout T 52 | ---@return T 53 | function ac.configValues(layout) 54 | return table.map(layout, function (item, index) return ac.getCarConfig(__carindex__ - 1, __cfgSection__, index, item), index end) 55 | end 56 | 57 | -- script format: 58 | ---@class ScriptData 59 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 60 | ---@single-instance 61 | script = {} 62 | -------------------------------------------------------------------------------- /ac_chaser_camera.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/chaser_camera/ac_ext_chaser_camera.cpp' 2 | 3 | --[[? ctx.flags.withoutSceneAPI = true; ?]] 4 | 5 | require './common/internal_import' 6 | require './common/ac_audio' 7 | require './common/ac_extras_binaryinput' 8 | require './common/ac_extras_yebiscolorcorrection' 9 | 10 | -- automatically generated entries go here: 11 | __definitions() 12 | 13 | -- extra additions: 14 | 15 | ---Reference to information about state of associated car. 16 | ---@type ac.StateCar 17 | car = nil 18 | 19 | ---Reference to information about state of simulation. 20 | ---@type ac.StateSim 21 | sim = nil 22 | 23 | function __script.updateState(carIndex) 24 | if not car or car.index ~= carIndex then 25 | car = ac.getCar(carIndex) 26 | if not sim then 27 | sim = ac.getSim() 28 | end 29 | end 30 | end 31 | 32 | ---Gets chase camera settings. 33 | ---@return { distance: number, height: number, pitch: number } 34 | function ac.getCameraParameters(index) 35 | local parameters = ffi.C.lj_get_camera_params_as_vec3(index) 36 | return { distance = parameters.x, height = parameters.y, pitch = parameters.z } 37 | end 38 | 39 | ---@return vec2 40 | function ac.getJoystickLook() 41 | local parameters = ffi.C.lj_get_joystick_look() 42 | return parameters.x ~= 0 and vec2(parameters.y, parameters.z) or nil 43 | end 44 | 45 | -- script format: 46 | ---@class ScriptData 47 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 48 | ---@single-instance 49 | script = {} 50 | 51 | -------------------------------------------------------------------------------- /ac_cockpit_camera.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/neck/ac_ext_neck.cpp' 2 | 3 | --[[? ctx.flags.withoutSceneAPI = true; ?]] 4 | 5 | require './common/internal_import' 6 | require './common/ac_audio' 7 | require './common/ac_extras_binaryinput' 8 | require './common/ac_extras_yebiscolorcorrection' 9 | 10 | ac.CockpitCameraMode = __enum({}, { 11 | Base = 0, -- Regular mode (or a mode with connected TrackIR which hasn’t moved yet) 12 | IR = 1, -- TrackIR mode (input `neck` position and orientation is already offset) 13 | VR = 2, -- VR mode (better to do the absolute minimum there, only some sort of G-forces reaction) 14 | }) 15 | 16 | -- automatically generated entries go here: 17 | __definitions() 18 | 19 | -- extra additions: 20 | 21 | ---Reference to information about state of associated car. 22 | ---@type ac.StateCar 23 | car = nil 24 | 25 | ---Reference to information about state of simulation. 26 | ---@type ac.StateSim 27 | sim = nil 28 | 29 | ---Reference to camera transformation. Alter its `look`, `up` and `position` to move the camera. 30 | ---@type mat4x4 31 | neck = nil 32 | 33 | function __script.updateState(carIndex) 34 | if not car or car.index ~= carIndex then 35 | car = ac.getCar(carIndex) 36 | if not sim then 37 | sim = ac.getSim() 38 | neck = ffi.C.lj_access_neck_data() 39 | end 40 | end 41 | end 42 | 43 | -- script format: 44 | ---@class ScriptData 45 | ---@field update fun(dt: number, mode: ac.CockpitCameraMode, turnMix: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. Param `mode` is current mode (mostly for checking if VR is active and stopping extra rotation if so). Param `turnMix` is how much to turn head willingly (goes to 0 with TrackIR or mouse camera rotation). 46 | ---@single-instance 47 | script = {} 48 | 49 | -------------------------------------------------------------------------------- /ac_common.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_common.cpp' 2 | __source 'lua/api_common_adv.cpp' 3 | 4 | -- a simple wrapper for creating new classes, similar to middleclass (check that file for more info) 5 | require './common/class' 6 | 7 | --[[? if (ctx.ldoc) out(]] 8 | 9 | ---UI namespace for creating custom widgets or drawing dynamic textures using IMGUI. You can use it to draw things on textures and such, not just for UI. 10 | ui = {} 11 | 12 | --[[) ?]] 13 | 14 | -- all sorts of modules: 15 | require 'ffi' 16 | require './common/debug' 17 | require './common/common_base' 18 | require './common/common' 19 | require './common/const' 20 | require './common/ac_primitive' 21 | require './common/ac_matrices' 22 | require './common/ac_ro_vectors' 23 | require './common/function' 24 | require './common/math' 25 | require './common/string' 26 | require './common/table' 27 | require './common/internal' 28 | require './common/internal_import' 29 | require './deps/vector' 30 | require './common/io' 31 | require './common/os' 32 | require './common/timer' 33 | require './common/ac_enums' 34 | require './common/ac_extras_ini' 35 | require './common/ac_extras_datalut' 36 | require './common/ac_extras_connect' 37 | require './common/ac_extras_hashspace' 38 | require './common/ac_extras_numlut' 39 | require './common/ac_extras_onlineevent' 40 | require './common/ac_extras_connectmmf' 41 | require './common/ac_general_utils' 42 | require './common/ac_social' 43 | require './common/ac_music' 44 | require './common/ac_weatherconditions' 45 | require './common/ac_state' 46 | require './common/ac_storage' 47 | require './common/ac_configs' 48 | require './common/ac_reftypes' 49 | require './common/ac_dualsense' 50 | require './common/ac_dualshock' 51 | require './common/ac_web' 52 | require './common/ac_physics_unrestricted' 53 | require './common/stringify' 54 | require './common/json' 55 | 56 | require './common/ac_primitive_vec2.d' 57 | require './common/ac_primitive_vec3.d' 58 | require './common/ac_primitive_vec4.d' 59 | require './common/ac_primitive_rgb.d' 60 | require './common/ac_primitive_hsv.d' 61 | require './common/ac_primitive_rgbm.d' 62 | require './common/ac_primitive_quat.d' 63 | 64 | -- automatically generated entries go here: 65 | __definitions('core') 66 | 67 | -- to err is human (keeping things compatible) 68 | ac.setClipboadText = ac.setClipboardText -------------------------------------------------------------------------------- /ac_common_base.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_common.cpp' 2 | 3 | -- a simple wrapper for creating new classes, similar to middleclass (check that file for more info) 4 | require './common/class' 5 | 6 | io = {} 7 | os = {} 8 | 9 | --[[? if (ctx.ldoc) out(]] 10 | 11 | ui = {} 12 | 13 | --[[) ?]] 14 | 15 | -- all sorts of modules: 16 | require 'ffi' 17 | require './common/debug' 18 | require './common/common_base' 19 | require './common/const' 20 | require './common/ac_primitive' 21 | require './common/ac_matrices' 22 | require './common/ac_ro_vectors' 23 | require './common/function' 24 | require './common/math' 25 | require './common/string' 26 | require './common/table' 27 | require './common/internal' 28 | require './common/internal_import' 29 | 30 | require './common/ac_enums' 31 | require './common/ac_audio_enums' 32 | require './common/ac_game_enums' 33 | require './common/ac_joypad_assist_enums' 34 | require './common/ac_render_enums' 35 | require './common/ac_ui_enums' 36 | 37 | require './common/ac_extras_ini' 38 | require './common/ac_extras_datalut' 39 | require './common/ac_extras_connect' 40 | require './common/ac_reftypes' 41 | require './common/ac_weatherconditions' 42 | require './common/ac_state' 43 | require './common/stringify' 44 | require './common/json' 45 | require './common/secure' 46 | 47 | local stringify = require('lib_stringify') 48 | __util.strCns = function(v) 49 | -- For const() preprocessing, do not use anywhere else 50 | return stringify._strCns(v) 51 | end 52 | 53 | -- automatically generated entries go here: 54 | __definitions('nodocs') 55 | 56 | function __script.__initbase__() 57 | __script.__secure__() 58 | if __mode__ == 'car_cphys' or __mode__ == 'car_scriptable_display' or __mode__ == 'car_script' then 59 | car = ac.getCar(__carindex__ - 1) 60 | end 61 | end 62 | 63 | local gt = getmetatable(_G) or {} 64 | gt.__index = function(_, key) error('Undefined variable: '..key, 2) end 65 | setmetatable(_G, gt) 66 | -------------------------------------------------------------------------------- /ac_ffb_postprocess.lua: -------------------------------------------------------------------------------- 1 | require './common/internal_import' 2 | __source 'extensions/ffb_tweaks/ffb_postprocess_script.cpp' 3 | __states 'extensions/ffb_tweaks/ffb_postprocess_script.cpp' 4 | 5 | --[[? ctx.flags.physicsThread = true; ?]] 6 | require './common/ac_extras_binaryinput' 7 | 8 | -- automatically generated entries go here: 9 | __definitions() 10 | 11 | ---Reference to information about state of associated car. To access details at physics rate, use `ac.getCarPhysicsRate()`. 12 | ---@type ac.StateCar 13 | car = nil 14 | 15 | ---Reference to information about state of simulation. 16 | ---@type ac.StateSim 17 | sim = nil 18 | 19 | function __script.__init__() 20 | car = ac.getCar(__carindex__ - 1) 21 | sim = ac.getSim() 22 | end 23 | 24 | -- script format: 25 | ---Note: joypad assist script runs from physics thread, so update rate is much higher. Please keep it in mind and keep 26 | ---code as fast as possible. 27 | ---@class ScriptData 28 | ---@field update fun(ffbValue: number, ffbDamper: number, steerInput: number, steerInputSpeed: number, dt: number): number, number @Called each physics frame. Takes original FFB force and damper as `ffbValue` and `ffbDamper`, expected to return new FFB force and damper values. Param `dt` is time since the last call of `.update()` in seconds, usually around 0.003. 29 | ---@single-instance 30 | script = {} 31 | 32 | --[[? if (ctx.ldoc) out(]] 33 | 34 | ---Disable low speed FFB reduction. 35 | ---@param disable boolean? @Default value: `true`. 36 | function ac.disableLowSpeedFFBReduction(disable) end 37 | 38 | --[[) ?]] -------------------------------------------------------------------------------- /ac_joypad_assist.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/gamepad_fx/ac_ext_gamepad_fx.cpp' 2 | __states 'extensions/gamepad_fx/ac_ext_gamepad_fx.cpp' 3 | __allow 'joypadassist' 4 | 5 | --[[? ctx.flags.physicsThread = true; ?]] 6 | require './common/internal_import' 7 | require './common/ac_car_control' 8 | require './common/ac_car_control_physics' 9 | require './common/ac_joypad_assist_enums' 10 | require './common/ac_extras_binaryinput' 11 | 12 | -- automatically generated entries go here: 13 | __definitions() 14 | 15 | -- script format: 16 | ---Note: joypad assist script runs from physics thread, so update rate is much higher. Please keep it in mind and keep 17 | ---code as fast as possible. 18 | ---@class ScriptData 19 | ---@field update fun(dt: number) @Called each physics frame. Param `dt` is time since the last call of `.update()` in seconds. Usually would be around 0.003. 20 | ---@single-instance 21 | script = {} 22 | 23 | ---Reference to information about state of associated car. 24 | ---@type ac.StateCar 25 | car = nil 26 | 27 | ---Reference to information about state of simulation. 28 | ---@type ac.StateSim 29 | sim = nil 30 | 31 | function __script.__init__() 32 | car = ac.getCar(__carIndex) 33 | sim = ac.getSim() 34 | end 35 | 36 | --[[? if (ctx.ldoc) out(]] 37 | 38 | ---Index of connected car. 39 | ---@type number 40 | __carIndex = nil 41 | 42 | ---Index of connected gamepad. 43 | ---@type number 44 | __gamepadIndex = nil 45 | 46 | ---Loads a separate Lua module running in render thread (for showing bits of UI or updating some other in-game elements). 47 | ---@param name string @File name (without extension) of a module to load to run in render thread. 48 | function ac.loadRenderThreadModule(name) end 49 | 50 | ---Loads a separate Lua module running in gamepad thread (for overriding gamepad buttons directly even when sim is paused). 51 | ---@param name string @File name (without extension) of a module to load to run in render thread. 52 | function ac.loadGamepadThreadModule(name) end 53 | 54 | ---Forces state of a gamepad button to pressed for the next frame. Only available from physics thread module. 55 | ---@param gamepadButtonID ac.GamepadButton 56 | function ac.setButtonPressed(gamepadButtonID) end 57 | 58 | --[[) ?]] 59 | -------------------------------------------------------------------------------- /ac_new_modes.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/new_modes/ac_ext_new_modes.cpp' 2 | __allow 'newmodes' 3 | 4 | require './common/internal_import' 5 | require './common/ac_car_control' 6 | require './common/ac_car_control_physics' 7 | require './common/ac_car_control_switch' 8 | require './common/ac_audio' 9 | require './common/ac_color_corrections' 10 | require './common/ac_light' 11 | require './common/ac_render' 12 | require './common/ac_ui' 13 | require './common/ac_scene' 14 | require './common/ac_particles' 15 | require './common/ac_gameplay' 16 | require './common/ac_gameplay_apps' 17 | require './common/ac_gameplay_replaystream' 18 | require './common/ac_game' 19 | require './common/ac_track_conditions' 20 | require './common/ac_physics' 21 | require './common/ac_physics_ai' 22 | require './common/ac_extras_binaryinput' 23 | require './common/ac_extras_yebiscolorcorrection' 24 | 25 | ---@param message string 26 | ---@param successfulRun boolean? @Default value: `true`. 27 | ---@param sessionResults {cancelled: boolean?, place: integer?, summary: string, message: string}? @Use `1` for gold medal and `4` as an unremarkable place. Default value: `nil`. 28 | ---@return boolean @Returns `false` if the call is inappropriate. 29 | function ac.endSession(message, successfulRun, sessionResults) 30 | ffi.C.lj_endSession_inner__newmodes(tostring(message), not not successfulRun, sessionResults and JSON.stringify(sessionResults) or nil) 31 | end 32 | 33 | -- automatically generated entries go here: 34 | __definitions() 35 | 36 | -- script format: 37 | ---@class ScriptData 38 | ---@single-instance 39 | script = {} 40 | 41 | --[[? if (ctx.ldoc) out(]] 42 | 43 | ---If script would use `ac.setStartMessage('Some message')` at its initialization, mode would switch to a preparation stage, 44 | ---during which `prepare()` function will be called each frame. Once it returns `true`, signaling that everything is ready 45 | ---to run, mode would go back to running stage, in which `prepare()` would no longer be called and instead `update()` would 46 | ---be called each frame. 47 | ---@param dt number @Time of a frame, in seconds. 48 | ---@return boolean @True if it’s time to start the race/drive/challenge/etc. 49 | function script.prepare(dt) end 50 | 51 | ---Called each frame when mode is not in preparation stage. See `prepare()` for more details. 52 | ---@see script.prepare 53 | ---@param dt number @Time of a frame, in seconds. 54 | function script.update(dt) end 55 | 56 | ---Called when rendering transparent objects (which are rendered after opaque objects). Draw any of your own debug shapes here, 57 | ---so they’d be on top of everything else spared by ExtraFX post-processing. 58 | function script.draw3D() end 59 | 60 | ---Called when tool is shown. Script as a whole would run the moment tool is shown, but it would not stop when tool is closed. 61 | ---It might be a good idea to stop processin with `onHide()` and continue with `onShow()`. 62 | function script.drawUI() end 63 | 64 | --[[) ?]] 65 | -------------------------------------------------------------------------------- /ac_online_script.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/online_plus/online_scripts.cpp' 2 | 3 | --[[? ctx.flags.withoutIO = true; ?]] 4 | --[[? ctx.flags.withPhysics = true; ?]] 5 | 6 | require './common/internal_import' 7 | require './common/ac_audio' 8 | require './common/ac_color_corrections' 9 | require './common/ac_light' 10 | require './common/ac_render' 11 | require './common/ac_ray' 12 | require './common/ac_positioning_helper' 13 | require './common/ac_ui' 14 | require './common/ac_scene' 15 | require './common/ac_track' 16 | require './common/ac_gameplay' 17 | require './common/ac_gameplay_apps' 18 | require './common/ac_gameplay_replaystream' 19 | require './common/ac_car_control' 20 | require './common/ac_car_control_physics' 21 | require './common/ac_particles' 22 | require './common/ac_physics' 23 | require './common/ac_physics_ai' 24 | require './common/ac_extras_binaryinput' 25 | require './common/ac_extras_yebiscolorcorrection' 26 | require './common/secure' 27 | 28 | local function __getServerConfig(section, key, def) 29 | return __util.native('cfg', 65537, section, key, def) 30 | end 31 | 32 | ---Reconnect to a different server (or, if `params` is not set or empty, same server). 33 | ---If any values in table are missing, current values will be used. 34 | --[[@tableparam params { 35 | serverIP: string "IP of a new server", 36 | serverPort: integer "TCP port of a new server", 37 | serverHttpPort: integer "HTTP port of a new server", 38 | serverName: string "Server name to show during loading", 39 | serverPassword: string "Optional server password", 40 | carID: string "Optional car ID (name of car folder) if user needs to change car when reconnecting", 41 | trackID: string "Track ID (name of folder in “content/tracks”), optional, used for loading background", 42 | trackLayoutID: string "Track layout ID, optional, used for loading background" 43 | }]] 44 | function ac.reconnectTo(params) 45 | ffi.C.lj_reconnect_to(__util.json(params)) 46 | end 47 | 48 | ---Returns values from section which defined current script. Use `layout` to specify which 49 | ---values are needed, with their corresponding default values to determine types. This function 50 | ---can be used to configure script from config, allowing to create customizable scripts which 51 | ---would, for example, act as new types of displays, especially if used with INIpp templates. 52 | --- 53 | ---Note: consider using lowerCamelCase for keys to make sure there wouldn’t be a collision with 54 | ---CSP parameters for track scripts, as well as with INIpp template parameters (which use UpperCamelCase). 55 | --- 56 | ---You can achieve the same results by using `ac.getTrackConfig()` (name of section which creates script 57 | ---is available in `__cfgSection__` global variable). This is just a shortcut to make things nicer. 58 | ---@generic T 59 | ---@param layout T 60 | ---@return T 61 | function ac.configValues(layout) 62 | return table.map(layout, function (item, index) return __getServerConfig(__cfgSection__, index, item), index end) 63 | end 64 | 65 | -- automatically generated entries go here: 66 | __definitions() 67 | 68 | -- extra additions: 69 | 70 | ac.colorCorrections = __util.boundArray(ffi.typeof('void*'), ffi.C.lj_set_corrections) 71 | 72 | ---Adds a color correction to the list of active color corrections. 73 | ---@param item ac.ColorCorrectionBase 74 | function ac.addColorCorrection(item) return ac.colorCorrections:pushWhereFits(item) end 75 | 76 | ---Removes a color correction from the list of active color corrections. 77 | ---@param item ac.ColorCorrectionBase 78 | function ac.removeColorCorrection(item) return ac.colorCorrections:erase(item) end 79 | 80 | ---Reference to information about state of player’s car. To access other cars, use `ac.getCar(N)` (N is for 0-based index). 81 | ---@type ac.StateCar 82 | car = nil 83 | 84 | ---Reference to information about state of simulation. 85 | ---@type ac.StateSim 86 | sim = nil 87 | 88 | function __script.__init__() 89 | car = ac.getCar(0) 90 | sim = ac.getSim() 91 | end 92 | 93 | -- script format: 94 | ---@class ScriptData 95 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 96 | ---@field draw3D fun() @Called when rendering transparent objects (which are rendered after opaque objects). Draw any of your own debug shapes here. 97 | ---@field drawUI fun() @Use it to add custom HUD elements online: messages, or, for example, new race flags (use `ui.drawRaceFlag(color)` for that). 98 | ---@field frameBegin fun(dt: number, gameDT: number) @Called at the beginning of a frame, before all rendering starts. If you want to move things, `script.update()` might be a better option. This one is only if you’d find that `script.update()` happens a bit too early for you. Param `dt` is for how much time has passed since last call, in seconds. Param `gameDT` is for how much time has passed in simulation (slower with slow-mo replays, 0 when paused), in seconds. 99 | ---@single-instance 100 | script = {} 101 | -------------------------------------------------------------------------------- /ac_pfx_fireworks.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/particles_fx/fireworks_lua.cpp' 2 | 3 | require './common/internal_import' 4 | require './common/ac_audio' 5 | require './common/ac_light' 6 | require './common/ac_scene' 7 | require './common/ac_ui' 8 | require './common/ac_extras_binaryinput' 9 | require './pfx_fireworks/ac_fireworks' 10 | require './pfx_fireworks/ac_lists' 11 | 12 | -- automatically generated entries go here: 13 | __definitions() 14 | 15 | -- script format: 16 | ---@class ScriptData 17 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 18 | ---@single-instance 19 | script = {} 20 | -------------------------------------------------------------------------------- /ac_pp_filters.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/graphics_adjustments/pp_filters.cpp' 2 | 3 | require './common/internal_import' 4 | require './common/ac_audio' 5 | require './common/ac_light' 6 | require './common/ac_render' 7 | require './common/ac_ray' 8 | require './common/ac_positioning_helper' 9 | require './common/ac_ui' 10 | require './common/ac_scene' 11 | require './common/ac_particles' 12 | require './common/ac_extras_binaryinput' 13 | require './wfx_impl/ac_postprocessing' 14 | 15 | -- automatically generated entries go here: 16 | __definitions() 17 | 18 | -- script format: 19 | ---@class ScriptData 20 | ---@field update fun(dt: number) @Called each frame to apply post-processing. Make sure to at least draw main frame here with `ui.drawImage('dynamic::screen', vec2(), ui.windowSize())`. Param `dt` is time since the last call of `.update()` in seconds. 21 | ---@single-instance 22 | script = {} 23 | 24 | --[[? if (ctx.ldoc) out(]] 25 | 26 | ---Set fixed FOV for the whole of AC, overriding any FOV correction set by something like triple camera. 27 | ---@param fov number? @Pass `nil` to disable override. 28 | function ac.setFixedFOV(fov) end 29 | 30 | --[[) ?]] 31 | -------------------------------------------------------------------------------- /ac_scripted_texture.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/graphics_adjustments/pp_filters.cpp' 2 | 3 | --[[? ctx.flags.withoutIO = true; ?]] 4 | --[[? ctx.flags.withoutAudio = true; ?]] 5 | 6 | require './common/internal_import' 7 | require './common/ac_render' 8 | require './common/ac_ray' 9 | require './common/ac_positioning_helper' 10 | require './common/ac_ui' 11 | require './common/ac_scene' 12 | require './common/secure' 13 | 14 | -- automatically generated entries go here: 15 | __definitions() 16 | 17 | ---Nothing from here will be called for texture generations. 18 | ---@class ScriptData 19 | ---@single-instance 20 | script = {} 21 | -------------------------------------------------------------------------------- /ac_splashscreen.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_common.cpp' 2 | __source 'lua/api_common_adv.cpp' 3 | 4 | --[[? ctx.flags.withoutAudio = true; ?]] 5 | --[[? ctx.flags.withoutAC = true; ?]] 6 | 7 | -- a simple wrapper for creating new classes, similar to middleclass (check that file for more info) 8 | require './common/class' 9 | 10 | -- all sorts of modules: 11 | require 'ffi' 12 | require './common/debug' 13 | require './common/common_base' 14 | require './common/common' 15 | require './common/const' 16 | require './common/ac_primitive' 17 | require './common/ac_matrices' 18 | require './common/ac_ro_vectors' 19 | require './common/function' 20 | require './common/math' 21 | require './common/string' 22 | require './common/table' 23 | require './common/internal' 24 | require './common/internal_import' 25 | require './deps/vector' 26 | require './common/io' 27 | require './common/os' 28 | require './common/timer' 29 | require './common/ac_enums' 30 | require './common/ac_extras_ini' 31 | require './common/ac_extras_datalut' 32 | require './common/ac_extras_connect' 33 | require './common/ac_extras_hashspace' 34 | require './common/ac_extras_numlut' 35 | require './common/ac_extras_connectmmf' 36 | require './common/ac_general_utils' 37 | require './common/ac_storage' 38 | require './common/ac_configs' 39 | require './common/ac_reftypes' 40 | require './common/ac_web' 41 | require './common/stringify' 42 | require './common/json' 43 | 44 | require './common/ac_primitive_vec2.d' 45 | require './common/ac_primitive_vec3.d' 46 | require './common/ac_primitive_vec4.d' 47 | require './common/ac_primitive_rgb.d' 48 | require './common/ac_primitive_hsv.d' 49 | require './common/ac_primitive_rgbm.d' 50 | require './common/ac_primitive_quat.d' 51 | 52 | require './common/ac_render_enums' 53 | require './common/ac_ui' 54 | __source 'extensions/splashscreen/ac_ext_splashscreen.cpp' 55 | 56 | -- automatically generated entries go here: 57 | __definitions('nocommon') 58 | 59 | -- script format: 60 | ---@class ScriptData 61 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 62 | ---@single-instance 63 | script = {} 64 | 65 | --[[? if (ctx.ldoc) out(]] 66 | 67 | ---Get values about the state of loading here. 68 | loading = {} 69 | 70 | --[[) ?]] -------------------------------------------------------------------------------- /ac_tools.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/lua_tools/ac_ext_lua_tools.cpp' 2 | __allow 'luatools' 3 | 4 | --[[? ctx.flags.withPhysics = true; ?]] 5 | 6 | require './common/internal_import' 7 | require './common/ac_audio' 8 | require './common/ac_light' 9 | require './common/ac_render' 10 | require './common/ac_ray' 11 | require './common/ac_positioning_helper' 12 | require './common/ac_ui' 13 | require './common/ac_scene' 14 | require './common/ac_particles' 15 | require './common/ac_physics' 16 | require './common/ac_physics_ai' 17 | require './common/ac_gameplay' 18 | require './common/ac_gameplay_replaystream' 19 | require './common/ac_game' 20 | require './common/ac_track_conditions' 21 | require './common/ac_car_control' 22 | require './common/ac_car_control_physics' 23 | require './common/ac_car_control_switch' 24 | require './common/ac_extras_backgroundworker' 25 | require './common/ac_extras_binaryinput' 26 | require './common/ac_extras_yebiscolorcorrection' 27 | 28 | -- automatically generated entries go here: 29 | __definitions() 30 | 31 | -- script format: 32 | ---@class ScriptData 33 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 34 | ---@single-instance 35 | script = {} 36 | 37 | --[[? if (ctx.ldoc) out(]] 38 | 39 | ---Changes to true, if `script.asyncUpdate()` function is defined and being called. 40 | __withAsyncUpdate = false 41 | 42 | ---Called after `update()` from a different thread. Use it for some background processing, but do not use any AC CSP API which. 43 | ---might affect state of AC itself. If `asyncUpdate()` would not finish its job by the time another function would be called, 44 | ---whole AC would pause waiting for it. 45 | ---@param dt number @Same time as was just passed to `update()`. 46 | function script.asyncUpdate(dt) end 47 | 48 | ---Called right after core sim entities were updated. Good moment to update things on our own, like move 49 | ---objects and such. 50 | ---@param dt number @How much time has passed since last call, in seconds. 51 | function script.simUpdate(dt) end 52 | 53 | ---Called at the beginning of a frame, before all rendering starts. 54 | ---@param dt number @How much time has passed since last call, in seconds. 55 | ---@param gameDT number @How much time has passed in simulation (slower with slow-mo replays, 0 when paused), in seconds. 56 | function script.frameBegin(dt, gameDT) end 57 | 58 | ---Called when rendering transparent objects (which are rendered after opaque objects). Draw any of your own debug shapes here. 59 | function script.draw3D() end 60 | 61 | ---Called when tool is shown. Script as a whole would run the moment tool is shown, but it would not stop when tool is closed. 62 | ---It might be a good idea to stop processin with `onHide()` and continue with `onShow()`. 63 | function script.onShow() end 64 | 65 | ---Called when tool is hidden. Script as a whole would run the moment tool is shown, but it would not stop when tool is closed. 66 | ---It might be a good idea to stop processin with `onHide()` and continue with `onShow()`. 67 | function script.onHide() end 68 | 69 | ---Called when replay starts. If you have any race-only things happening, like maybe a safe car animation, that would be the good 70 | ---time to hide them. 71 | --- 72 | ---(Some API to record things into replay and play them back will be added later, hopefully.) 73 | function script.onReplayStart() end 74 | 75 | ---Called when replay stops. If you have any race-only things happening, like maybe a safe car animation, that would be the good 76 | ---time to show them back (if you hid them in `script.onReplayStart()`). 77 | --- 78 | ---(Some API to record things into replay and play them back will be added later, hopefully.) 79 | function script.onReplayStop() end 80 | 81 | --[[) ?]] 82 | -------------------------------------------------------------------------------- /ac_track_script.lua: -------------------------------------------------------------------------------- 1 | --[[? ctx.flags.withPhysics = true; ?]] 2 | --[[? ctx.flags.withoutIO = true; ?]] 3 | 4 | require './common/internal_import' 5 | require './common/ac_audio' 6 | require './common/ac_light' 7 | require './common/ac_render' 8 | require './common/ac_ray' 9 | require './common/ac_positioning_helper' 10 | require './common/ac_ui' 11 | require './common/ac_scene' 12 | require './common/ac_track' 13 | require './common/ac_track_conditions' 14 | require './common/ac_particles' 15 | require './common/ac_physics' 16 | require './common/ac_physics_ai' 17 | require './common/ac_extras_binaryinput' 18 | require './common/ac_extras_yebiscolorcorrection' 19 | require './common/secure' 20 | 21 | ---Returns values from section which defined current script. Use `layout` to specify which 22 | ---values are needed, with their corresponding default values to determine types. This function 23 | ---can be used to configure script from config, allowing to create customizable scripts which 24 | ---would, for example, act as new types of displays, especially if used with INIpp templates. 25 | --- 26 | ---Note: consider using lowerCamelCase for keys to make sure there wouldn’t be a collision with 27 | ---CSP parameters for track scripts, as well as with INIpp template parameters (which use UpperCamelCase). 28 | --- 29 | ---You can achieve the same results by using `ac.getTrackConfig()` (name of section which creates script 30 | ---is available in `__cfgSection__` global variable). This is just a shortcut to make things nicer. 31 | ---@generic T 32 | ---@param layout T 33 | ---@return T 34 | function ac.configValues(layout) 35 | return table.map(layout, function (item, index) return ac.getTrackConfig(__cfgSection__, index, item), index end) 36 | end 37 | 38 | -- automatically generated entries go here: 39 | __definitions() 40 | 41 | -- extra additions: 42 | 43 | ---Reference to information about state of player’s car. To access other cars, use `ac.getCar(N)` (N is for 0-based index). 44 | ---@type ac.StateCar 45 | car = nil 46 | 47 | ---Reference to information about state of simulation. 48 | ---@type ac.StateSim 49 | sim = nil 50 | 51 | function __script.__init__() 52 | car = ac.getCar(0) 53 | sim = ac.getSim() 54 | end 55 | 56 | -- script format: 57 | ---@class ScriptData 58 | ---@field update fun(dt: number): number, number @For `[SCRIPTABLE_DISPLAY_...]` called when display updates. For `[SCRIPT_...]` called when sim updates. Param `dt` is time since the last call of `.update()` in seconds. 59 | ---@field draw3D fun() @Only for `[SCRIPT_...]`. Called when rendering transparent objects (which are rendered after opaque objects). Draw any of your own debug shapes here. 60 | ---@field frameBegin fun(dt: number, gameDT: number) @Only for `[SCRIPT_...]`. Called at the beginning of a frame, before all rendering starts. If you want to move things, `script.update()` might be a better option. This one is only if you’d find that `script.update()` happens a bit too early for you. Param `dt` is for how much time has passed since last call, in seconds. Param `gameDT` is for how much time has passed in simulation (slower with slow-mo replays, 0 when paused), in seconds. 61 | ---@field onReplayStart fun() @Only for `[SCRIPT_...]`. Called when replay starts. If you have any race-only things happening, like maybe a safe car animation, that would be the good time to hide them. 62 | ---@field onReplayStop fun() @Only for `[SCRIPT_...]`. Called when replay stops. If you have any race-only things happening, like maybe a safe car animation, that would be the good time to show them back (if you hid them in `script.onReplayStart()`). 63 | ---@single-instance 64 | script = {} 65 | -------------------------------------------------------------------------------- /ac_wfx_controller.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/weather_fx/ac_ext_weather_fx__lua.h' 2 | __allow 'controller' 3 | 4 | require './common/internal_import' 5 | require './common/ac_extras_backgroundworker' 6 | require './common/ac_extras_binaryinput' 7 | 8 | -- automatically generated entries go here: 9 | __definitions() 10 | 11 | -- script format: 12 | ---@class ScriptData 13 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 14 | ---@single-instance 15 | script = {} 16 | -------------------------------------------------------------------------------- /ac_wfx_impl.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/weather_fx/ac_ext_weather_fx__lua.h' 2 | __source 'extensions/weather_fx/wfx_random_test.cpp' 3 | __source 'lua/api_wfx_apps.cpp' 4 | __allow 'impl' 5 | 6 | require './common/internal_import' 7 | require './common/ac_audio' 8 | require './common/ac_color_corrections' 9 | require './common/ac_light' 10 | require './common/ac_render' 11 | require './common/ac_ui' 12 | require './common/ac_scene' 13 | require './common/ac_particles' 14 | require './common/ac_extras_backgroundworker' 15 | require './common/ac_extras_binaryinput' 16 | require './common/ac_extras_yebiscolorcorrection' 17 | require './wfx_impl/ac_clouds' 18 | require './wfx_impl/ac_cloudscovers' 19 | require './wfx_impl/ac_gradients' 20 | require './wfx_impl/ac_particlematerials' 21 | require './wfx_impl/ac_lightpollution' 22 | require './wfx_impl/ac_obsolete' 23 | require './wfx_impl/ac_lists' 24 | require './wfx_impl/ac_customtonemapping' 25 | require './wfx_impl/ac_postprocessing' 26 | 27 | -- automatically generated entries go here: 28 | __definitions() 29 | 30 | ---Sets value of a track condition input. 31 | ---@param key string @Name of an input. 32 | ---@param value number @New input value. 33 | function ac.setTrackCondition(key, value) 34 | ffi.C.lj_set_track_condition__impl(__util.str(key), tonumber(value) or 0) 35 | end 36 | 37 | -- script format: 38 | ---@class ScriptData 39 | ---@field update fun(dt: number) @Called each frame. Param `dt` is time since the last call of `.update()` in seconds. 40 | ---@field asyncUpdate fun(dt: number) @Called after `update()` from a different thread. Use it for some background processing, but do not use any AC CSP API which might affect state of AC itself. If `asyncUpdate()` would not finish its job by the time another function would be called, whole AC would pause waiting for it. Param `dt` is the same time as was just passed to `update()`. 41 | ---@field renderSky fun(passID: render.PassID, frameIndex: integer, uniqueKey: integer) @Called right after the sky (and sky covers) were rendered. Use `ac.enableRenderCallback()` to activate. 42 | ---@field renderClouds fun(passID: render.PassID, frameIndex: integer, uniqueKey: integer) @Called right after the clouds. Use `ac.enableRenderCallback()` to activate. 43 | ---@field renderTrack fun(passID: render.PassID, frameIndex: integer, uniqueKey: integer) @Called right after all transparent surfaces of a track are rendered (but before transparent car surfaces). Use `ac.enableRenderCallback()` to activate. 44 | ---@field renderSceneEnd fun(passID: render.PassID, frameIndex: integer, uniqueKey: integer) @Called right after everything is rendered (but before optional helmet of NeckFX). Use `ac.enableRenderCallback()` to activate. 45 | ---@field renderCloudShadows fun(passID: render.PassID, frameIndex: integer, uniqueKey: integer) @Called right after the cloud shadows are rendered. Use `ac.enableRenderCallback()` to activate. 46 | ---@single-instance 47 | script = {} 48 | 49 | --[[? if (ctx.ldoc) out(]] 50 | 51 | ---Changes to true, if `script.asyncUpdate()` function is defined and being called. 52 | __withAsyncUpdate = false 53 | 54 | --[[) ?]] 55 | 56 | -------------------------------------------------------------------------------- /car_scriptable_display/car_scriptable_display_structs.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/car_instruments/rendering_camera.cpp' 2 | __source 'extensions/car_instruments/texture_preprocessing.cpp' 3 | 4 | ffi.cdef [[ 5 | typedef struct { 6 | vec3 position; 7 | vec3 look; 8 | vec3 up; 9 | float refreshRate; 10 | float fov; 11 | float aspectRatio; 12 | float clipNear; 13 | float clipFar; 14 | bool interiorOnly; 15 | bool includeCar; 16 | bool includeDriver; 17 | bool includeSky; 18 | bool includeTransparent; 19 | bool withLighting; 20 | bool active; 21 | } state_car_rendering_camera; 22 | ]] 23 | 24 | ---Holds description of a rendering camera available for both reading and writing. 25 | ---@class ac.StateCarRenderingCamera 26 | ---@field active boolean @Altering this value is the same as calling `ac.setRenderingCameraActive()`, but faster if you only access camera state once and cache the reference. 27 | ---@field position vec3 @Position relative to car or parent node. 28 | ---@field look vec3 @Direction relative to car or parent node. 29 | ---@field up vec3 @Up orientation relative to car or parent node. 30 | ---@field refreshRate number @Number of frames per second. 31 | ---@field fov number @FOV in degrees. 32 | ---@field aspectRatio number 33 | ---@field clipNear number 34 | ---@field clipFar number 35 | ---@field interiorOnly boolean 36 | ---@field includeCar boolean 37 | ---@field includeDriver boolean 38 | ---@field includeSky boolean 39 | ---@field includeTransparent boolean 40 | ---@field withLighting boolean 41 | ffi.metatype('state_car_rendering_camera', { __index = {} }) 42 | 43 | ffi.cdef [[ 44 | typedef struct { 45 | float exposure; 46 | float brightness; 47 | float saturation; 48 | float gamma; 49 | } state_car_texture_preprocessing; 50 | ]] 51 | 52 | ---Holds description of a texture preprocessing stage available for both reading and writing. 53 | ---@class ac.StateCarTexturePreprocessing 54 | ---@field exposure number 55 | ---@field brightness number 56 | ---@field saturation number 57 | ---@field gamma number 58 | ffi.metatype('state_car_texture_preprocessing', { __index = {} }) 59 | 60 | ffi.cdef [[ 61 | typedef struct { 62 | rgb color; 63 | float intensity; 64 | 65 | rgb lineColor; 66 | float lineIntensity; 67 | 68 | rgb offColor; 69 | float offIntensity; 70 | 71 | vec3 position; 72 | vec3 direction; 73 | vec3 up; 74 | vec3 linePos; 75 | 76 | float specularMultiplier; 77 | vec3 offPosition; 78 | 79 | float spotEdgeSharpness; 80 | float offRangeMultiplier; 81 | float spot; 82 | float secondSpot; 83 | 84 | vec3 spotEdgeOffset; 85 | float secondSpotTrimStart; 86 | float secondSpotRange; 87 | float secondSpotIntensity; 88 | 89 | float _p00__; 90 | float _p01__; 91 | float _p02__; 92 | float _p03__; 93 | float spotSharpness; 94 | float _p05__; 95 | 96 | float _p10__; 97 | float _p11__; 98 | float _p12__; 99 | float _p13__; 100 | float secondSpotSharpness; 101 | float _p15__; 102 | 103 | float singleFrequency; 104 | float range; 105 | float rangeGradientOffset; 106 | bool active; 107 | } state_car_light; 108 | ]] 109 | 110 | ---Holds description of a texture preprocessing stage available for both reading and writing. 111 | ---@class ac.StateCarLight 112 | ---@field active boolean @Disabling unused lights is always a good idea. 113 | ---@field color rgb 114 | ---@field intensity number 115 | ---@field lineColor rgb 116 | ---@field lineIntensity number 117 | ---@field offColor rgb 118 | ---@field offIntensity number 119 | ---@field position vec3 120 | ---@field direction vec3 121 | ---@field up vec3 122 | ---@field linePos vec3 123 | ---@field specularMultiplier number 124 | ---@field offPosition vec3 125 | ---@field spotEdgeSharpness number 126 | ---@field offRangeMultiplier number 127 | ---@field spot number 128 | ---@field spotSharpness number 129 | ---@field spotEdgeOffset vec3 @Affects headlights and brake lights. 130 | ---@field secondSpot number @Affects headlights and brake lights. 131 | ---@field secondSpotSharpness number @Affects headlights and brake lights. 132 | ---@field secondSpotTrimStart number @Affects headlights and brake lights. 133 | ---@field secondSpotRange number @Affects headlights and brake lights. 134 | ---@field secondSpotIntensity number @Affects headlights and brake lights. 135 | ---@field singleFrequency number 136 | ---@field range number 137 | ---@field rangeGradientOffset number 138 | ffi.metatype('state_car_light', { __index = {} }) 139 | -------------------------------------------------------------------------------- /car_scriptable_display/car_scriptable_display_utils.d.lua: -------------------------------------------------------------------------------- 1 | ---@class display.InteractiveMeshFactory 2 | local _interactiveMeshFactory = {} 3 | 4 | ---Call function and check its return value. If it’s `true`, event is happening right now. No need to cache 5 | ---its output somewhere local, it shouldn’t be that expensive (all raycasting is done when needed and no more than 6 | ---once per frame). 7 | ---@alias display.EventListener fun(): boolean 8 | 9 | ---Create new listener which would response to mesh (or area of a mesh) being clicked (proper VR integration is coming later). If you only need to 10 | ---listen to an area of a mesh, you can limit it by specifying a certain UV region or an area close to some point in car space (or both). 11 | ---@param texStart vec2|nil @UV coordinate of the top left corner of UV region. Optional. Goes from 0 to 1 unless you specified resolution when creating `display.InteractiveMeshFactory`, otherwise it would use coordinates in pixels. 12 | ---@param texSize vec2|nil @UV region size. Optional. Goes from 0 to 1 unless you specified resolution when creating `display.InteractiveMeshFactory`, otherwise it would use coordinates in pixels. 13 | ---@param inCarPos vec3|nil @Target position relative to car. Optional. If it and `inCarRadius` are set, only clicks closer to `inCarPos` than `inCarRadius` are registered. 14 | ---@param inCarRadius number|nil @Threshold radius in meters. Optional. If it and `inCarPos` are set, only clicks closer to `inCarPos` than `inCarRadius` are registered. 15 | ---@param inCarLocalPos boolean|nil @If set to true, local mesh coordinates are checked. Note: if mesh is scaled, distance would have to be scaled too. 16 | ---@param repeatIntervalSeconds number|nil @If set, clicks would repeat after given time has passed. Helps to create repeating buttons (ones that you can hold for a value to increase more and more, for example). 17 | ---@overload fun(inCarPos: vec3, inCarRadius: number, repeatIntervalSeconds?: number): display.EventListener 18 | ---@overload fun(inCarPos: vec3, inCarRadius: number, inCarLocalPos: boolean, repeatIntervalSeconds?: number): display.EventListener 19 | ---@overload fun(texStart: vec2, texSize: vec2, inCarPos: vec3, inCarRadius: number, repeatIntervalSeconds?: number): display.EventListener 20 | ---@overload fun(texStart: vec2, texSize: vec2, repeatIntervalSeconds?: number): display.EventListener 21 | ---@return display.EventListener 22 | function _interactiveMeshFactory.clicked(texStart, texSize, inCarPos, inCarRadius, inCarLocalPos, repeatIntervalSeconds) end 23 | 24 | ---Create new listener which would response to mesh (or area of a mesh) being pressed by a mouse (proper VR integration is coming later). If you only need to 25 | ---listen to an area of a mesh, you can limit it by specifying a certain UV region or an area close to some point in car space (or both). 26 | --- 27 | ---For holding button down (to press a couple of them at once, for example) use right or middle mouse buttons. 28 | ---@param texStart vec2|nil @UV coordinate of the top left corner of UV region. Optional. Goes from 0 to 1 unless you specified resolution when creating `display.InteractiveMeshFactory`, otherwise it would use coordinates in pixels. 29 | ---@param texSize vec2|nil @UV region size. Optional. Goes from 0 to 1 unless you specified resolution when creating `display.InteractiveMeshFactory`, otherwise it would use coordinates in pixels. 30 | ---@param inCarPos vec3|nil @Target position relative to car. Optional. If it and `inCarRadius` are set, only mouse presses closer to `inCarPos` than `inCarRadius` are registered. 31 | ---@param inCarRadius number|nil @Threshold radius in meters. Optional. If it and `inCarPos` are set, only mouse presses closer to `inCarPos` than `inCarRadius` are registered. 32 | ---@param inCarLocalPos boolean|nil @If set to true, local mesh coordinates are checked. Note: if mesh is scaled, distance would have to be scaled too. 33 | ---@overload fun(inCarPos: vec3, inCarRadius: number): display.EventListener 34 | ---@overload fun(texStart: vec2, texSize: vec2): display.EventListener 35 | ---@return display.EventListener 36 | function _interactiveMeshFactory.pressed(texStart, texSize, inCarPos, inCarRadius, inCarLocalPos) end -------------------------------------------------------------------------------- /common/ac_apps.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/gui/lua_apps.cpp' 2 | __allow 'apps' 3 | -------------------------------------------------------------------------------- /common/ac_audio.d.lua: -------------------------------------------------------------------------------- 1 | ---Create a new audio event from previously loaded soundbank. 2 | ---@param eventName string @Event name, for example, `'/cars/lada_revolution/door'` (leading “/” or “event:” prefix are optional). 3 | ---@param reverbResponse boolean @Set to true if audio event should be affected by reverb in tunnels and such. 4 | ---@param useOcclusion boolean? @Set to true if audio event should be affected by walls on configured tracks. Default value: `false`. Car audio events are always occluded if 2D. 5 | ---@return ac.AudioEvent 6 | function ac.AudioEvent(eventName, reverbResponse, useOcclusion) end 7 | 8 | ---Create a new audio event from a file. Consequent calls with the same parameters would reuse previously loaded audio file. 9 | ---Note: if `loop` is set to `false` when used with `filename`, audio event will become invalid once played once. Feel free to dispose the old one 10 | ---and create a new audio event. 11 | --[[@tableparam params { 12 | filename: string "Audio filename", 13 | stream: {name: string, size: integer} = nil "Audio stream (as an alternative to `filename` for live streaming data using a memory mapped file)", 14 | group: ac.AudioEvent = nil "Optional audio event group", 15 | use3D: boolean = true "Set to `false` to load audio without any 3D effects", 16 | useOcclusion: boolean = nil "Set to `true` to let audio occlude based on track geometry (for configured tracks, 3D audio events only)", 17 | loop: boolean = true "Set to `false` to disable audio looping", 18 | insideConeAngle: number = nil "Angle in degrees at which audio is at 100% volume", 19 | outsideConeAngle: number = nil "Angle in degrees at which audio is at `outsideVolume` volume", 20 | outsideVolume: number = nil "Volume multiplier if listener is outside of the cone", 21 | minDistance: number = nil "Distance at which audio would stop going louder as it approaches listener (default is 1)", 22 | maxDistance: number = nil "Distance at which audio would attenuating as it gets further away from listener (default is 10 km)", 23 | dopplerEffect: number = nil "Scale for doppler effect", 24 | dsp: ac.AudioDSP[] = nil "IDs of DSPs to add" 25 | }]] 26 | ---@param reverbResponse boolean @Set to true if audio event should be affected by reverb in tunnels and such. 27 | ---@return ac.AudioEvent 28 | function ac.AudioEvent.fromFile(params, reverbResponse) end 29 | 30 | ---Create a new audio event group for audio events created from files. 31 | --[[@tableparam params { 32 | group: ac.AudioEvent = nil "Optional audio event group", 33 | use3D: boolean = true "Set to `false` to load audio without any 3D effects", 34 | useOcclusion: boolean = nil "Set to `true` to let audio occlude based on track geometry (for configured tracks, 3D audio events only)", 35 | loop: boolean = true "Set to `false` to disable audio looping", 36 | insideConeAngle: number = nil "Angle in degrees at which audio is at 100% volume", 37 | outsideConeAngle: number = nil "Angle in degrees at which audio is at `outsideVolume` volume", 38 | outsideVolume: number = nil "Volume multiplier if listener is outside of the cone", 39 | minDistance: number = nil "Distance at which audio would stop going louder as it approaches listener (default is 1)", 40 | maxDistance: number = nil "Distance at which audio would attenuating as it gets further away from listener (default is 10 km)", 41 | dopplerEffect: number = nil "Scale for doppler effect", 42 | dsp: ac.AudioDSP[] = nil "IDs of DSPs to add" 43 | }]] 44 | ---@param reverbResponse boolean @Set to true if audio event should be affected by reverb in tunnels and such. 45 | ---@return ac.AudioEvent 46 | function ac.AudioEvent.group(params, reverbResponse) end 47 | 48 | -------------------------------------------------------------------------------- /common/ac_audio_enums.lua: -------------------------------------------------------------------------------- 1 | ac.AudioDSP = __enum({ underlying = 'string' }, { 2 | Oscillator = "oscillator", --- Generates sine/square/saw/triangle or noise tones. 3 | LowPass = "lowpass", --- Filters sound using a high quality, resonant lowpass filter algorithm but consumes more CPU time. 4 | ITLowPass = "itlowpass", --- Filters sound using a resonant lowpass filter algorithm that is used in Impulse Tracker, but with limited cutoff range (0 to 8060hz). 5 | HighPass = "highpass", --- Filters sound using a resonant highpass filter algorithm. 6 | Echo = "echo", --- Produces an echo on the sound and fades out at the desired rate. 7 | Fader = "fader", --- Pans and scales the volume of a unit. 8 | Flange = "flange", --- Produces a flange effect on the sound. 9 | Distortion = "distortion", --- Distorts the sound. 10 | Normalize = "normalize", --- Normalizes or amplifies the sound to a certain level. 11 | Limiter = "limiter", --- Limits the sound to a certain level. 12 | ParamEQ = "parameq", --- Attenuates or amplifies a selected frequency range. 13 | PitchShift = "pitchshift", --- Bends the pitch of a sound without changing the speed of playback. 14 | Chorus = "chorus", --- Produces a chorus effect on the sound. 15 | SFXReverb = "sfxreverb", --- Implements SFX reverb 16 | LowPassSimple = "lowpasssimple", --- Filters sound using a simple lowpass with no resonance, but has flexible cutoff and is fast. 17 | Delay = "delay", --- Produces different delays on individual channels of the sound. 18 | Tremolo = "tremolo", --- Produces a tremolo / chopper effect on the sound. 19 | HighPassSimple = "highpasssimple", --- Filters sound using a simple highpass with no resonance, but has flexible cutoff and is fast. 20 | Pan = "pan", --- Pans the signal, possibly upmixing or downmixing as well. 21 | ThreeEQ = "threeeq", --- Is a three-band equalizer. 22 | }) 23 | -------------------------------------------------------------------------------- /common/ac_car_control_physics.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_car_control_physics.cpp' 2 | __allow 'carc' 3 | 4 | ffi.cdef [[ 5 | typedef struct { 6 | bool unavailable; 7 | bool holdMode; 8 | bool stationaryOnly; 9 | bool neutralGearOnly; 10 | bool requiresBrake; 11 | } extra_switch_params; 12 | ]] 13 | 14 | ---A helper structure to simulate some inputs for controlling the car. 15 | ---@class ac.CarExtraSwitchParams 16 | ---@field unavailable boolean @Set to `true` to make a switch inaccessible by user with hotkeys. Car controlling scripts would still be able to alter its state. 17 | ---@field holdMode boolean @Set to `true` to get switch to work only if a button is currently held down. 18 | ---@field stationaryOnly boolean @Set to `true` to only allow user to change the flag if car is stationary. 19 | ---@field neutralGearOnly boolean @Set to `true` to only allow user to change the flag if car is in neutral gear. 20 | ---@field requiresBrake boolean @Set to `true` to only allow user to change the flag if brake pedal is fully pressed. 21 | ---@cpptype extra_switch_params 22 | ffi.metatype('extra_switch_params', { __index = {} }) 23 | 24 | ---@param index integer @0-based switch index. 25 | ---@return ac.CarExtraSwitchParams? @Returns `nil` if there is no switch with such index. 26 | function ac.accessExtraSwitchParams(index) 27 | local r = ffi.C.lj_accessExtraSwitchParams_inner__carc(tonumber(index) or 0) 28 | return r ~= nil and r or nil 29 | end 30 | -------------------------------------------------------------------------------- /common/ac_car_control_switch.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_car_control_switch.cpp' 2 | __allow 'carcsw' 3 | -------------------------------------------------------------------------------- /common/ac_car_cphys_enums.lua: -------------------------------------------------------------------------------- 1 | -- ac.TyreParameterID = __enum({ cpp = 'tyre_parameter_id' }, { 2 | -- RollingResistanceSlip = 0 3 | -- }) 4 | 5 | ac.CarPhysicsValueID = __enum({ cpp = 'car_physics_value_id' }, { 6 | ERSRecovery = 0, ---Value from 0 to 1 7 | ERSHeatCharging = 1, ---`true` or `false` 8 | AWD2MaxTorque = 2, ---Won’t work if there is an AWD2 controller present 9 | }) 10 | -------------------------------------------------------------------------------- /common/ac_configs.d.lua: -------------------------------------------------------------------------------- 1 | ---@class ac.ConfigProvider 2 | local _ac_ConfigProvider = {} 3 | 4 | ---@param section string 5 | ---@param key string 6 | ---@param defaultValue boolean|nil 7 | ---@return boolean 8 | function _ac_ConfigProvider.bool(section, key, defaultValue) end 9 | 10 | ---@param section string 11 | ---@param key string 12 | ---@param defaultValue number 13 | ---@return number 14 | function _ac_ConfigProvider.number(section, key, defaultValue) end 15 | 16 | ---@param section string 17 | ---@param key string 18 | ---@param defaultValue string|nil 19 | ---@return string 20 | function _ac_ConfigProvider.string(section, key, defaultValue) end 21 | 22 | ---@param section string 23 | ---@param key string 24 | ---@param defaultValue rgb|nil 25 | ---@return rgb 26 | function _ac_ConfigProvider.rgb(section, key, defaultValue) end 27 | 28 | ---@param section string 29 | ---@param key string 30 | ---@param defaultValue rgbm|nil 31 | ---@return rgbm 32 | function _ac_ConfigProvider.rgbm(section, key, defaultValue) end 33 | 34 | ---@param section string 35 | ---@param key string 36 | ---@param defaultValue vec2|nil 37 | ---@return vec2 38 | function _ac_ConfigProvider.vec2(section, key, defaultValue) end 39 | ---@param section string 40 | ---@param key string 41 | ---@param defaultValue vec3|nil 42 | ---@return vec3 43 | function _ac_ConfigProvider.vec3(section, key, defaultValue) end 44 | 45 | ---@param section string 46 | ---@param key string 47 | ---@param defaultValue vec4|nil 48 | ---@return vec4 49 | function _ac_ConfigProvider.vec4(section, key, defaultValue) end 50 | 51 | ---Reads a value from the config of currently loaded track. To use it, you need to specify `defaultValue` value, it would be used to determine 52 | ---the type of the value you need (and would be returned if value in config is missing). 53 | --- 54 | ---Alternatively, if called without arguments, returns ac.ConfigProvider which then can be used to access 55 | ---values in a typed manner. For it, `defaultValue` is optional. 56 | ---@generic T 57 | ---@param section string @Section name in config (the one in square brackets). 58 | ---@param key string @Config key (value before “=” sign). 59 | ---@param defaultValue T @Value that’s returned as a result if value is missing. Also determines the type needed. 60 | ---@return T 61 | ---@overload fun(): ac.ConfigProvider 62 | function ac.getTrackConfig(section, key, defaultValue) end 63 | 64 | ---Reads a value from the config of a car. To use it, you need to specify `defaultValue` value, it would be used to determine 65 | ---the type of the value you need (and would be returned if value in config is missing). 66 | --- 67 | ---Alternatively, if called with car index only, returns ac.ConfigProvider which then can be used to access 68 | ---values in a typed manner. For it, `defaultValue` is optional. 69 | ---@generic T 70 | ---@param carIndex integer @0-based car index. 71 | ---@param section string @Section name in config (the one in square brackets). 72 | ---@param key string @Config key (value before “=” sign). 73 | ---@param defaultValue T @Value that’s returned as a result if value is missing. Also determines the type needed. 74 | ---@return T 75 | ---@overload fun(carIndex: integer): ac.ConfigProvider 76 | function ac.getCarConfig(carIndex, section, key, defaultValue) end 77 | -------------------------------------------------------------------------------- /common/ac_configs.lua: -------------------------------------------------------------------------------- 1 | local _coimpl = {} 2 | 3 | local function _cfgProvider(car) 4 | if _coimpl[car] == nil then 5 | _coimpl[car] = { 6 | bool = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), def and true or false) end, 7 | number = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), tonumber(def) or 0) end, 8 | string = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), __util.str(def)) end, 9 | rgb = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), __util.ensure_rgb_nil(def) or rgb()) end, 10 | rgbm = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), __util.ensure_rgbm_nil(def) or rgbm()) end, 11 | vec2 = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), __util.ensure_vec2_nil(def) or vec2()) end, 12 | vec3 = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), __util.ensure_vec3_nil(def) or vec3()) end, 13 | vec4 = function(section, key, def) return __util.native('cfg', car, __util.str(section), __util.str(key), __util.ensure_vec4_nil(def) or vec4()) end, 14 | } 15 | end 16 | return _coimpl[car] 17 | end 18 | 19 | function ac.getTrackConfig(section, key, def) 20 | if section == nil then return _cfgProvider(65536) end 21 | return __util.native('cfg', 65536, section, key, def) 22 | end 23 | 24 | function ac.getCarConfig(car, section, key, def) 25 | if type(car) ~= 'number' or car < 0 then error('Car ID is required to be a number', 2) end 26 | if section == nil then return _cfgProvider(car) end 27 | return __util.native('cfg', car, section, key, def) 28 | end 29 | -------------------------------------------------------------------------------- /common/ac_display.lua: -------------------------------------------------------------------------------- 1 | ---Display namespace with helper functions for creating dynamic textures. 2 | display = {} 3 | 4 | ---Draw a rectangle. 5 | --[[@tableparam params { 6 | pos: vec2 "Coordinates of the top left corner in pixels", 7 | size: vec2 "Size in pixels", 8 | color: rgbm = rgbm.colors.white "Rectangle color" 9 | }]] 10 | function display.rect(params) 11 | local pos = params.pos -- rect position 12 | local size = params.size -- rect size 13 | local color = params.color -- rect color 14 | ui.drawRectFilled(pos, pos + size, color) 15 | end 16 | 17 | ---Draw an image. 18 | --- 19 | ---If you’re drawing a lot of different images, consider combining them into a single atlas and using 20 | ---`uvStart`/`uvEnd` to specify the region. 21 | --[[@tableparam params { 22 | image: string "Path to image to draw", 23 | pos: vec2 "Coordinates of the top left corner in pixels", 24 | size: vec2 "Size in pixels", 25 | color: rgbm = rgbm.colors.white "Image will be multiplied by this color", 26 | uvStart: vec2 = vec2(0, 0) "UV coordinates of the top left corner", 27 | uvEnd: vec2 = vec2(1, 1) "UV coordinates of the bottom right corner" 28 | }]] 29 | function display.image(params) 30 | local image = params.image -- image source 31 | local pos = params.pos -- image position 32 | local size = params.size -- image size 33 | local color = params.color -- image tint 34 | local uvStart = params.uvStart -- UV for upper left corner, optional 35 | local uvEnd = params.uvEnd -- UV for bottom right corner, optional 36 | ui.drawImage(image, pos, pos + size, color, uvStart, uvEnd) 37 | end 38 | 39 | ---Draw text using AC font. 40 | --[[@tableparam params { 41 | text: string "Text to draw", 42 | pos: vec2 "Coordinates of the top left corner in pixels", 43 | letter: vec2 = vec2(20, 40) "Size of each letter", 44 | font: string = 'aria' "AC font to draw text with, either from “content/fonts” or from a folder with a script (can refer to a subfolder)", 45 | color: rgbm = rgbm.colors.white "Text color", 46 | alignment: number = 0 "0 for left, 0.5 for center, 1 for middle, could be anything in-between. Set `width` as well so it would know in what area to align text.", 47 | width: number = 200 "Required for non-left alignment", 48 | spacing: number = 0 "Additional offset between characters, could be either positive or negative" 49 | }]] 50 | function display.text(params) 51 | local text = tostring(params.text) -- text to draw 52 | local pos = params.pos -- text position, optional 53 | local letter = params.letter -- size of each letter 54 | local font = params.font -- name of font, optional 55 | local color = params.color -- color, optional 56 | local width = params.width or 0 -- width, optional 57 | local alignment = params.alignment -- alignment, optional (0.5 for center, 1 for right) 58 | local spacing = params.spacing or 0 -- extra spacing between letters 59 | 60 | local textLen = #text 61 | if textLen == 0 then return end 62 | 63 | local actualWidth = letter.x * textLen + (textLen > 0 and spacing * (textLen - 1) or 0) 64 | if width > actualWidth then 65 | pos.x = pos.x + (width - actualWidth) * alignment 66 | end 67 | 68 | if font ~= nil then ui.pushACFont(font) end 69 | if pos ~= nil then ui.setCursor(pos) end 70 | ui.acText(text, letter, spacing, color) 71 | if font ~= nil then ui.popACFont() end 72 | end 73 | 74 | ---Draw simple horizontal bar (like progress bar) consisting of several sections. 75 | --[[@tableparam params { 76 | text: string "Text to draw", 77 | pos: vec2 "Coordinates of the top left corner of the bar in pixels", 78 | size: vec2 = vec2(200, 40) "Size of the whole bar", 79 | delta: number = 8 "Distance between elements", 80 | activeColor: rgbm = rgbm.colors.white "Active color", 81 | inactiveColor: rgbm = rgbm.colors.transparent "Inactive color", 82 | total: integer = 12 "Total number of sections", 83 | active: integer = 8 "Number of active sections" 84 | }]] 85 | function display.horizontalBar(params) 86 | local pos = params.pos -- bar position 87 | local size = params.size -- bar size 88 | local delta = params.delta -- distance between elements 89 | local activeColor = params.activeColor -- active color 90 | local inactiveColor = params.inactiveColor -- inactive color 91 | local total = params.total 92 | local active = params.active 93 | local itemSize = (size.x - delta * (total - 1)) / total 94 | for i = 1, total do 95 | ui.drawRectFilled(pos, pos + vec2(itemSize, size.y), i <= active and activeColor or inactiveColor) 96 | pos = pos + vec2(itemSize + delta, 0) 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /common/ac_dualsense.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_dualsense.cpp' 2 | __states 'lua/api_dualsense.cpp' 3 | 4 | local _dslv, _dsrn 5 | 6 | ---Return table with gamepad indices for keys and 0-based indices of associated cars for values. 7 | ---@return table 8 | function ac.getDualSenseControllers() 9 | if not _dsrn then _dsrn = refnumber(-1) end 10 | ffi.C.lj_getDualSenseControllers_inner(_dsrn) 11 | _dslv = __util.result() or _dslv 12 | return _dslv or error('Failed to get data', 2) 13 | end 14 | 15 | -------------------------------------------------------------------------------- /common/ac_dualshock.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_dualshock.cpp' 2 | __states 'lua/api_dualshock.cpp' 3 | 4 | local _dhlv, _dhrn 5 | 6 | ---Return table with gamepad indices for keys and 0-based indices of associated cars for values. 7 | ---@return table 8 | function ac.getDualShockControllers() 9 | if not _dhrn then _dhrn = refnumber(-1) end 10 | ffi.C.lj_getDualShockControllers_inner(_dhrn) 11 | _dhlv = __util.result() or _dhlv 12 | return _dhlv or error('Failed to get data', 2) 13 | end 14 | 15 | -------------------------------------------------------------------------------- /common/ac_extras_backgroundworker.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_extras_backgroundworker.cpp' 2 | -------------------------------------------------------------------------------- /common/ac_extras_binaryinput.d.lua: -------------------------------------------------------------------------------- 1 | ---@param id string @Name of a section in “controls.ini”. If you are adding a new input, use something like “your.namespace/Nice Name” (without square brackets or colons) to ensure there won’t be collisions and it would integrate nicely. 2 | ---@param defaults {keyboard: nil|{key: ui.KeyIndex?, ctrl: boolean?, shift: boolean?, alt: boolean?}, gamepad: nil|ac.GamepadButton, period: nil|number, hold: nil|boolean}? @Default settings if user has not configured input yet. Parameter `period` can be used to create buttons which would keep reporting as pressed (or call `:onDown()`) while held, can be configured in “controls.ini” as “REPEAT_PERIOD”; by default repeating is disable. Set parameter `hold` to a boolean value and control widget will get a “hold” switch. Buttons configured in “hold” mode return `true` on `:pressed()` when both pressed and released, as well as trigger `:onPressed()` when released too. Note: if `ac.ControlButton()` is called multiple times within a race session, only defaults from the first run will be taken into account (but if subsequent calls will have a “hold” value, button editing widget will still get a “hold” switch). 3 | ---@return ac.ControlButton 4 | function ac.ControlButton(id, defaults) end 5 | -------------------------------------------------------------------------------- /common/ac_extras_connect.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_extras_connect.cpp' 2 | 3 | require('./ac_struct_item') 4 | 5 | ---Creates a new shared structure to quickly exchange data between different Lua scripts within a session. Example: 6 | ---``` 7 | ---local sharedData = ac.connect{ 8 | --- ac.StructItem.key('myChannel'), -- optional, to avoid collisions 9 | --- someString = ac.StructItem.string(24), -- 24 is for capacity 10 | --- someInt = ac.StructItem.int(), 11 | --- someDouble = ac.StructItem.double(), 12 | --- someVec = ac.StructItem.vec3() 13 | ---} 14 | ---``` 15 | --- 16 | ---Note: to connect two scripts, both of them chould use `ac.connect()` and pass exactly the same layouts. Also, consider using more 17 | ---specific names to avoid possible unwanted collisions. For example, instead of using `value = ac.StructItem.int()` which might be 18 | ---used somewhere else, use `weatherBrightnessValue = ac.StructItem.int()`. Or, simply add `ac.StructItem.key('myUniqueKey')`. 19 | --- 20 | ---For safety reasons, car scripts can only connect to other car scripts, and track scripts can only connect to other track scripts. 21 | ---@generic T 22 | ---@param layout T @A table containing fields of structure and their types. Use `ac.StructItem` methods to select types. Alternatively, you can pass a string for the body of the structure here, but be careful with it. 23 | ---@param keepLive boolean? @Set to true to keep structure even if any references were removed or script was unloaded. 24 | ---@param namespace nil|ac.SharedNamespace @Optional namespace stopping scripts of certain types to access data of scripts with different types. For more details check `ac.SharedNamespace` documentation. 25 | ---@return T 26 | function ac.connect(layout, keepLive, namespace) 27 | if not __allowIO__ and namespace == ac.SharedNamespace.Global then error('Script of this type can’t use global namespace', 2) end 28 | local s_name, s_layout = __util.__si_ffi(layout, false) 29 | local s_size = ffi.sizeof(s_name) 30 | return __util.__si_proxy(layout, ffi.gc(ffi.cast(s_name..'*', 31 | ffi.C.lj_connect_new(s_layout, type(namespace) == 'string' and namespace or nil, s_size, keepLive ~= false)), ffi.C.lj_connect_gc)) 32 | end 33 | 34 | ---Create a new struct from a given layout. Could be used in calls like `ac.structBytes()` and `ac.fillStructWithBytes()`. Each call defines and creates a new struct, so don’t 35 | ---call them each frame, I believe LuaJIT doesn’t do garbage collection on struct definitions. 36 | ---@generic T 37 | ---@param layout T 38 | ---@param compact boolean? 39 | ---@return T 40 | ---@return integer @Structure size. 41 | ---@return string @Structure name. 42 | function ac.StructItem.combine(layout, compact) 43 | local s_name = __util.__si_ffi(layout, compact) 44 | return ffi.new(s_name), ffi.sizeof(s_name), s_name 45 | end 46 | -------------------------------------------------------------------------------- /common/ac_extras_connectmmf.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_extras_connectmmf.cpp' 2 | 3 | require('./ac_struct_item') 4 | 5 | local function mmfGC(item) 6 | -- ac.log('GC: %p' % ffi.cast('uint64_t', item)) 7 | ffi.gc(item, nil) 8 | ffi.C.lj_connectmmf_gc(ffi.cast('void*', item)) 9 | end 10 | 11 | ---Opens shared memory file for reading. Do not attempt to modify any of its contents: doing so pretty much always would result in Assetto Corsa 12 | ---just straight up crashing. 13 | ---@generic T 14 | ---@param filename string @Shared memory file filename (without “Local\” bit). 15 | ---@param layout T @String for the body of the structure. 16 | ---@param persist boolean? @Keep file alive even after the script stopped or the variable was cleared by garbage collector. Default value: `false`. 17 | ---@return T 18 | ---@overload fun(filename: string, layout: string, persist: boolean?): any 19 | function ac.readMemoryMappedFile(filename, layout, persist) 20 | if not __allowIO__ and not string.startsWith(filename, 'AcTools.CSP.Limited.') then error('Script of this type can’t access shared memory files', 2) end 21 | 22 | local ret 23 | if type(layout) == 'number' then 24 | if persist then 25 | error('Option “persist” is not available with raw files', 2) 26 | end 27 | ret = ffi.C.lj_connectmmf_new(filename, layout, false) 28 | if ret == nil then error('Failed to open shared memory file', 2) end 29 | else 30 | local s_name = __util.__si_ffi(layout, false) 31 | local ptr = ffi.C.lj_connectmmf_new(filename, ffi.sizeof(s_name), false) 32 | if ptr == nil then error('Failed to open shared memory file', 2) end 33 | ret = ffi.cast(ffi.typeof(s_name..'*'), ptr) 34 | end 35 | 36 | if not persist then ret = ffi.gc(ret, mmfGC) end 37 | return __util.__si_proxy(layout, ret) 38 | end 39 | 40 | ---Opens shared memory file for writing. Note: if the file would exist at the moment of opening (for example, created before by a different 41 | ---Lua script, or by a separate process), it would retain its current state, but if it’s a new file, it’ll be initialized with all zeroes. 42 | ---@generic T 43 | ---@param filename string @Shared memory file filename (without “Local\” bit). 44 | ---@param layout T @String for the body of the structure. 45 | ---@param persist boolean? @Keep file alive even after the script stopped or the variable was cleared by garbage collector. Default value: `false`. 46 | ---@return T 47 | ---@overload fun(filename: string, layout: string, persist: boolean?): any 48 | function ac.writeMemoryMappedFile(filename, layout, persist) 49 | if not __allowIO__ and not string.startsWith(filename, 'AcTools.CSP.Limited.') then error('Script of this type can’t access shared memory files', 2) end 50 | 51 | local ret 52 | if type(layout) == 'number' then 53 | if persist then 54 | error('Option “persist” is not available with raw files', 2) 55 | end 56 | ret = ffi.C.lj_connectmmf_new(filename, layout, true) 57 | if ret == nil then error('Failed to open shared memory file', 2) end 58 | else 59 | local s_name = __util.__si_ffi(layout, false) 60 | local ptr = ffi.C.lj_connectmmf_new(filename, ffi.sizeof(s_name), true) 61 | if ptr == nil then error('Failed to open shared memory file', 2) end 62 | ret = ffi.cast(ffi.typeof(s_name..'*'), ptr) 63 | end 64 | 65 | if not persist then ret = ffi.gc(ret, mmfGC) end 66 | return __util.__si_proxy(layout, ret) 67 | end 68 | 69 | ---Forcefully closes memory mapped file opened either for reading or writing without waiting for GC to pick it up. 70 | function ac.disposeMemoryMappedFile(reference) 71 | if type(reference) == 'table' and reference.__data_cdata__ then 72 | reference = reference.__data_cdata__.i 73 | end 74 | if type(reference) == 'cdata' then 75 | -- ac.log('DISPOSE: %p' % ffi.cast('uint64_t', reference)) 76 | mmfGC(reference) 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /common/ac_extras_datalut.d.lua: -------------------------------------------------------------------------------- 1 | ---Creates a new empty 1D-to-1D LUT. Use `ac.DataLUT11:add(input, output)` to fill it with data. 2 | ---@return ac.DataLUT11 3 | function ac.DataLUT11() end 4 | 5 | ---Parse 1D-to-1D LUT from a string in “(|Input1=Output1|Input2=Output2|…|)” format. 6 | ---@param data string @Serialized LUT data. 7 | ---@return ac.DataLUT11 8 | function ac.DataLUT11.parse(data) end 9 | 10 | ---Load 1D-to-1D LUT file. 11 | ---@param filename string @LUT filename. 12 | ---@return ac.DataLUT11 13 | function ac.DataLUT11.load(filename) end 14 | 15 | ---Load car data 1D-to-1D LUT file. Supports “data.acd” files as well. 16 | ---@param carIndex number @0-based car index. 17 | ---@param fileName string @Car data file name, such as `'power.lut'`. 18 | ---@return ac.DataLUT11 19 | function ac.DataLUT11.carData(carIndex, fileName) end 20 | 21 | ---Creates a new empty 2D-to-1D LUT. Use `ac.DataLUT21:add(input, output)` to fill it with data. 22 | ---@return ac.DataLUT21 23 | function ac.DataLUT21() end 24 | 25 | ---Parse 2D-to-1D LUT from a string in “(|X1,Y1=Output1|X2,Y2=Output2|…|)” format. 26 | ---@param data string @Serialized LUT data. 27 | ---@return ac.DataLUT21 28 | function ac.DataLUT21.parse(data) end 29 | 30 | ---Load 2D-to-1D LUT file. 31 | ---@param filename string @LUT filename. 32 | ---@return ac.DataLUT21 33 | function ac.DataLUT21.load(filename) end 34 | 35 | ---Load car data 2D-to-1D LUT file. Supports “data.acd” files as well. 36 | ---@param carIndex number @0-based car index. 37 | ---@param fileName string @Car data file name, such as `'speed_throttle.2dlut'`. 38 | ---@return ac.DataLUT21 39 | function ac.DataLUT21.carData(carIndex, fileName) end 40 | -------------------------------------------------------------------------------- /common/ac_extras_hashspace.lua: -------------------------------------------------------------------------------- 1 | -- Helper for more efficient collision checks in 2D space (using 3D coordinates) 2 | 3 | ---@param cellSize number @Should be about twice as large as your largest entity. 4 | ---@return ac.HashSpace 5 | function ac.HashSpace(cellSize) return __util.lazy('lib_hashspace')(cellSize) end 6 | -------------------------------------------------------------------------------- /common/ac_extras_leapmotion.lua: -------------------------------------------------------------------------------- 1 | -- __source 'ac_hooks/ac_hooks_leap.cpp' 2 | -- __states 'ac_hooks/ac_hooks_leap.cpp' 3 | -------------------------------------------------------------------------------- /common/ac_extras_numlut.lua: -------------------------------------------------------------------------------- 1 | ---@param data string @String with LUT data, in a format similar to AC LUT formats. Please note: rows must be ordered for efficient binary search. 2 | ---@param hsvColumns integer[] @1-based indices of columns storing HSV data. Such columns, of course, will be interpolated differently (for example, mixing hues 350 and 20 would produce 10). 3 | ---@return ac.Lut 4 | function ac.Lut(data, hsvColumns) 5 | return __util.lazy('lib_numlut')(data, hsvColumns) 6 | end 7 | 8 | ---@type ac.Lut 9 | ac.LutCpp = ac.Lut 10 | 11 | ac.LutJit = {} 12 | 13 | ---Creates new ac.LuaJit instance. Deprecated and broken, use `ac.Lut` instead. 14 | ---@deprecated 15 | ---@param data any 16 | ---@param hsvRows integer[] @ 1-based indices of columns (not rows) storing HSV values in them. 17 | ---@return table 18 | function ac.LutJit:new(o, data, hsvRows) 19 | return __util.lazy('lib_numlut_jit'):new(o, data, hsvRows) 20 | end 21 | -------------------------------------------------------------------------------- /common/ac_extras_tracklines.lua: -------------------------------------------------------------------------------- 1 | ---@return ac.TrackPaint 2 | function ac.TrackPaint() 3 | return __util.lazy('lib_tracklines')() 4 | end 5 | -------------------------------------------------------------------------------- /common/ac_game_enums.lua: -------------------------------------------------------------------------------- 1 | ac.PhysicsDebugLines = __enum({ cpp = 'phys_debug_lines_switches' }, { 2 | None = 0, 3 | Tyres = 1, -- Tyres raycasting 4 | WetSkidmarks = 2, -- Marks left by tyres reducing grip in rain 5 | Script = 4, -- Lines drawn by custom physics script 6 | RainLane = 65536, -- Alternative AI lane for rain 7 | }) 8 | 9 | ac.LightsDebugMode = __enum({ cpp = 'lights_debug_mode' }, { 10 | Off = 0, -- @hidden 11 | None = 0, 12 | Outline = 1, 13 | BoundingBox = 2, 14 | BoundingSphere = 4, 15 | Text = 8, 16 | }) 17 | 18 | ac.VRSRateMode = __enum({ cpp = 'vrs_rate_mode' }, { 19 | X0 = 0, 20 | X16 = 1, 21 | X8 = 2, 22 | X4 = 3, 23 | X2 = 4, 24 | X1 = 5, 25 | X1_2X1 = 6, 26 | X1_1X2 = 7, 27 | X1_2X2 = 8, 28 | X1_4X2 = 9, 29 | X1_2X4 = 10, 30 | X1_4X4 = 11, 31 | }) 32 | 33 | ac.ScreenshotFormat = __enum({ cpp = 'screenshot_format' }, { 34 | Auto = 0, -- As configured in AC system settings 35 | BMP = 1, 36 | JPG = 2, 37 | JPEG = 2, 38 | PNG = 3, 39 | DDS = 4, 40 | }) 41 | 42 | ac.SceneTweakFlag = __enum({}, { 43 | Default = 0, 44 | ForceOn = 1, 45 | ForceOff = 2, 46 | }) 47 | 48 | ac.CarControlsInput = {} 49 | 50 | ac.CarControlsInput.Flag = __enum({}, { 51 | Skip = -1, 52 | Disable = 0, 53 | Enable = 1 54 | }) 55 | -------------------------------------------------------------------------------- /common/ac_gameplay_replaystream.lua: -------------------------------------------------------------------------------- 1 | 2 | local _rpsActive = {} 3 | ffi.cdef [[ 4 | typedef struct { 5 | void* _frame; 6 | } replayextension; 7 | ]] 8 | 9 | local function __si_replayMixing(reordered) 10 | local mixing = {} 11 | if reordered then 12 | local offset = 0 13 | 14 | local function procItem(v) 15 | local u = 1 16 | if v.array then 17 | for i = 1, #v.array do 18 | u = u * v.array[i] 19 | end 20 | end 21 | if v.struct then 22 | for _ = 1, u do 23 | for _, c in ipairs(v.struct) do 24 | procItem(c) 25 | end 26 | end 27 | return 28 | elseif v.replayType then 29 | for _ = 1, u do 30 | if v.replayType > 99 then 31 | local c = math.floor(v.replayType / 100) 32 | for _ = 1, c do 33 | mixing[#mixing + 1] = string.format('%d:%d', offset, v.replayType % 100) 34 | offset = offset + v.packingSize / c 35 | end 36 | else 37 | mixing[#mixing + 1] = string.format('%d:%d', offset, v.replayType) 38 | offset = offset + v.packingSize 39 | end 40 | end 41 | else 42 | offset = offset + v.realSize 43 | end 44 | end 45 | 46 | for _, v in ipairs(reordered) do 47 | procItem(v) 48 | end 49 | end 50 | return table.concat(mixing, '\n') 51 | end 52 | 53 | ---Create a new stream for recording data to replays. Write data in returned structure if not in replay mode, read data if in replay mode (use `sim.isReplayActive` to check if you need to write or read the data). 54 | ---Few important points: 55 | --- - Each frame should not exceed 256 bytes to keep replay size appropriate. 56 | --- - While data will be interpolated between frames during reading, directional vectors won’t be re-normalized. 57 | --- - If two different apps would open a stream with the same layout, they’ll share a replay entry. 58 | --- - Each opened replay stream will persist through the entire AC session to be saved at the end. Currently, the limit is 128 streams per session. 59 | --- - Default values for unitialized frames are zeroes. 60 | ---@generic T 61 | ---@param layout T @A table containing fields of structure and their types. Use `ac.StructItem` methods to select types. Unlike other similar functions, here you shouldn’t use string, otherwise data blending won’t work. 62 | ---@param callback fun()? @Callback that will be called when replay stops. Use this callback to re-apply data from structure: at the moment of the call it will contain stuff from last recorded frame allowing you to restore the state of a simulation to when replay mode was activated. 63 | ---@return T? @Might return `nil` if there is game is launched in replay mode and there is no such data stored in the replay. 64 | function ac.ReplayStream(layout, callback) 65 | local layoutStr, reordered = __util.__si_build(layout) 66 | local s_name = __util.__si_ffi(layoutStr, true) 67 | local cached = _rpsActive[s_name] 68 | if cached == nil then 69 | local created = ffi.gc(ffi.C.lj_replayextension_new('__rps_'..tostring(ac.checksumXXH(layoutStr)), ffi.sizeof(s_name), __si_replayMixing(reordered), 70 | callback and __util.setCallback(callback) or 0), ffi.C.lj_replayextension_gc) 71 | cached = {created, created._frame ~= nil and ffi.cast(s_name..'*', created._frame) or nil} 72 | _rpsActive[s_name] = cached 73 | end 74 | return cached[2] 75 | end 76 | 77 | if __script.__test then 78 | __util.__si_replayMixing = __si_replayMixing 79 | end -------------------------------------------------------------------------------- /common/ac_general_utils.lua: -------------------------------------------------------------------------------- 1 | local _lapTimeEvaluation = nil 2 | 3 | ffi.cdef[[ 4 | typedef struct { 5 | int estimated_lap_time_ms; 6 | int sectors_count; 7 | int* estimated_sector_time_ms; 8 | } lua_time_evaluation; 9 | ]] 10 | 11 | ---Estimates lap time and sector times for main car using AC function originally used by Time Attack mode. Could be 12 | ---helpful in creating custom time attack modes. Uses “ideal_line.ai” from “track folder/data”, so might not work 13 | ---well with mods. If that file is missing, returns nil. 14 | ---@return {lapTimeMs: integer, sectorTimesMs: integer[]}|nil @Returns table with times in milliseconds, or `nil` if “ideal_lane.ai” is missing. 15 | function ac.evaluateLapTime() 16 | if not _lapTimeEvaluation then 17 | local data = ffi.C.lj_evaluate_lap_time() 18 | _lapTimeEvaluation = { 19 | lapTimeMs = data.estimated_lap_time_ms, 20 | sectorTimesMs = table.range(data.sectors_count, function (i, d) return i[d - 1] end, data.estimated_sector_time_ms) 21 | } 22 | end 23 | return _lapTimeEvaluation.lapTimeMs > 0 and _lapTimeEvaluation or nil 24 | end 25 | -------------------------------------------------------------------------------- /common/ac_joypad_assist_enums.lua: -------------------------------------------------------------------------------- 1 | ac.DualSenseHapticParam = __enum({ cpp = 'dualsense_haptic_param' }, { 2 | Bodywork = 0, 3 | Engine = 1, 4 | Gear = 2, 5 | GearGrind = 3, 6 | Limiter = 4, 7 | Skid = 5, 8 | Wheel = 6, 9 | CollisionCar = 7, 10 | CollisionObject = 8, 11 | CollisionTrack = 9, 12 | ScrapeCar = 10, 13 | ScrapeTrack = 11, 14 | }) 15 | -------------------------------------------------------------------------------- /common/ac_matrices.d.lua: -------------------------------------------------------------------------------- 1 | ---Creates a new neutral matrix. 2 | ---@return mat3x3 3 | function mat3x3.identity() end 4 | 5 | ---@class mat3x3 6 | ---@field row1 vec3 7 | ---@field row2 vec3 8 | ---@field row3 vec3 9 | ---@constructor fun(row1: vec3?, row2: vec3?, row3: vec3?): mat3x3 10 | 11 | ---@param value mat3x3 12 | ---@return mat3x3 13 | function mat3x3:set(value) end 14 | 15 | ---@return mat3x3 16 | function mat3x3:clone() end 17 | 18 | ---Creates a new neutral matrix. 19 | ---@return mat4x4 20 | function mat4x4.identity() end 21 | 22 | ---Creates a translation matrix. 23 | ---@param offset vec3 24 | ---@return mat4x4 25 | function mat4x4.translation(offset) end 26 | 27 | ---Creates a rotation matrix. 28 | ---@param angle number @Angle in radians. 29 | ---@param axis vec3 30 | ---@return mat4x4 31 | function mat4x4.rotation(angle, axis) end 32 | 33 | ---Creates a rotation matrix from Euler angles in radians. 34 | ---@param head number 35 | ---@param pitch number 36 | ---@param roll number 37 | ---@return mat4x4 38 | function mat4x4.euler(head, pitch, roll) end 39 | 40 | ---Creates a scaling matrix. 41 | ---@param scale vec3 42 | ---@return mat4x4 43 | function mat4x4.scaling(scale) end 44 | 45 | ---Creates a look-at matrix from position and directional vectors. Ensures all vectors are properly normalized. 46 | ---@param position vec3 47 | ---@param look vec3 48 | ---@param up vec3? @Default value: `vec3(0, 1, 0)`. 49 | ---@return mat4x4 50 | function mat4x4.look(position, look, up) end 51 | 52 | ---Creates a perspective matrix. 53 | ---@param fovY number @Vertical view angle in radians. 54 | ---@param aspect number @Aspect ratio. 55 | ---@param zNear number @Near clipping plane. 56 | ---@param zFar number @Far clipping plane. 57 | ---@return mat4x4 58 | function mat4x4.perspective(fovY, aspect, zNear, zFar) end 59 | 60 | ---Creates an orthogonal matrix. Might act unexpected with Z values, shifting by range should help. 61 | ---@param extentMin vec3 62 | ---@param extentMax vec3 63 | ---@return mat4x4 64 | function mat4x4.ortho(extentMin, extentMax) end 65 | 66 | ---@class mat4x4 67 | ---@field row1 vec4 68 | ---@field row2 vec4 69 | ---@field row3 vec4 70 | ---@field row4 vec4 71 | ---@field position vec3 72 | ---@field look vec3 73 | ---@field side vec3 74 | ---@field up vec3 75 | ---@constructor fun(row1: vec4?, row2: vec4?, row3: vec4?, row4: vec4?): mat4x4 76 | 77 | ---@param value mat4x4 78 | ---@return mat4x4 79 | function mat4x4:set(value) end 80 | 81 | ---@param destination vec3 82 | ---@param vec vec3 83 | ---@return vec3 84 | function mat4x4:transformVectorTo(destination, vec) end 85 | 86 | ---@param vec vec3 87 | ---@return vec3 88 | function mat4x4:transformVector(vec) end 89 | 90 | ---@param destination vec4 91 | ---@param vec vec4 92 | ---@return vec4 93 | function mat4x4:transformTo(destination, vec) end 94 | 95 | ---@param vec vec4 96 | ---@return vec4 97 | function mat4x4:transform(vec) end 98 | 99 | ---@param destination vec3 100 | ---@param vec vec3 101 | ---@return vec3 102 | function mat4x4:transformPointTo(destination, vec) end 103 | 104 | ---@param vec vec3 105 | ---@return vec3 106 | function mat4x4:transformPoint(vec) end 107 | 108 | ---@return mat4x4 109 | function mat4x4:clone() end 110 | 111 | ---Creates a new matrix. 112 | ---@return mat4x4 113 | function mat4x4:inverse() end 114 | 115 | ---Modifies current matrix. 116 | ---@return mat4x4 @Returns self for easy chaining. 117 | function mat4x4:inverseSelf() end 118 | 119 | ---Creates a new matrix. 120 | ---@return mat4x4 121 | function mat4x4:normalize() end 122 | 123 | ---Modifies current matrix. 124 | ---@return mat4x4 @Returns self for easy chaining. 125 | function mat4x4:normalizeSelf() end 126 | 127 | ---Creates a new matrix. 128 | ---@return mat4x4 129 | function mat4x4:transpose() end 130 | 131 | ---Modifies current matrix. 132 | ---@return mat4x4 @Returns self for easy chaining. 133 | function mat4x4:transposeSelf() end 134 | 135 | ---Note: unlike vector’s `:mul()`, this one creates a new matrix! 136 | ---@param other mat4x4 137 | ---@return mat4x4 138 | function mat4x4:mul(other) end 139 | 140 | ---Modifies current matrix. 141 | ---@param other mat4x4 142 | ---@return mat4x4 @Returns self for easy chaining. 143 | function mat4x4:mulSelf(other) end 144 | 145 | ---Writes result into a separate matrix. 146 | ---@param destination mat4x4 147 | ---@param other mat4x4 148 | ---@return mat4x4 @Returns destination matrix. 149 | function mat4x4:mulTo(destination, other) end 150 | -------------------------------------------------------------------------------- /common/ac_matrices.d.off.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | ---Creates a helper for packing and unpacking matrices into compact binary structures. Parameters `destination` and `source` of `any` type should 4 | ---receive reference to a binary array in an FFI structure. 5 | ---@param compactPosition boolean? @If `true`, position is packed into 3 bytes, otherwise it will take 6 bytes. Default value: `false`. 6 | ---@param compactRotation boolean? @If `true`, rotation is packed into 3 bytes, otherwise it will take 6 bytes. Default value: `false`. 7 | ---@param rangeFrom vec3? @Minimal expected position. Pass it together with `rangeTo` to encode position data more efficiently. 8 | ---@param rangeTo vec3? @Maximum expected position. Pass it together with `rangeFrom` to encode position data more efficiently. 9 | ---@return {pack: fun(destination: any, matrix: mat4x4), unpack: (fun(source: any): mat4x4), unpackTo: fun(source: any, destination: mat4x4)} 10 | function mat4x4.packer(compactPosition, compactRotation, rangeFrom, rangeTo) end -------------------------------------------------------------------------------- /common/ac_music.d.lua: -------------------------------------------------------------------------------- 1 | ---Information about currently playing track. Use function `ac.currentlyPlaying()` to get 2 | ---a reference to it. 3 | --- 4 | ---To draw album cover, pass `ac.MusicData` as an argument to something like `ui.image()`. 5 | ---@class ac.MusicData 6 | ---@field isPlaying boolean @If `true`, music is currently playing. 7 | ---@field hasCover boolean @If `true`, album cover is present. 8 | ---@field title string @Name of currently playing track. 9 | ---@field album string @Name of currently playing album (if not available, an empty string). 10 | ---@field artist string @Name of currently playing artist (if not available, an empty string). 11 | ---@field sourceID string @Source ID from where track is coming from. To draw an icon for it, pass it as Icon24 ID. You can check if there is an icon using `ui.isKnownIcon24(playing.sourceID)`. 12 | ---@field albumTracksCount integer @Number of tracks in current album, or 0 if value is not available. 13 | ---@field trackNumber integer @1-based track number in current album, or 0 if value is not available. 14 | ---@field trackDuration integer @Track duration in seconds, or -1 if value is not available. 15 | ---@field trackPosition integer @Track position in seconds, or -1 if value is not available. 16 | local _musicData = {} 17 | 18 | -------------------------------------------------------------------------------- /common/ac_music.lua: -------------------------------------------------------------------------------- 1 | ---Syncs information about currently playing music and returns a table with details. Takes data from 2 | ---Windows 10 Media API, or from other sources configured in Music module of CSP. 3 | ---@return ac.MusicData 4 | function ac.currentlyPlaying() 5 | ac.currentlyPlaying = __util.lazy('lib_music') 6 | return ac.currentlyPlaying() 7 | end 8 | -------------------------------------------------------------------------------- /common/ac_physics.d.lua: -------------------------------------------------------------------------------- 1 | ---State of car controls. 2 | ---@class physics.CarControls 3 | ---@field gas number @Gas from 0 to 1 (pedal is fully pressed with 1). 4 | ---@field brake number @Braking from 0 to 1 (pedal is fully pressed with 1). 5 | ---@field steer number @Steering angle from -1 to 1. 6 | ---@field clutch number @1 for fully depressed clutch pedal (clutch fully engaged), 0 for pedal fully pressed (and clutch disengaged). 7 | ---@field gearUp boolean 8 | ---@field gearDown boolean 9 | ---@field drs boolean 10 | ---@field kers boolean 11 | ---@field brakeBalanceUp boolean 12 | ---@field brakeBalanceDown boolean 13 | ---@field requestedGearIndex integer 14 | ---@field isShifterSupported boolean 15 | ---@field handbrake number 16 | ---@field absUp boolean 17 | ---@field absDown boolean 18 | ---@field tcUp boolean 19 | ---@field tcDown boolean 20 | ---@field turboUp boolean 21 | ---@field turboDown boolean 22 | ---@field engineBrakeUp boolean 23 | ---@field engineBrakeDown boolean 24 | ---@field mgukDeliveryUp boolean 25 | ---@field mgukDeliveryDown boolean 26 | ---@field mgukRecoveryUp boolean 27 | ---@field mgukRecoveryDown boolean 28 | ---@field mguhMode integer 29 | ---@cpptype ac_car_controls 30 | local _carControls = {} 31 | -------------------------------------------------------------------------------- /common/ac_physics_ai.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_physics_ai.cpp' 2 | __namespace 'physics' 3 | -------------------------------------------------------------------------------- /common/ac_physics_raycast.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_physics_raycast.cpp' 2 | __namespace 'physics' 3 | 4 | ---Physics namespace. Provides access to physics raycast only. For more functions, try a different type of script. 5 | physics = {} 6 | -------------------------------------------------------------------------------- /common/ac_physics_unrestricted.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_physics_unrestricted.cpp' 2 | __namespace 'physics' 3 | 4 | ---Physics namespace. Note: most functions here are accessible only if track has expicitly allowed it with its 5 | ---extended CSP physics. 6 | --- 7 | ---To allow scriptable physics, add to surfaces.ini: 8 | ---```ini 9 | ---[_SCRIPTING_PHYSICS] 10 | ---ALLOW_TRACK_SCRIPTS=1 ; choose ones that you need 11 | ---ALLOW_DISPLAY_SCRIPTS=1 12 | ---ALLOW_NEW_MODE_SCRIPTS=1 13 | ---ALLOW_TOOLS=1 14 | ---``` 15 | --- 16 | ---And to activate extended physics, use: 17 | ---```ini 18 | ---[SURFACE_0] 19 | ---WAV_PITCH=extended-0 20 | ---``` 21 | physics = {} 22 | -------------------------------------------------------------------------------- /common/ac_positioning_helper.lua: -------------------------------------------------------------------------------- 1 | __source 'apps/positioning_helper.cpp' 2 | __namespace 'render' 3 | 4 | require './ac_ray' 5 | 6 | ffi.cdef [[ 7 | typedef struct { 8 | void* __car; 9 | uint __highlighted_axis_bits_; 10 | int __highlighted_rotational_axis_; 11 | bool __moving_active_; 12 | bool __rotating_active_; 13 | bool __relative_coords_; 14 | uint8_t __skipAxis; 15 | bool __forceRelative; 16 | 17 | vec2 __rotating_start_pos_; 18 | vec3 __drag_start_origin_; 19 | ray __drag_start_onscreen_; 20 | } positioning_helper; 21 | ]] 22 | 23 | ---A helper for moving things around using mouse with XYZ arrows. 24 | ---@param params {skipAxis: ('x'|'y'|'z')[], alwaysAligned: boolean?}? 25 | ---@return render.PositioningHelper 26 | function render.PositioningHelper(params) 27 | local created = ffi.new('positioning_helper') 28 | if type(params) == 'table' then 29 | if params.skipAxis then 30 | for _, v in ipairs(params.skipAxis) do 31 | created.__skipAxis = bit.bor(created.__skipAxis, v == 'x' and 1 or v == 'y' and 2 or v == 'z' and 4 or 0) 32 | end 33 | end 34 | if params.alwaysAligned then 35 | created.__forceRelative = true 36 | end 37 | end 38 | return created 39 | end 40 | 41 | ---@class render.PositioningHelper 42 | ---@explicit-constructor render.PositioningHelper 43 | ffi.metatype('positioning_helper', { 44 | __gc = ffi.C.lj_positioninghelper_gc__render, 45 | __index = { 46 | 47 | ---@param pos vec3 48 | ---@param forceInactive boolean? @Prevents PositioningHelper from moving. Default value: `false`. 49 | ---@return boolean 50 | render = function(s, pos, forceInactive) 51 | if not pos then error('Argument `pos` is required', 2) end 52 | return ffi.C.lj_positioninghelper_render__render(s, pos, forceInactive and true or false) 53 | end, 54 | 55 | ---@param pos vec3 56 | ---@param look vec3 57 | ---@param forceInactive boolean? @Prevents PositioningHelper from moving. Default value: `false`. 58 | ---@return boolean 59 | renderAligned = function(s, pos, look, forceInactive) 60 | if not pos then error('Argument `pos` is required', 2) end 61 | if not look then error('Argument `look` is required', 2) end 62 | return ffi.C.lj_positioninghelper_render_l__render(s, pos, __util.ensure_vec3(look), forceInactive and true or false) 63 | end, 64 | 65 | ---@param pos vec3 66 | ---@param look vec3 67 | ---@param up vec3 68 | ---@param forceInactive boolean? @Prevents PositioningHelper from moving. Default value: `false`. 69 | ---@return boolean 70 | renderFullyAligned = function(s, pos, look, up, forceInactive) 71 | if not pos then error('Argument `pos` is required', 2) end 72 | if not look then error('Argument `look` is required', 2) end 73 | if not up then error('Argument `up` is required', 2) end 74 | return ffi.C.lj_positioninghelper_render_lu__render(s, pos, __util.ensure_vec3(look), __util.ensure_vec3(up), forceInactive and true or false) 75 | end, 76 | 77 | ---@return boolean 78 | anyHighlight = ffi.C.lj_positioninghelper_anyhighlight__render, 79 | 80 | ---@return boolean 81 | movingInScreenSpace = ffi.C.lj_positioninghelper_movinginscreenspace__render, 82 | } }) 83 | -------------------------------------------------------------------------------- /common/ac_primitive.lua: -------------------------------------------------------------------------------- 1 | ffi.cdef [[ 2 | typedef struct { float x, y; } vec2; 3 | typedef struct { float x, y, z; } vec3; 4 | typedef struct { float x, y, z, w; } vec4; 5 | typedef struct { float r, g, b; } rgb; 6 | typedef struct { float h, s, v; } hsv; 7 | typedef struct { union { struct { float r, g, b; }; struct { rgb rgb; }; }; float mult; } rgbm; 8 | typedef struct { float x, y, z, w; } quat; 9 | ]] 10 | 11 | --[[? if (!ctx.ldoc) for (let TYPE of ['vec2', 'vec3', 'vec4', 'rgb', 'hsv', 'rgbm', 'quat']) { 12 | 13 | function buildMetaTable(T, INDEX_TABLE){ 14 | if (/__tostring/.test(INDEX_TABLE) && /__call/.test(INDEX_TABLE)) return INDEX_TABLE; 15 | 16 | const dims = +T[3]; 17 | const F = ['x', 'y', 'z', 'w'].slice(0, dims); 18 | const M = (s, j) => F.map(x => s.replace(/\$/g, x)).join(j || ', '); 19 | const C = s => T + '(' + F.map(x => s.replace(/\$/g, x)) + ')'; 20 | const I = s => `ffi.istype(ct${T}, ${s})`; 21 | const O = s => `function(v, u) return type(v) == 'number' and ${C(`v${s}u.$`)} or ${I('u')} and ${C(`v.$${s}u.$`)} or ${C(`v.$${s}u`)} end`; 22 | 23 | return `{ 24 | __call = function(_, ${F}) return ct${T}(${M('$ or 0')}) end, 25 | __tostring = function(v) return string.format('(${M('%s')})', ${M('v.$')}) end, 26 | __add = ${O('+')}, 27 | __sub = ${O('-')}, 28 | __mul = ${O('*')}, 29 | __div = ${O('/')}, 30 | __pow = ${O('^')}, 31 | __unm = function(v) return ${C('-v.$')} end, 32 | __len = function(v) return v:length() end, 33 | __eq = function(v, o) return ${I('v')} and ${I('o')} and ${M('v.$ == o.$', ' and ')} end, 34 | __lt = function(v, o) return ${I('v')} and ${I('o')} and ${M('v.$ < o.$', ' and ')} end, 35 | __le = function(v, o) return ${I('v')} and ${I('o')} and ${M('v.$ <= o.$', ' and ')} end, 36 | __index = ${INDEX_TABLE} 37 | }`; 38 | } 39 | 40 | const src = ('' + fs.readFileSync('common/ac_primitive_' + TYPE + '.lua')).split(';--[[]' + ']_G()').map(x => x.trim()); 41 | // out(TYPE + ' = nil\ndo '); 42 | out('do '); 43 | out(src[0] + '\n'); 44 | out(TYPE + ' = ffi.metatype(ct' + TYPE + ', ' + buildMetaTable(TYPE, src[1].replace()) + ')\n'); 45 | out(src[2] + ' end\n'); } ?]] 46 | 47 | smoothing = setmetatable({}, { 48 | __call = function(_, v, s) 49 | return __util.lazy('lib_smoothing').c(v, s) 50 | end, 51 | __index = { 52 | setDT = function(dt) 53 | return __util.lazy('lib_smoothing').s(dt) 54 | end 55 | } 56 | }) 57 | -------------------------------------------------------------------------------- /common/ac_primitive_hsv.d.lua: -------------------------------------------------------------------------------- 1 | ---Creates new instance. It’s usually faster to create a new item with `hsv(h, s, v)`. 2 | ---@param h number? 3 | ---@param s number? 4 | ---@param v number? 5 | ---@return hsv 6 | function hsv.new(h, s, v) end 7 | 8 | ---Checks if value is hsv or not. 9 | ---@param p any 10 | ---@return boolean 11 | function hsv.ishsv(p) end 12 | 13 | ---Temporary HSV color. For most cases though, it might be better to define those locally and use those. Less chance of collision. 14 | ---@return hsv 15 | function hsv.tmp() end 16 | 17 | ---HSV color (hue, saturation, value). Equality operator is overloaded. 18 | ---@class hsv 19 | ---@field h number 20 | ---@field s number 21 | ---@field v number 22 | ---@constructor fun(h: number?, s: number?, v: number?): hsv 23 | 24 | ---Makes a copy of a vector. 25 | ---@return hsv 26 | function hsv:clone() end 27 | 28 | ---Unpacks hsv into three numbers. 29 | ---@return rgb, number 30 | function hsv:unpack() end 31 | 32 | ---Turns hsv into a table with three numbers. 33 | ---@return number[] 34 | function hsv:table() end 35 | 36 | ---Returns reference to hsv class. 37 | function hsv:type() end 38 | 39 | ---@param h number 40 | ---@param s number 41 | ---@param v number 42 | ---@return hsv @Returns itself. 43 | function hsv:set(h, s, v) end 44 | 45 | ---Returns RGB color. 46 | ---@return rgb 47 | function hsv:rgb() end 48 | -------------------------------------------------------------------------------- /common/ac_primitive_hsv.lua: -------------------------------------------------------------------------------- 1 | local function hsvToRgb(t) 2 | local v = t.v 3 | if t.s <= 0 then return rgb(v, v, v) end 4 | 5 | local e = t.h 6 | if e >= 360 then e = 0 end 7 | 8 | e = e / 60 9 | local i = math.floor(e) 10 | local u = e - i 11 | local p = v * (1 - t.s) 12 | local q = v * (1 - t.s * u) 13 | local t = v * (1 - t.s * (1 - u)) 14 | 15 | if i == 0 then return rgb(v, t, p) end 16 | if i == 1 then return rgb(q, v, p) end 17 | if i == 2 then return rgb(p, v, t) end 18 | if i == 3 then return rgb(p, q, v) end 19 | if i == 4 then return rgb(t, p, v) end 20 | return rgb(v, p, q) 21 | end 22 | 23 | local vHtmp1 24 | local cthsv = ffi.typeof('hsv') 25 | 26 | ;--[[]]_G() 27 | 28 | { 29 | __call = function(_, h, s, v) 30 | return setmetatable({ h = h or 0, s = s or 0, v = v or 0 }, hsv) 31 | end, 32 | 33 | __tostring = function(v) 34 | return string.format('(H=%f, S=%f, V=%f)', v.h, v.s, v.v) 35 | end, 36 | 37 | __eq = function(v, o) if rawequal(o, nil) or rawequal(v, nil) then return rawequal(v, o) end return ffi.istype(cthsv, v) and ffi.istype(cthsv, o) and v.h == o.h and v.s == o.s and v.v == o.v end, 38 | __index = { 39 | new = function(h, s, v) 40 | if type(h) ~= 'number' then h = 0 end 41 | if type(s) ~= 'number' then s = h end 42 | if type(v) ~= 'number' then v = s end 43 | return hsv(h, s, v) 44 | end, 45 | 46 | ishsv = function(self) return ffi.istype(cthsv, self) end, 47 | tmp = function() return vHtmp1 end, 48 | type = function() return hsv end, 49 | clone = function(self) return hsv(self.h, self.s, self.v) end, 50 | unpack = function(self) return self.h, self.s, self.v end, 51 | table = function(self) return {self.h, self.s, self.v} end, 52 | 53 | set = function(self, h, s, v) 54 | if hsv.ishsv(h) then h, s, v = h.h, h.s, h.v end 55 | self.h = h 56 | self.s = s 57 | self.v = v 58 | return self 59 | end, 60 | 61 | rgb = hsvToRgb, 62 | toRgb = hsvToRgb, 63 | } 64 | } 65 | 66 | ;--[[]]_G() 67 | 68 | vHtmp1 = hsv() -------------------------------------------------------------------------------- /common/ac_primitive_quat.d.lua: -------------------------------------------------------------------------------- 1 | ---Creates new quaternion. It’s usually faster to create a new item with `quat(x, y, z, w)` directly, but the way LuaJIT works, 2 | ---that call only works with four numbers. If you only provide a single number, rest will be set to 0. This call, however, supports 3 | ---various calls (which also makes it slightly slower). 4 | ---@overload fun(value: quat): quat 5 | ---@overload fun(tableOfFour: number[]): quat 6 | ---@overload fun(value: number): quat 7 | ---@param x number? 8 | ---@param y number? 9 | ---@param z number? 10 | ---@param w number? 11 | ---@return quat 12 | function quat.new(x, y, z, w) end 13 | 14 | ---Checks if value is quat or not. 15 | ---@param p any 16 | ---@return boolean 17 | function quat.isquat(p) end 18 | 19 | ---Creates a new quaternion. 20 | ---@param angle number @In radians. 21 | ---@param x vec3|number 22 | ---@param y number? 23 | ---@param z number? 24 | ---@return quat 25 | function quat.fromAngleAxis(angle, x, y, z) end 26 | 27 | ---Creates a new quaternion. 28 | ---@param x vec3|number 29 | ---@param y number? 30 | ---@param z number? 31 | ---@return quat 32 | function quat.fromDirection(x, y, z) end 33 | 34 | ---Creates a new quaternion. 35 | ---@param u quat 36 | ---@param v quat 37 | ---@return quat 38 | function quat.between(u, v) end 39 | 40 | ---Temporary quaternion. For most cases though, it might be better to define those locally and use those. Less chance of collision. 41 | ---@return quat 42 | function quat.tmp() end 43 | 44 | ---Quaternion. All operators are overloaded. 45 | ---@class quat 46 | ---@field x number 47 | ---@field y number 48 | ---@field z number 49 | ---@field w number 50 | ---@constructor fun(x: number?, y: number?, z: number?, w: number?): quat 51 | 52 | ---Makes a copy of a quaternion. 53 | ---@return quat 54 | function quat:clone() end 55 | 56 | ---Unpacks quat into four numbers. 57 | ---@return number, number, number, number 58 | function quat:unpack() end 59 | 60 | ---Turns quat into a table with four values. 61 | ---@return number[] 62 | function quat:table() end 63 | 64 | ---Returns reference to quat class. 65 | function quat:type() end 66 | 67 | ---@param x quat|number 68 | ---@param y number? 69 | ---@param z number? 70 | ---@param w number? 71 | ---@return quat @Returns itself. 72 | function quat:set(x, y, z, w) end 73 | 74 | ---@param angle number @In radians. 75 | ---@param x vec3|number 76 | ---@param y number? 77 | ---@param z number? 78 | ---@return quat @Returns itself. 79 | function quat:setAngleAxis(angle, x, y, z) end 80 | 81 | ---@return number @Angle in radians. 82 | ---@return number @Axis, X. 83 | ---@return number @Axis, Y. 84 | ---@return number @Axis, Z. 85 | function quat:getAngleAxis() end 86 | 87 | ---@param u quat 88 | ---@param v quat 89 | ---@return quat @Returns itself. 90 | function quat:setBetween(u, v) end 91 | 92 | ---@param x vec3|number 93 | ---@param y number? 94 | ---@param z number? 95 | ---@return quat @Returns itself. 96 | function quat:setDirection(x, y, z) end 97 | 98 | ---@param valueToAdd quat|number 99 | ---@param out quat|nil @Optional destination argument. 100 | ---@return quat @Returns itself or out value. 101 | function quat:add(valueToAdd, out) end 102 | 103 | ---@param valueToSubtract quat|number 104 | ---@param out quat|nil @Optional destination argument. 105 | ---@return quat @Returns itself or out value. 106 | function quat:sub(valueToSubtract, out) end 107 | 108 | ---@param valueToMultiplyBy quat 109 | ---@param out quat|nil @Optional destination argument. 110 | ---@return quat @Returns itself or out value. 111 | function quat:mul(valueToMultiplyBy, out) end 112 | 113 | ---@param multiplier number 114 | ---@param out quat|nil @Optional destination argument. 115 | ---@return quat @Returns itself or out value. 116 | function quat:scale(multiplier, out) end 117 | 118 | ---@return number 119 | function quat:length() end 120 | 121 | ---Normalizes itself (unless different `out` is provided). 122 | ---@param out quat|nil @Optional destination argument. 123 | ---@return quat @Returns itself or out value. 124 | function quat:normalize(out) end 125 | 126 | ---Rewrites own values with values of lerp of itself and other quaternion (unless different `out` is provided). 127 | ---@param otherVector quat 128 | ---@param mix number 129 | ---@param out quat|nil @Optional destination argument. 130 | ---@return quat @Returns itself or out value. 131 | function quat:lerp(otherVector, mix, out) end 132 | 133 | ---Rewrites own values with values of slerp of itself and other quaternion (unless different `out` is provided). 134 | ---@param otherVector quat 135 | ---@param mix number 136 | ---@param out quat|nil @Optional destination argument. 137 | ---@return quat @Returns itself or out value. 138 | function quat:slerp(otherVector, mix, out) end 139 | -------------------------------------------------------------------------------- /common/ac_ray.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_ray.cpp' 2 | __namespace 'render' 3 | 4 | ffi.cdef [[ 5 | typedef struct { 6 | vec3 pos; 7 | vec3 dir; 8 | vec3 _pad0; 9 | vec3 _pad1; 10 | float length; 11 | } ray; 12 | ]] 13 | 14 | local _rayVecTmp = vec3() 15 | 16 | ---Ray for simple geometric raycasting. Do not create ray manually, instead use `render.createRay(pos, dir, length)` or `render.createMouseRay()`. 17 | ---Do not alter direction directly, or, if you do, do not cast it against lines, triangles or meshes, it stores some other precomputed values 18 | ---for faster and more accurate raycasting. 19 | ---@class ray 20 | ---@field pos vec3 @Ray origin. 21 | ---@field dir vec3 @Ray direction. 22 | ---@field length number @Ray length (used for physics raycasting, shorter rays are faster). 23 | ffi.metatype('ray', { __index = { 24 | 25 | ---Ray/AABB intersection. 26 | ---@param min vec3 @AABB min corner. 27 | ---@param max vec3 @AABB max corner. 28 | ---@return boolean @True if there was an intersection. 29 | aabb = function(s, min, max) 30 | return ffi.C.lj_ray_aabb__render(s, __util.ensure_vec3(min), __util.ensure_vec3(max)) 31 | end, 32 | 33 | ---Ray/thick line intersection. 34 | ---@param from vec3 @Line, starting point. 35 | ---@param to vec3 @Line, finishing point. 36 | ---@param width number @Line width. 37 | ---@return number @Intersection distance, or -1 if there was no intersection. 38 | line = function(s, from, to, width) 39 | return ffi.C.lj_ray_line__render(s, __util.ensure_vec3(from), __util.ensure_vec3(to), width) 40 | end, 41 | 42 | ---Ray/plaane intersection. 43 | ---@param planePoint vec3 44 | ---@param planeNormal vec3 45 | ---@return number @Intersection distance, or -1 if there was no intersection. 46 | plane = function(s, planePoint, planeNormal) 47 | return ffi.C.lj_ray_plane__render(s, __util.ensure_vec3(planePoint), __util.ensure_vec3(planeNormal)) 48 | end, 49 | 50 | ---Ray/triangle intersection. 51 | ---@param p1 vec3 @Triangle, point A. 52 | ---@param p2 vec3 @Triangle, point B. 53 | ---@param p3 vec3 @Triangle, point C. 54 | ---@return number @Intersection distance, or -1 if there was no intersection. 55 | triangle = function(s, p1, p2, p3) 56 | return ffi.C.lj_ray_triangle__render(s, __util.ensure_vec3(p1), __util.ensure_vec3(p2), __util.ensure_vec3(p3)) 57 | end, 58 | 59 | ---Ray/sphere intersection. 60 | ---@param center vec3 @Sphere, center. 61 | ---@param radius number @Sphere, radius. 62 | ---@return number @Intersection distance, or -1 if there was no intersection. 63 | sphere = function(s, center, radius) 64 | return ffi.C.lj_ray_sphere__render(s, __util.ensure_vec3(center), tonumber(radius) or 0) 65 | end, 66 | 67 | ---Ray/track intersection. 68 | ---@return number @Intersection distance, or -1 if there was no intersection. 69 | track = ffi.C.lj_ray_track__render, 70 | 71 | ---Ray/scene intersection (both with track and cars). 72 | ---@return number @Intersection distance, or -1 if there was no intersection. 73 | scene = ffi.C.lj_ray_scene__render, 74 | 75 | ---Ray/cars intersection. 76 | ---@return number @Intersection distance, or -1 if there was no intersection. 77 | cars = ffi.C.lj_ray_cars__render, 78 | 79 | ---Ray/physics meshes intersection. 80 | ---@param outPosition vec3 @Optional vec3 to which contact point will be written. 81 | ---@param outNormal vec3 @Optional vec3 to which contact normal will be written. 82 | ---@return number @Intersection distance, or -1 if there was no intersection. 83 | physics = function(s, outPosition, outNormal) 84 | return ffi.C.lj_ray_physics__render(s, vec3.isvec3(outPosition) and outPosition or nil, vec3.isvec3(outNormal) and outNormal or nil) 85 | end, 86 | 87 | ---Distance between ray and a point. 88 | ---@param p vec3 @Point. 89 | ---@return number @Distance. 90 | distance = function(s, p) 91 | local v = _rayVecTmp:set(p):sub(s.pos) 92 | local t = v:dot(s.dir) 93 | return p:distance(v:set(s.dir):scale(t):add(s.pos)) 94 | end 95 | 96 | } }) 97 | 98 | -------------------------------------------------------------------------------- /common/ac_reftypes.lua: -------------------------------------------------------------------------------- 1 | ffi.cdef [[ 2 | typedef struct { bool value; } refbool; 3 | typedef struct { float value; } refnumber; 4 | ]] 5 | 6 | local ctrefbool = ffi.typeof('refbool') 7 | local ctrefnumber = ffi.typeof('refnumber') 8 | 9 | ---Stores a boolean value and can be used as a reference to it. 10 | ---@class refbool 11 | ---@field value boolean @Stored value. 12 | refbool = ffi.metatype(ctrefbool, { __index = { 13 | ---@return boolean 14 | isrefbool = function(x) return ffi.istype(ctrefbool, x) end, 15 | 16 | ---For easier use with UI controls. 17 | ---@param newValue boolean 18 | ---@return refbool 19 | set = function (s, newValue) s.value = newValue return s end 20 | }, __call = function(s) return s.value end }) 21 | 22 | ---Stores a numerical value and can be used as a reference to it. 23 | ---@class refnumber 24 | ---@field value number @Stored value. 25 | refnumber = ffi.metatype(ctrefnumber, { __index = { 26 | ---@return boolean 27 | isrefnumber = function(x) return ffi.istype(ctrefnumber, x) end, 28 | 29 | ---For easier use with UI controls. 30 | ---@param newValue number 31 | ---@return refnumber 32 | set = function (s, newValue) s.value = newValue return s end 33 | }, __call = function(s) return s.value end }) 34 | 35 | -------------------------------------------------------------------------------- /common/ac_render_shader.lua: -------------------------------------------------------------------------------- 1 | function __util.getRsData2(params, templateCache) 2 | __util.lazy('lib_shader') 3 | return __util.getRsData2(params, templateCache) 4 | end 5 | 6 | function __util.setShaderParams2(params, templateCache) 7 | __util.lazy('lib_shader') 8 | return __util.setShaderParams2(params, templateCache) 9 | end 10 | -------------------------------------------------------------------------------- /common/ac_ro_vectors.lua: -------------------------------------------------------------------------------- 1 | --[[? for (const TYPE of ['int', 'float']){ out(]] 2 | 3 | ffi.cdef [[ typedef struct { const __TYPE__* _begin; const __TYPE__* _end; const __TYPE__* _cap; } luavec___TYPE__; ]] 4 | ffi.metatype('luavec___TYPE__', { 5 | __len = function (v) 6 | return v._end - v._begin 7 | end, 8 | __ipairs = function (v) 9 | return __util.lvi, v, -1 10 | end, 11 | __tostring = function(v) 12 | local t, n = {'('}, 2 13 | for i = 0, v._end - v._begin - 1 do 14 | if n == 2 then 15 | t[n], n = v._begin[i], n + 1 16 | else 17 | t[n], t[n + 1], n = ', ', v._begin[i], n + 2 18 | end 19 | end 20 | t[n] = ')' 21 | return table.concat(t) 22 | end, 23 | __index = function(v, k) 24 | if type(k) ~= 'number' or k < 0 or v._begin + k >= v._end then return nil end 25 | return v._begin[k] 26 | end, 27 | __newindex = function() error('This list is read-only', 2) end 28 | }) 29 | 30 | --[[) } ?]] 31 | -------------------------------------------------------------------------------- /common/ac_smoothing.d.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | ---Call it once every frame at the beginning, it would affect all instances of `smoothing`. 4 | ---@param dt number @Time passed since last frame, in seconds. 5 | function smoothing.setDT(dt) end 6 | 7 | ---Strange thing which is only kept for backwards compatibility. Holds a value, numerical or a vector, and 8 | ---updates it every frame slowly moving it towards target value. For new projects, I would recommend to use 9 | ---something else. 10 | --- 11 | ---It doesn’t even use lag parameter, but instead some strange “smooth” thing… 12 | ---@class smoothing 13 | ---@field val number|vec2|vec3|vec4 14 | ---@field lastValue number|vec2|vec3|vec4 15 | ---@field smooth number 16 | ---@constructor fun(initialValue: number|vec2|vec3|vec4, smoothingIntensity: number? "Default value: 100."): smoothing 17 | 18 | ---Updates value, moving it closer to `newValue`. 19 | ---@param newValue number|vec2|vec3|vec4 @Target value to move to. 20 | function smoothing:update(newValue) end 21 | 22 | ---Updates value, moving it closer to `newValue`, but only if `newValue` is different from the one used in 23 | ---`:updateIfNew()` last time. And for vectors it compares not by value, but by reference. Makes me wonder what was I 24 | ---thinking about. 25 | ---@param newValue number|vec2|vec3|vec4 @Target value to move to. 26 | function smoothing:updateIfNew(newValue) end 27 | -------------------------------------------------------------------------------- /common/ac_social.lua: -------------------------------------------------------------------------------- 1 | ---Faster way to deal with driver tags. Any request of unsupported fields will return `false` for further extendability. 2 | ---Scripts with access to I/O can also alter fields. 3 | ---@class ac.DriverTags 4 | ---@field color rgbm @User name color. Could be derived from custom color set via `ac.DriverTags` (or `ac.setDriverChatNameColor()`), turns reddish if driver is muted, or uses custom driver tag from CM if any is set, or turns greenish if driver is marked as a friend (in CSP or CM). For the player entry it will always be yellow. 5 | ---@field friend boolean @Friend tag, uses CSP and CM databases. 6 | ---@field muted boolean @Muted tag, if set messages in chat will be hidden. 7 | ---@constructor fun(driverName: string): ac.DriverTags 8 | function ac.DriverTags(driverName) 9 | __util.lazy('lib_social') 10 | return ac.DriverTags(driverName) 11 | end 12 | 13 | ---Checks if a user is tagged as a friend. Uses CSP and CM databases. Deprecated, use `ac.DriverTags` instead. 14 | ---@deprecated 15 | ---@param driverName string @Driver name. 16 | ---@return boolean 17 | function ac.isTaggedAsFriend(driverName) 18 | __util.lazy('lib_social') 19 | return ac.isTaggedAsFriend(driverName) 20 | end 21 | 22 | ---Tags user as a friend (or removes the tag if `false` is passed). Deprecated, use `ac.DriverTags` instead. 23 | ---@deprecated 24 | ---@param driverName string @Driver name. 25 | ---@param isFriend boolean? @Default value: `true`. 26 | function ac.tagAsFriend(driverName, isFriend) 27 | __util.lazy('lib_social') 28 | return ac.tagAsFriend(driverName, isFriend) 29 | end 30 | -------------------------------------------------------------------------------- /common/ac_state.d.lua: -------------------------------------------------------------------------------- 1 | ---Returns reference to a structure with various information about the state of a car. Very cheap to use. 2 | ---This is a new version with shorter name and 0-based indexing (to match other API functions). 3 | --- 4 | ---Updates once per graphics frame. You can use it in physics scripts to access things such as tyre radius, but 5 | ---for anything live there please look for specialized physics-rate updating values. 6 | --- 7 | ---Note: index starts with 0. Make sure to check result for `nil` if you’re accessing a car that might not be there. First car 8 | ---with index 0 is always there. 9 | ---@param index integer @0-based index. 10 | ---@return ac.StateCar? 11 | function ac.getCar(index) end 12 | 13 | ---Returns Nth closest to camera car (pass 0 to get an ID of the nearest car). Inactive cars don’t count, so the number of cars 14 | ---here might be smaller than total number of cars in the race. 15 | ---@param index integer @0-based index. 16 | ---@return ac.StateCar? 17 | function ac.getCar.ordered(index) end 18 | 19 | ---Returns Nth car in the race leaderboard (uses lap times in practice and qualify sessions). Pass 0 to get the top one. 20 | ---@param index integer @0-based index. 21 | ---@return ac.StateCar? 22 | function ac.getCar.leaderboard(index) end 23 | 24 | ---Returns Nth car in server entry list. Pass 0 to get the first one. In offline races returns `nil`. 25 | ---@param index integer @0-based index. 26 | ---@return ac.StateCar? 27 | function ac.getCar.serverSlot(index) end 28 | 29 | ---Iterates over all the cars from one with 0th index to the last one. Use in a for-loop. To get a Nth car, use `ac.getCar()`. 30 | --- 31 | ---Example: 32 | ---``` 33 | ---for i, c in ac.iterateCars() do 34 | --- ac.debug(i, car.position) 35 | ---end 36 | ---``` 37 | ---@return fun(): integer, ac.StateCar @Iterator to be used in a loop (1-based index and car state) 38 | function ac.iterateCars() end 39 | 40 | ---Iterates over active cars (excluding disconnected ones online) from nearest to furthest. Use in a for-loop. To get a Nth car, use `ac.getCar.ordered()`. 41 | --- 42 | ---Example: 43 | ---``` 44 | ---for i, c in ac.iterateCars.ordered() do 45 | --- ac.debug(i, car.position) 46 | ---end 47 | ---``` 48 | ---@param inverse boolean? @Set to `true` to iterate in inverse order (available since 0.2.5). 49 | ---@return fun(): integer, ac.StateCar @Iterator to be used in a loop (1-based index and car state) 50 | function ac.iterateCars.ordered(inverse) end 51 | 52 | ---Iterates over cars from first to last in the race leaderboard (uses lap times in practice and qualify sessions). Use in a for-loop. To get a Nth car, use `ac.getCar.leaderboard()`. 53 | --- 54 | ---Example: 55 | ---``` 56 | ---for i, c in ac.iterateCars.leaderboard() do 57 | --- ac.debug(i, car.position) 58 | ---end 59 | ---``` 60 | ---@return fun(): integer, ac.StateCar @Iterator to be used in a loop (1-based index and car state) 61 | function ac.iterateCars.leaderboard() end 62 | 63 | ---Iterates over cars based on their `sessionID` (index of a session slot). Use in a for-loop. To get a Nth car, use `ac.getCar.serverSlot()`. In offline races 64 | ---returns an empty iterator. 65 | --- 66 | ---Example: 67 | ---``` 68 | ---for i, c in ac.iterateCars.leaderboard() do 69 | --- ac.debug(i, car.position) 70 | ---end 71 | ---``` 72 | ---@return fun(): integer, ac.StateCar @Iterator to be used in a loop (1-based index and car state) 73 | function ac.iterateCars.serverSlots() end 74 | -------------------------------------------------------------------------------- /common/ac_state.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_state_provider.cpp' 2 | __states 'lua/api_state_provider.cpp' 3 | 4 | do 5 | local function _ptri(v, i) 6 | if v[i] ~= nil then return i + 1, v[i] end 7 | end 8 | 9 | ac.iterateCars = setmetatable({ 10 | ordered = function (inverse) 11 | return _ptri, ffi.C.lj_getCars_inner(inverse and 4 or 1), 0 12 | end, 13 | leaderboard = function () 14 | return _ptri, ffi.C.lj_getCars_inner(2), 0 15 | end, 16 | serverSlot = function () 17 | -- for compatibility, there was a typo 18 | return _ptri, ffi.C.lj_getCars_inner(3), 0 19 | end, 20 | serverSlots = function () 21 | return _ptri, ffi.C.lj_getCars_inner(3), 0 22 | end, 23 | }, { 24 | __call = function(_) 25 | return _ptri, ffi.C.lj_getCars_inner(0), 0 26 | end 27 | }) 28 | 29 | local _cc = {} 30 | ac.getCar = setmetatable({ 31 | ordered = function (index) 32 | return __util.secure_state(ffi.C.lj_getCar_innerord(1, tonumber(index) or 0)) 33 | end, 34 | leaderboard = function (index) 35 | return __util.secure_state(ffi.C.lj_getCar_innerord(2, tonumber(index) or 0)) 36 | end, 37 | serverSlot = function (index) 38 | return __util.secure_state(ffi.C.lj_getCar_innerord(3, tonumber(index) or 0)) 39 | end, 40 | serverSlots = function (index) 41 | -- for compatibility, there was a typo 42 | return __util.secure_state(ffi.C.lj_getCar_innerord(3, tonumber(index) or 0)) 43 | end, 44 | }, { 45 | __call = function(_, index) 46 | local k = tonumber(index) or 0 47 | local r = _cc[k] 48 | if not r then 49 | r = __util.secure_state(ffi.C.lj_getCar_inner(k)) 50 | _cc[k] = r 51 | end 52 | return r 53 | end 54 | }) 55 | end 56 | 57 | ---@class ac.StateCar 58 | ffi.metatype('state_car', { __index = { 59 | ---@return string 60 | skin = function (s) return ac.getCarSkinID(s.index) end, 61 | 62 | ---@return string 63 | tyresName = function (s) return ac.getTyresName(s.index) end, 64 | 65 | ---@return string 66 | tyresLongName = function (s) return ac.getTyresLongName(s.index) end, 67 | 68 | ---@return string 69 | id = function (s) return ac.getCarID(s.index) end, 70 | 71 | ---@return string 72 | name = function (s) return ac.getCarName(s.index) end, 73 | 74 | ---@return string 75 | brand = function (s) return ac.getCarBrand(s.index) end, 76 | 77 | ---@return string 78 | country = function (s) return ac.getCarCountry(s.index) end, 79 | 80 | ---@return string 81 | driverName = function (s) return ac.getDriverName(s.index) end, 82 | 83 | ---@return string 84 | driverNationCode = function (s) return ac.getDriverNationCode(s.index) end, 85 | 86 | ---@return string 87 | driverNationality = function (s) return ac.getDriverNationality(s.index) end, 88 | 89 | ---@return string 90 | driverTeam = function (s) return ac.getDriverTeam(s.index) end, 91 | 92 | ---@return string 93 | driverNumber = function (s) return ac.getDriverNumber(s.index) end, 94 | } }) -------------------------------------------------------------------------------- /common/ac_storage.d.lua: -------------------------------------------------------------------------------- 1 | ---@class ac.StoredValue 2 | local _ac_StoredValue = {} 3 | 4 | ---@return string|number|boolean|vec2|vec3|vec4|rgb|rgbm 5 | function _ac_StoredValue:get() end 6 | 7 | ---@param value string|number|boolean|vec2|vec3|vec4|rgb|rgbm 8 | function _ac_StoredValue:set(value) end 9 | 10 | ---Storage function. Easiest way to use is to pass it a table with default values — it would give you a table back 11 | ---which would load values on reads and save values on writes. Values have to be either strings, numbers, booleans, 12 | ---vectors or colors. Example: 13 | ---``` 14 | ---local storedValues = ac.storage{ 15 | --- someKey = 15, 16 | --- someStringValue = 20 17 | ---} 18 | ---storedValues.someKey = 20 19 | ---``` 20 | ---Alternatively, you can use it as a function which would take a key and a default value and return you an 21 | ---`ac.StoredValue` wrapper with methods `:get()` and `:set(newValue)`: 22 | ---``` 23 | ---local stored = ac.storage('someKey', 15) 24 | ---stored:get() 25 | ---stored:set(20) 26 | ---``` 27 | ---Or, just access it directly in `localStorage` style of JavaScript. Similar to JavaScript, this way you can only store 28 | ---strings: 29 | ---``` 30 | ---ac.storage.key = 'value' 31 | ---ac.debug('loaded', ac.storage.key) 32 | ---``` 33 | ---Data will be saved in “Documents\Assetto Corsa\cfg\extension\state\lua”, in corresponding subfolder. Actual writing 34 | ---will happen a few seconds after new value was pushed, and only if value was changed, so feel free to use this function 35 | ---to write things often. 36 | ---@generic T 37 | ---@param layout T 38 | ---@param keyPrefix string|nil @Optional parameter for adding a prefix to keys. 39 | ---@return T 40 | ---@overload fun(key: string, value: string|number|boolean|vec2|vec3|vec4|rgb|rgbm): ac.StoredValue 41 | function ac.storage(layout, keyPrefix) end 42 | -------------------------------------------------------------------------------- /common/ac_track.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/track_adjustments/track_scriptable_display.cpp' 2 | __allow 'tsd' 3 | 4 | ---Finds a car at a given place in a race, for creating leaderboards. Returns nil if couldn’t find a car. 5 | ---@param place integer @Starts with 1 for first place. 6 | ---@return ac.StateCar|nil 7 | function ac.findCarAtPlace(place) 8 | for i = 0, ac.getSim().carsCount - 1 do -- getCar() needs IDs from 0 to N-1 9 | local car = ac.getCar(i) 10 | if car.racePosition == place then return car end 11 | end 12 | return nil -- couldn’t find anything 13 | end 14 | -------------------------------------------------------------------------------- /common/ac_track_conditions.d.lua: -------------------------------------------------------------------------------- 1 | ---Creates a wrapper to access track condition. If you want to get the value often, consider caching and reusing the wrapper. 2 | ---@param expression string @Expression similar to ones config have as CONDITION=… value. 3 | ---@param offset number? @Condition offset. Default value: 0. 4 | ---@param defaultValue number? @Default value in case referenced condition is missing or parsing failed. Default value: 0. 5 | ---@return ac.TrackCondition 6 | function ac.TrackCondition(expression, offset, defaultValue) end 7 | 8 | ---Returns number of conditions defined on the current track. 9 | ---@return integer 10 | function ac.TrackCondition.count() end 11 | 12 | ---Returns name of a condition with certain index. 13 | ---@param index integer @0-based condition index. 14 | ---@return string? @Returns `nil` if there is no such condition. 15 | function ac.TrackCondition.name(index) end 16 | 17 | ---Returns input of a condition with certain index. 18 | ---@param index integer @0-based condition index. 19 | ---@return string? @Returns `nil` if there is no such condition. 20 | function ac.TrackCondition.input(index) end 21 | 22 | ---Returns value of a condition with certain index. 23 | ---@param index integer @0-based condition index. 24 | ---@param offset number? @Optional offset for conditions with variance. 25 | ---@return number @Returns `0` if there is no such condition. 26 | function ac.TrackCondition.get(index, offset) end 27 | 28 | ---Returns RGB value of a condition with certain index. 29 | ---@param index integer @0-based condition index. 30 | ---@param offset number? @Optional offset for conditions with variance. 31 | ---@return rgb @Returns `rgb()` if there is no such condition. 32 | function ac.TrackCondition.getColor(index, offset) end 33 | 34 | -------------------------------------------------------------------------------- /common/ac_track_conditions.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_track_conditions.cpp' 2 | 3 | ffi.cdef [[ 4 | typedef struct { 5 | void* data__; 6 | } trackcondition; 7 | ]] 8 | 9 | ac.TrackCondition = setmetatable({ 10 | count = function () 11 | return ffi.C.lj_trackcondition_count() 12 | end, 13 | name = function (i) 14 | return __util.strrefp(ffi.C.lj_trackcondition_at_name(tonumber(i) or -1)) 15 | end, 16 | input = function (i) 17 | return __util.strrefp(ffi.C.lj_trackcondition_at_input(tonumber(i) or -1)) 18 | end, 19 | get = function (i, offset) 20 | return ffi.C.lj_trackcondition_at_value(tonumber(i) or -1, tonumber(offset) or 0) 21 | end, 22 | getColor = function (i, offset) 23 | return ffi.C.lj_trackcondition_at_value3(tonumber(i) or -1, tonumber(offset) or 0) 24 | end, 25 | }, { 26 | __call = function(_, expression, offset, defaultValue) 27 | if type(expression) == 'number' then 28 | return ffi.gc(ffi.C.lj_trackcondition_new_id(expression, tonumber(offset) or 0, tonumber(defaultValue) or 0), ffi.C.lj_trackcondition_gc) 29 | end 30 | return ffi.gc(ffi.C.lj_trackcondition_new(__util.str(expression), tonumber(offset) or 0, tonumber(defaultValue) or 0), ffi.C.lj_trackcondition_gc) 31 | end 32 | }) 33 | 34 | ---Track condition evaluator. Given expression, which might refer to some existing condition, condition input or a complex expression of those, 35 | ---computes the resulting value. 36 | ---@class ac.TrackCondition 37 | ffi.metatype('trackcondition', { __index = { 38 | 39 | ---@param offset number? @Optional offset (in case track condition has a variance). 40 | ---@return number 41 | get = function (s, offset) return ffi.C.lj_trackcondition_get(s, tonumber(offset) or 0) end, 42 | 43 | ---@param offset number? @Optional offset (in case track condition has a variance). 44 | ---@return rgb 45 | getColor = function (s, offset) return ffi.C.lj_trackcondition_getcolor(s, tonumber(offset) or 0) end, 46 | 47 | ---Returns `true` if there is a condition with value changing live. 48 | ---@return boolean 49 | isDynamic = function (s) return ffi.C.lj_trackcondition_isdynamic(s) end, 50 | 51 | ---Returns condition input, if any. 52 | ---@return string? 53 | input = function (s) return __util.strrefp(ffi.C.lj_trackcondition_input(s)) end, 54 | 55 | ---Returns condition name, if any. 56 | ---@return string? 57 | name = function (s) return __util.strrefp(ffi.C.lj_trackcondition_name(s)) end, 58 | 59 | } }) 60 | -------------------------------------------------------------------------------- /common/ac_weatherconditions.lua: -------------------------------------------------------------------------------- 1 | __source 'extensions/weather_fx/ac_ext_weather_fx__luacommon.h' 2 | 3 | ffi.cdef [[ 4 | typedef struct { 5 | float ambient, road; 6 | } weather_conditions_temperatures; 7 | 8 | typedef struct { 9 | float sessionStart, sessionTransfer, randomness, lapGain; 10 | } weather_conditions_track; 11 | 12 | typedef struct { 13 | float direction, speedFrom, speedTo; 14 | } weather_conditions_wind; 15 | 16 | typedef struct { 17 | weather_type currentType; 18 | weather_type upcomingType; 19 | weather_conditions_temperatures temperatures; 20 | weather_conditions_track trackState; 21 | weather_conditions_wind wind; 22 | float transition; 23 | float humidity, pressure; 24 | float variableA, variableB, variableC; 25 | float rainIntensity, rainWetness, rainWater; 26 | } weather_conditions; 27 | ]] 28 | 29 | ---State of the track surface. 30 | ---@class ac.TrackConditions 31 | ---@field sessionStart number @From 0 to 100. 32 | ---@field sessionTransfer number @From 0 to 100. 33 | ---@field randomness number 34 | ---@field lapGain number 35 | ac.TrackConditions = ffi.metatype('weather_conditions_track', { __index = {} }) 36 | 37 | ---@class ac.TemperatureParams 38 | ---@field ambient number @Temperature in C°. 39 | ---@field road number @Temperature in C°. 40 | ac.TemperatureParams = ffi.metatype('weather_conditions_temperatures', { __index = {} }) 41 | 42 | ---@class ac.WindParams 43 | ---@field direction number @Direction in degrees. 44 | ---@field speedFrom number @Speed in km/h. 45 | ---@field speedTo number @Speed in km/h. 46 | ac.WindParams = ffi.metatype('weather_conditions_wind', { __index = {} }) 47 | 48 | ---@class ac.ConditionsSet 49 | ---@field currentType ac.WeatherType 50 | ---@field upcomingType ac.WeatherType 51 | ---@field temperatures ac.TemperatureParams 52 | ---@field trackState ac.TrackConditions 53 | ---@field wind ac.WindParams 54 | ---@field transition number @From 0 to 1 (if you’re doing linear transition, better to apply `math.smoothstep()` function to this value). 55 | ---@field humidity number @From 0 to 1, 1 for 100% humidity. 56 | ---@field pressure number @Pressure In pascals. 57 | ---@field variableA number @Custom value for extra data to exchange between controller and implementation (please remember that controller can be swapped with a different one). 58 | ---@field variableB number @Custom value for extra data to exchange between controller and implementation (please remember that controller can be swapped with a different one). 59 | ---@field variableC number @Custom value for extra data to exchange between controller and implementation (please remember that controller can be swapped with a different one). 60 | ---@field rainIntensity number @From 0 to 1, 0.5 for a good heavy-ish rain, everything above is for more absurd thunderstorms. 61 | ---@field rainWetness number @From 0 to 1, quickly goes to 1 as rain starts pouring, quickly goes to 0 when rain stops. 62 | ---@field rainWater number @Amount of puddles, should slowly move towards rainIntensity value. 63 | ac.ConditionsSet = ffi.metatype('weather_conditions', { __index = {} }) 64 | -------------------------------------------------------------------------------- /common/ac_web.d.lua: -------------------------------------------------------------------------------- 1 | ---@class WebResponse 2 | ---@field status integer 3 | ---@field headers table 4 | ---@field body string 5 | local _webResponse = {} 6 | 7 | ---Two possible ways to present payload: either as a string with data, or a table with a key `'filename'`. 8 | ---Second way can be used as a shortcut for `io.loadAsync()` (it loads data asyncronously). 9 | ---Data string can contain zeroes. 10 | ---@alias WebPayload string|{filename: string} 11 | -------------------------------------------------------------------------------- /common/common.lua: -------------------------------------------------------------------------------- 1 | -- API available to all scripts (but not available within `const()` preprocessing). 2 | 3 | ac.skipSaneChecks = function() end 4 | 5 | ---Stores value in session shared Lua/Python storage. This is not a long-term storage, more of a way for 6 | ---different scripts to exchange data. Note: if you need to exchange a lot of data between Lua scripts, 7 | ---consider using ac.connect instead. 8 | --- 9 | ---Data string can contain zeroes. 10 | ---@param key string 11 | ---@param value string|number 12 | function ac.store(key, value) 13 | key = tostring(key or "") 14 | if type(value) == 'number' then 15 | ffi.C.lj_store_number(key, value) 16 | else 17 | ffi.C.lj_store_string(key, value and __util.blob(value) or nil) 18 | end 19 | end 20 | 21 | ---Reads value from session shared Lua/Python storage. This is not a long-term storage, more of a way for 22 | ---different scripts to exchange data. Note: if you need to exchange data between Lua scripts, 23 | ---use `ac.connect()` instead. And if despite that you need to exchange data between car scripts, make sure to add 24 | ---car index to the key. 25 | ---@param key string 26 | ---@return nil|string|number 27 | function ac.load(key) 28 | key = tostring(key or "") 29 | if ffi.C.lj_has_number(key) then 30 | return ffi.C.lj_load_number(key) 31 | else 32 | return __util.strrefp(ffi.C.lj_load_string(key)) 33 | end 34 | end 35 | 36 | do 37 | 38 | local releaseCallbacks = nil 39 | 40 | local function preventReleaseCallback(callback) 41 | table.removeItem(releaseCallbacks, callback) 42 | end 43 | 44 | ---Adds a callback which might be called when script is unloading. Use it for some state reversion, but 45 | ---don’t rely on it too much. For example, if Assetto Corsa would crash or just close rapidly, it would not 46 | ---be called. It should be called when scripts reload though. 47 | ---@generic T 48 | ---@param callback fun(item: T) 49 | ---@param item T? @Optional parameter. If provided, will be passed to callback on release, but stored with a weak reference, so it could still be GCed before that (in that case, callback won’t be called at all). 50 | ---@return fun() @Call to disable callback. 51 | function ac.onRelease(callback, item) 52 | if item then 53 | local callbackBak, store = callback, setmetatable({item}, {__mode = 'v'}) 54 | callback = function () 55 | if store[1] then callbackBak(store[1]) end 56 | end 57 | end 58 | if not releaseCallbacks then releaseCallbacks = {} end 59 | table.insert(releaseCallbacks, callback) 60 | return function () 61 | preventReleaseCallback(callback) 62 | end 63 | end 64 | 65 | function __script.release() 66 | if releaseCallbacks then 67 | local p = {} 68 | preventReleaseCallback = function (callback) 69 | table.insert(p, callback) 70 | end 71 | for i = 1, #releaseCallbacks do 72 | if not table.contains(p, releaseCallbacks[i]) then 73 | local s, err = pcall(releaseCallbacks[i]) 74 | if not s then 75 | ac.warn('onRelease exception: '..tostring(err)) 76 | ffi.C.lj_critical_assert() 77 | end 78 | end 79 | end 80 | end 81 | end 82 | 83 | end 84 | 85 | ---For easy import of scripts from subdirectories. Provide it a name of a directory relative 86 | ---to main script folder and it would add that directory to paths it searches for. 87 | ---@param dir string 88 | function package.add(dir) 89 | package.path = package.path .. ';' .. __dirname .. '/' .. dir .. '/?.lua' 90 | package.cpath = package.cpath .. ';' .. __dirname .. '/' .. dir .. '/?.dll' 91 | end 92 | 93 | ---Sets a callback which will be called when server welcome message and extended config arrive. 94 | ---@param callback fun(message: string, config: ac.INIConfig) @Callback function. 95 | ---@return ac.Disposable 96 | function ac.onOnlineWelcome(callback) 97 | __util.callable(callback) 98 | return __util.disposable(ffi.C.lj_onOnlineWelcome_inner(__util.setCallback(function (message, config) 99 | callback(message, ac.INIConfig(ac.INIFormat.Extended, config)) 100 | end))) 101 | end 102 | -------------------------------------------------------------------------------- /common/const.lua: -------------------------------------------------------------------------------- 1 | ---Does nothing, but with preprocessing optimizations inlines value as constant. 2 | ---@generic T 3 | ---@param value T 4 | ---@return T 5 | function const(value) 6 | return value 7 | end 8 | -------------------------------------------------------------------------------- /common/debug.d.lua: -------------------------------------------------------------------------------- 1 | ---Displays value in Lua Debug app, great for tracking state of your values live. 2 | ---@param key string 3 | ---@param value any? 4 | ---@overload fun(key: string, value: number, min: number?, max: number?, collect: integer?, collectMode: ac.DebugCollectMode?) @Variant with fixed range for a graph in Lua Debug app. Set `collect` to a value above 1 if you need Lua Debug App to combine a few values so that graph would move slower. Parameter `collectMode` can specify the way in which values will be combined. 5 | function ac.debug(key, value) end -------------------------------------------------------------------------------- /common/debug.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_common_debug.cpp' 2 | 3 | --[[? if (ctx.ldoc) out(]] 4 | 5 | ---Prints a message to a CSP log and to Lua App Debug log. To speed things up and only use Lua Debug app, call `ac.setLogSilent()`. 6 | ---@param ... string|number|boolean @Values. 7 | function ac.log(...) 8 | -- ffi.C.lj_log_inner(_logargs(', ', ...)) 9 | end 10 | 11 | ---Prints a warning message to a CSP log and to Lua App Debug log. To speed things up and only use Lua Debug app, call `ac.setLogSilent()`. 12 | ---@param ... string|number|boolean @Values. 13 | function ac.warn(...) 14 | -- ffi.C.lj_warn_inner(_logargs(', ', ...)) 15 | end 16 | 17 | ---Prints an error message to a CSP log and to Lua App Debug log. To speed things up and only use Lua Debug app, call `ac.setLogSilent()`. 18 | ---@param ... string|number|boolean @Values. 19 | function ac.error(...) 20 | -- ffi.C.lj_error_inner(_logargs(', ', ...)) 21 | end 22 | 23 | ---For compatibility, acts similar to `ac.log()`. 24 | function print(...) 25 | -- ffi.C.lj_log_inner(_logargs(' ', ...)) 26 | end 27 | 28 | --[[); else out(]] 29 | 30 | print = ac.log 31 | 32 | --[[) ?]] -------------------------------------------------------------------------------- /common/defs.lua: -------------------------------------------------------------------------------- 1 | -- For acc-lua only, does not get copied to final library or definitions. 2 | 3 | __source = function(name) end 4 | __states = function(name) end 5 | __allow = function(name) end 6 | __namespace = function(name) end 7 | 8 | ---@param cb function 9 | __post_cdef = function(cb) end 10 | 11 | __definitions = function(arg) end 12 | __enum = function(params, values) end 13 | __carindex__ = 0 14 | __cfgSection__ = 0 15 | __mode__ = nil 16 | 17 | ffi = {} 18 | newproxy = nil -------------------------------------------------------------------------------- /common/function.lua: -------------------------------------------------------------------------------- 1 | -- Extensions for functions 2 | 3 | do 4 | local _dgi = debug.getinfo 5 | 6 | -- (function (arg1, arg2) end):bind(value for arg1, value for arg2) 7 | -- this way calls should be faster than merging tables on-fly 8 | debug.setmetatable(function()end, { 9 | __index = { 10 | bind = function(s, ...) 11 | local a = {...} 12 | if #a == 0 then 13 | return s 14 | end 15 | local i = _dgi(s) 16 | if not i.isvararg then 17 | if i.nparams == 0 then return s end 18 | if i.nparams == 1 then return function() return s(a[1]) end end 19 | if i.nparams == 2 then 20 | if #a == 1 then return function(a0) return s(a[1], a0) end 21 | else return function() return s(a[1], a[2]) end end 22 | end 23 | if i.nparams == 3 then 24 | if #a == 1 then return function(a0, a1) return s(a[1], a0, a1) end 25 | elseif #a == 2 then return function(a0) return s(a[1], a[2], a0) end 26 | else return function() return s(a[1], a[2], a[3]) end end 27 | end 28 | if i.nparams == 4 then 29 | if #a == 1 then return function(a0, a1, a2) return s(a[1], a0, a1, a2) end 30 | elseif #a == 2 then return function(a0, a1) return s(a[1], a[2], a0, a1) end 31 | elseif #a == 3 then return function(a0) return s(a[1], a[2], a[3], a0) end 32 | else return function() return s(a[1], a[2], a[3], a[4]) end end 33 | end 34 | end 35 | if #a == 1 then return function(...) return s(a[1], ...) end end 36 | if #a == 2 then return function(...) return s(a[1], a[2], ...) end end 37 | if #a == 3 then return function(...) return s(a[1], a[2], a[3], ...) end end 38 | if #a == 4 then return function(...) return s(a[1], a[2], a[3], a[4], ...) end end 39 | if #a == 5 then return function(...) return s(a[1], a[2], a[3], a[4], a[5], ...) end end 40 | error('Not supported', 2) 41 | end, 42 | }, 43 | __pow = function (s, arg) 44 | local i = _dgi(s) 45 | if i.isvararg or i.nparams > 3 then 46 | return function (...) return s(arg, ...) end 47 | elseif i.nparams == 1 then 48 | return function () return s(arg) end 49 | elseif i.nparams == 2 then 50 | return function (a0) return s(arg, a0) end 51 | elseif i.nparams == 3 then 52 | return function (a0, a1) return s(arg, a0, a1) end 53 | else 54 | return s 55 | end 56 | end 57 | }) 58 | end 59 | -------------------------------------------------------------------------------- /common/internal_import.lua: -------------------------------------------------------------------------------- 1 | local __ust = __util.str 2 | local __uso = __util.str_opt 3 | local __usf = __util.strrefp 4 | local __usr = __util.strrefr 5 | local __uss = __util.secure_state 6 | local __ucf = __util.strcrefp 7 | local __ucr = __util.strcrefr 8 | local __uce = __util.cast_enum 9 | local __utvec2 = __util.cast_vec2 10 | local __utvec3 = __util.cast_vec3 11 | local __utvec4 = __util.cast_vec4 12 | local __utrgb = __util.cast_rgb 13 | local __utrgbm = __util.cast_rgbm 14 | local __utenum = __util.cast_enum 15 | local __utmat3x3 = __util.cast_mat3x3 16 | local __utmat4x4 = __util.cast_mat4x4 17 | -------------------------------------------------------------------------------- /common/io.d.lua: -------------------------------------------------------------------------------- 1 | ---Structure containing various file or directory attributes, including various flags and dates. All values are precomputed and ready to be used (there is 2 | ---no overhead in accessing them once you get the structure). 3 | ---@class io.FileAttributes 4 | ---@field fileSize integer @File size in bytes. 5 | ---@field creationTime integer @File creation time in seconds from 1970. 6 | ---@field lastAccessTime integer @File last access time in seconds from 1970. 7 | ---@field lastWriteTime integer @File last write time in seconds from 1970. 8 | ---@field exists boolean @True if file exists. 9 | ---@field isDirectory boolean @True if file is a directory. 10 | ---@field isHidden boolean @The file or directory is hidden. It is not included in an ordinary directory listing. 11 | ---@field isReadOnly boolean @A file that is read-only. Applications can read the file, but cannot write to it or delete it. 12 | ---@field isEncrypted boolean @A file or directory that is encrypted. For a file, all data streams in the file are encrypted. For a directory, encryption is the default for newly created files and subdirectories. 13 | ---@field isCompressed boolean @A file or directory that is compressed. For a file, all of the data in the file is compressed. For a directory, compression is the default for newly created files and subdirectories. 14 | ---@field isReparsePoint boolean @A file or directory that has an associated reparse point, or a file that is a symbolic link. 15 | ---@cpptype lua_file_attributes 16 | local _fileAttributes = {} 17 | -------------------------------------------------------------------------------- /common/io.lua: -------------------------------------------------------------------------------- 1 | --[[? if (!ctx.test) out(]] 2 | __source 'lua/api_io.cpp' 3 | __namespace 'io' 4 | --[[) ?]] 5 | 6 | ffi.cdef[[ 7 | 8 | typedef struct { 9 | int64_t fileSize; 10 | int64_t creationTime; 11 | int64_t lastAccessTime; 12 | int64_t lastWriteTime; 13 | bool exists; 14 | bool isDirectory; 15 | bool isHidden; 16 | bool isReadOnly; 17 | bool isEncrypted; 18 | bool isCompressed; 19 | bool isReparsePoint; 20 | } lua_file_attributes; 21 | 22 | typedef struct { 23 | lua_string_ref name__; 24 | int64_t fileSize; 25 | int64_t creationTime; 26 | int64_t lastAccessTime; 27 | int64_t lastWriteTime; 28 | bool exists; 29 | bool isDirectory; 30 | bool isHidden; 31 | bool isReadOnly; 32 | bool isEncrypted; 33 | bool isCompressed; 34 | bool isReparsePoint; 35 | } lua_dir_scan; 36 | 37 | ]] 38 | 39 | ---@alias io.ZipEntry {filename: string}|{data: binary}|binary 40 | 41 | ---Scan directory and call callback function for each of files, passing file name (not full name, but only name of the file) and attributes. If callback function would return 42 | ---a non-nil value, iteration will stop and value returned by callback would return from this function. This could be used to 43 | ---find a certain file without going through all files in the directory. Optionally, a mask can be used to pre-filter received files 44 | ---entries. 45 | --- 46 | ---If callback function is not provided, it’ll return list of files instead (file names only). 47 | --- 48 | ---System entries “.” and “..” will not be included in the list of files. Accessing attributes does not add extra cost. 49 | ---@generic TCallbackData 50 | ---@generic TReturn 51 | ---@param directory string @Directory to look for files in. Note: directory is relative to current directory, not to script directory. For AC in general it’s an AC root directory, but do not rely on it, instead use `ac.getFolder(ac.FolderID.Root)`. 52 | ---@param mask string? @Mask in a form of usual “*.*”. Default value: '*'. 53 | ---@param callback fun(fileName: string, fileAttributes: io.FileAttributes, callbackData: TCallbackData): TReturn? @Callback which will be ran for every file in directory fitting mask until it would return a non-nil value. 54 | ---@param callbackData TCallbackData? @Callback data that will be passed to callback as third argument, to avoid creating a capture. 55 | ---@return TReturn? @First non-nil value returned by callback. 56 | ---@overload fun(directory: string, callback: fun(fileName: string, fileAttributes: io.FileAttributes, callbackData: any), callbackData: any): any 57 | ---@overload fun(directory: string, mask: string|nil): string[] @This overload just returns the list 58 | function io.scanDir(directory, mask, callback, callbackData) 59 | if type(directory) ~= 'string' then error('First argument has to be a string with path to a directory', 2) end 60 | if type(mask) == 'function' then mask, callback, callbackData = nil, mask, callback end 61 | local s, r = ffi.C.lj_dirscan_start__io(__util.str(directory), __util.str_opt(mask)), nil 62 | if not callback then 63 | r = {} 64 | local n = 1 65 | while s.exists do 66 | r[n], n = __util.strrefr(s.name__), n + 1 67 | ffi.C.lj_dirscan_next__io(s) 68 | end 69 | else 70 | r = nil 71 | while s.exists do 72 | r = callback(__util.strrefr(s.name__), s, callbackData) 73 | if r ~= nil then break end 74 | ffi.C.lj_dirscan_next__io(s) 75 | end 76 | end 77 | ffi.C.lj_dirscan_end__io(s) 78 | return r 79 | end 80 | -------------------------------------------------------------------------------- /common/json.d.lua: -------------------------------------------------------------------------------- 1 | ---Very basic JSON processing library. Based on json.lua by rxi, but a bit simplified and streamlined. 2 | ---In case you need to store and load data within Lua scripts, consider using `stringify()` and 3 | ---`stringify.parse()` instead: it’s faster and more reliable. 4 | JSON = {} 5 | 6 | ---Serializes a Lua entity (like a table) into a compact JSON. 7 | ---@param data table|number|string|boolean|nil 8 | ---@return string 9 | function JSON.stringify(data) end 10 | 11 | ---Parses a compact JSON into a Lua entity. Note: if JSON is damaged, parser won’t throw an error, but 12 | ---results might be somewhat unpredictable. It’s an intended behaviour: in 99% of cases JSON parser 13 | ---used to exchange data with, for example, API endpoints, will receive correct data, but some of those 14 | ---AC JSON files are pretty screwed and often include things like missing commas, comments, etc. 15 | ---@param data string? 16 | ---@return any 17 | function JSON.parse(data) end 18 | -------------------------------------------------------------------------------- /common/json.lua: -------------------------------------------------------------------------------- 1 | JSON = { 2 | stringify = __util.json, 3 | parse = __util.jsonParse, 4 | } 5 | -------------------------------------------------------------------------------- /common/os.d.lua: -------------------------------------------------------------------------------- 1 | ---@class os.ConsoleProcessResult 2 | ---@field exitCode integer @If process finished successfully, 0. If failed to get the exit code, -1. 3 | ---@field stdout string @Contents of stdout stream of ran process. 4 | ---@field stderr string @Contents of stderr stream of ran process. Would be set only if `separateStderr` parameter was set to true. 5 | -------------------------------------------------------------------------------- /common/string.lua: -------------------------------------------------------------------------------- 1 | -- Most of these functions are implemented on C++ side to keep things fast (things like string splitting work about ten times as fast there) 2 | 3 | do 4 | -- Also, some new metamethods for extra niceness: 5 | local mt = getmetatable('') 6 | 7 | -- Index operator: `('abc')[1] == 'a'` 8 | mt.__index = function(str, i) return type(i) == 'number' and string.sub(str, i, i) or string[i] end 9 | 10 | -- String reversal: `-'abc' == 'cba'` 11 | -- mt.__unm = function(str, i) return string.reverse(str) end 12 | 13 | -- String multiplication: `'abc' * 2 == 'abcabc'` 14 | -- mt.__mul = function(str, i) return string.rep(str, i) end 15 | 16 | -- String contatenation (friendlier): `'abc' + nil == 'abcnil'` 17 | -- mt.__add = function(str, i) return str..tostring(i) end 18 | 19 | -- String format: `'%f' % 1 == '1.0000'`, `'%s, %s' % {1, 2} == '1, 2'` 20 | mt.__mod = function(str, i) 21 | return type(i) == 'table' and string.format(str, unpack(i)) or string.format(str, i) 22 | end 23 | 24 | -- For later: 25 | -- mt.__call = function(str, i) return string.upper(str) end 26 | end 27 | 28 | --[[ TODO: tests 29 | 30 | ac.log(('test')[4]) 31 | ac.log(('test'):upper()) 32 | ac.log(('test'):rep(4)) 33 | ac.log(-'test') 34 | ac.log('test' .. vec2(1, 2)) 35 | ac.log('test' + vec2(1, 2)) 36 | ac.log('test' * 2) 37 | ac.log('%f' % 1.2345) 38 | ac.log('%f, %f' % {1.2345, 2.3456}) 39 | ]] -------------------------------------------------------------------------------- /common/stringify.lua: -------------------------------------------------------------------------------- 1 | stringify = setmetatable({ 2 | tryParse = function(v, env, fallback) 3 | return __util.lazy('lib_stringify').tryParse(v, env, fallback) 4 | end, 5 | parse = function (v, env) 6 | return __util.lazy('lib_stringify').parse(v, env) 7 | end, 8 | register = function(n, v) 9 | return __util.lazy('lib_stringify').register(n, v) 10 | end, 11 | locals = function () 12 | local variables = {} 13 | local idx = 1 14 | while debug do 15 | local ln, lv = debug.getlocal(2, idx) 16 | if ln ~= nil then 17 | variables[ln] = lv 18 | else 19 | break 20 | end 21 | idx = 1 + idx 22 | end 23 | return variables 24 | end, 25 | substep = function (out, ptr, obj, lineBreak, depthLimit) 26 | return __util.lazy('lib_stringify').substep(out, ptr, obj, lineBreak, depthLimit) 27 | end, 28 | binary = setmetatable({ 29 | tryParse = function (v, fallback) 30 | local r, p = pcall(__util.prs().unpackString, v) 31 | if r then return p end 32 | return fallback 33 | end, 34 | parse = function (v) 35 | return __util.prs().unpackString(v) 36 | end, 37 | }, { 38 | __call = function(_, data) 39 | return __util.prs().packString(data) 40 | end 41 | }) 42 | }, { 43 | __call = function(_, v, compact, depthLimit) 44 | return __util.lazy('lib_stringify').call(v, compact, depthLimit) 45 | end 46 | }) -------------------------------------------------------------------------------- /common/table.d.lua: -------------------------------------------------------------------------------- 1 | ---Merges tables into one big table. Tables can be arrays or dictionaries, if it’s a dictionary same keys from subsequent tables will overwrite previously set keys. 2 | ---@generic T 3 | ---@param table T 4 | ---@vararg table 5 | ---@return T 6 | function table.chain(table, ...) end 7 | -------------------------------------------------------------------------------- /deps/vector.lua: -------------------------------------------------------------------------------- 1 | function __util.boundArray(ct, cb) 2 | return __util.lazy('lib_vector')(ct, cb, true)(16) 3 | end 4 | 5 | local __arrayTypeCache = {} 6 | function __util.arrayType(ct) 7 | local cache = __arrayTypeCache[ct] 8 | if cache then return cache end 9 | local created = __util.lazy('lib_vector')(ct, nil, false) 10 | __arrayTypeCache[ct] = created 11 | return created 12 | end 13 | 14 | function __util.stealVector(s, steal) 15 | if not steal then return s.raw, s._size, -1 end 16 | local r0, r1, r2 = s.raw, s._size, s._cap 17 | s.raw, s._size, s._cap = nil, 0, 0 18 | return r0, r1, r2 19 | end 20 | -------------------------------------------------------------------------------- /lib_hashspace.lua: -------------------------------------------------------------------------------- 1 | -- Helper for more efficient collision checks in 2D space (using 3D coordinates) 2 | 3 | __source 'lua/api_extras_hashspace.cpp' 4 | require './common/internal_import' 5 | 6 | ffi.cdef [[ 7 | typedef struct { 8 | int* _iterate_cur; 9 | int* _iterate_end; 10 | } hashspace; 11 | ]] 12 | 13 | ---@class ac.HashSpaceItem 14 | local HashSpaceItem = class('HashSpaceItem') 15 | function HashSpaceItem.allocate(h, id) 16 | return { _h = h, _id = id } 17 | end 18 | ---Returns ID associated with an item. 19 | ---@return integer 20 | function HashSpaceItem:id() 21 | return self._id 22 | end 23 | ---Moves an item to a position. 24 | ---@param pos vec3 25 | function HashSpaceItem:update(pos) 26 | if self._id < 0 then error('Already disposed', 2) end 27 | ffi.C.lj_hashspace_update(self._h, self._id, __util.ensure_vec3(pos)) 28 | end 29 | ---Removes item from its space. 30 | function HashSpaceItem:dispose() 31 | if self._id ~= -1 then 32 | ffi.C.lj_hashspace_delete(self._h, self._id) 33 | self._id = -1 34 | end 35 | end 36 | 37 | ---@param cellSize number @Should be about twice as large as your largest entity. 38 | ---@return ac.HashSpace 39 | function ac.HashSpace(cellSize) return ffi.gc(ffi.C.lj_hashspace_new(tonumber(cellSize) or 10), ffi.C.lj_hashspace_gc) end 40 | 41 | ---Simple structure meant to speed up collision detection by arranging items in a grid using hashmap. Cells are arranged horizontally. 42 | ---@class ac.HashSpace 43 | ---@explicit-constructor ac.HashSpace 44 | ffi.metatype('hashspace', { __index = { 45 | ---Iterates items around given position. 46 | ---@generic T 47 | ---@param pos vec3 48 | ---@param callback fun(id: integer, callbackData: T) 49 | ---@param callbackData T? 50 | iterate = function (s, pos, callback, callbackData) 51 | ffi.C.lj_hashspace_iteratebegin(s, __util.ensure_vec3(pos)) 52 | while s._iterate_cur ~= s._iterate_end do 53 | callback(s._iterate_cur[0], callbackData) 54 | s._iterate_cur = s._iterate_cur + 1 55 | end 56 | end, 57 | ---Checks if there are any items around given position. 58 | ---@param pos vec3 59 | ---@return boolean 60 | anyAround = function (s, pos) 61 | ffi.C.lj_hashspace_iteratebegin(s, __util.ensure_vec3(pos)) 62 | return s._iterate_cur ~= s._iterate_end 63 | end, 64 | ---Count amount of items around given position. 65 | ---@param pos vec3 66 | ---@return integer 67 | count = function (s, pos) 68 | ffi.C.lj_hashspace_iteratebegin(s, __util.ensure_vec3(pos)) 69 | return s._iterate_end - s._iterate_cur 70 | end, 71 | ---Returns raw pointers for given position for manual iteration. Be careful! 72 | ---@param pos vec3 73 | ---@return any, any 74 | rawPointers = function (s, pos) 75 | ffi.C.lj_hashspace_iteratebegin(s, __util.ensure_vec3(pos)) 76 | return s._iterate_cur, s._iterate_end 77 | end, 78 | ---Adds a new dynamic item to the grid. Each item gets a new ID. 79 | ---@return ac.HashSpaceItem 80 | add = function (s) return HashSpaceItem(s, ffi.C.lj_hashspace_add(s)) end, 81 | ---Adds a fixed item to the grid, with predetermined ID. Avoid mixing dynamic and fixed items in the same grid. 82 | ---@param id integer 83 | ---@param pos vec3 84 | addFixed = function (s, id, pos) 85 | if not vec3.isvec3(pos) then error('Position should be of a vec3 type', 2) end 86 | ffi.C.lj_hashspace_addfixed(s, tonumber(id) or 0, __util.ensure_vec3(pos)) 87 | end 88 | } }) 89 | 90 | __definitions() 91 | 92 | return function(cellSize) return ffi.gc(ffi.C.lj_hashspace_new(tonumber(cellSize) or 10), ffi.C.lj_hashspace_gc) end -------------------------------------------------------------------------------- /lib_jsonparse.lua: -------------------------------------------------------------------------------- 1 | __definitions() 2 | 3 | local __escapeMapInv = { [34] = '"', [39] = '\'', [47] = '/', [92] = '\\', [98] = '\8', [102] = '\12', [110] = '\n', [114] = '\13', [116] = '\9' } 4 | 5 | local function __nextNonWhitespace(str, i) 6 | for j = i, math.max(i, #str + 1) do 7 | local b = string.byte(str, j) 8 | if b ~= 9 and b ~= 10 and b ~= 13 and b ~= 32 then return j end 9 | end 10 | end 11 | 12 | local function __nextDelimiter(str, i) 13 | for j = i, math.max(i, #str + 1) do 14 | local b = string.byte(str, j) 15 | if not b or b == 9 or b == 10 or b == 13 or b == 32 or b == 44 or b == 93 or b == 125 then return j end 16 | end 17 | end 18 | 19 | local function __skip(str, i, commas) 20 | local o = string.byte(str, i) 21 | while o == 47 or commas and o == 44 do 22 | if o == 44 then 23 | i = __nextNonWhitespace(str, i + 1) 24 | elseif string.byte(str, i + 1) == 47 then 25 | for j = i + 2, #str do 26 | if string.byte(str, j) == 10 or string.byte(str, j) == 13 then 27 | i = __nextNonWhitespace(str, j + 1) 28 | break 29 | end 30 | end 31 | elseif string.byte(str, i + 1) == 42 then 32 | for j = i + 2, #str do 33 | if string.byte(str, j) == 42 and string.byte(str, j + 1) == 47 then 34 | i = __nextNonWhitespace(str, j + 2) 35 | break 36 | end 37 | end 38 | else 39 | return #str + 1, nil 40 | end 41 | o = string.byte(str, i) 42 | end 43 | return i, o 44 | end 45 | 46 | 47 | local function __endOfKey(str, i) 48 | for j = i, math.max(i, #str + 1) do 49 | local b = string.byte(str, j) 50 | if not b or b == 58 then return str:sub(i - 1, j - 1):trim(), j + 1 end 51 | if b == 47 and (string.byte(str, j + 1) == 47 or string.byte(str, j + 1) == 42) then 52 | local k = str:sub(i - 1, j - 1):trim() 53 | return k, __skip(str, j) + 1 54 | end 55 | end 56 | end 57 | 58 | local function __jsonParse(str, i) 59 | local o 60 | i, o = __skip(str, i) 61 | if o == 34 or o == 39 then 62 | local res, j, k = '', i + 1, i + 1 63 | while true do 64 | local x = string.byte(str, j) 65 | if not x or x == o then 66 | return res..str:sub(k, j - 1), j + 1 67 | elseif x == 92 then 68 | res, j = res..str:sub(k, j - 1), j + 1 69 | local c = string.byte(str, j) 70 | if c == 117 then 71 | local hex = str:match('^[dD][89aAbB]%x%x\\u%x%x%x%x', j + 1) or str:match('^%x%x%x%x', j + 1) or '' 72 | local n1, n2 = tonumber(hex:sub(1, 4), 16), tonumber(hex:sub(7, 10), 16) 73 | res, j = res..(n2 and (string.codePointToUTF8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)) 74 | or n1 and string.codePointToUTF8(n1) or ''), j + #hex 75 | else 76 | res = res..(__escapeMapInv[c] or '') 77 | end 78 | k = j + 1 79 | end 80 | j = j + 1 81 | end 82 | elseif o == 116 then 83 | if str:sub(i, i + 3) == 'true' then return true, i + 4 end 84 | return nil, __nextDelimiter(str, i) 85 | elseif o == 102 then 86 | if str:sub(i, i + 4) == 'false' then return false, i + 5 end 87 | return nil, __nextDelimiter(str, i) 88 | elseif o == 110 then 89 | if str:sub(i, i + 3) == 'null' then return nil, i + 4 end 90 | return nil, __nextDelimiter(str, i) 91 | elseif o >= 48 and o <= 57 or o == 45 then 92 | local x = __nextDelimiter(str, i) 93 | return tonumber(str:sub(i, x - 1)), x 94 | elseif o == 91 or o == 123 then 95 | local res, key = {}, nil 96 | i = i + 1 97 | -- print(string.sub(str, i)) 98 | while i < #str do 99 | i = __nextNonWhitespace(str, i) 100 | i = __skip(str, i, true) 101 | if string.byte(str, i) == 93 or string.byte(str, i) == 125 then --[[ ], } ]] 102 | -- print('END: %s' % string.sub(str, i)) 103 | return res, i + 1 104 | end 105 | if o == 91 then --[[ [ ]] 106 | res[#res + 1], i = __jsonParse(str, i) 107 | elseif o == 123 then --[[ { ]] 108 | if string.byte(str, i) == 34 or string.byte(str, i) == 39 then --[[ ", ' ]] 109 | key, i = __jsonParse(str, i) 110 | i = __nextNonWhitespace(str, i) 111 | i = __skip(str, i) 112 | -- print(key) 113 | -- print(string.byte(str, i)) 114 | if string.byte(str, i) == 58 --[[ : ]] then i = i + 1 end 115 | else 116 | key, i = __endOfKey(str, i + 1) 117 | end 118 | res[tostring(key)], i = __jsonParse(str, __nextNonWhitespace(str, i)) 119 | end 120 | end 121 | -- print('ERR:'.. string.sub(str, i)) 122 | return res, #str + 1 123 | else 124 | return nil, #str + 1 125 | end 126 | end 127 | 128 | return function(str) 129 | if str == nil then return nil end 130 | str = tostring(str) 131 | local s, r = pcall(__jsonParse, str, __nextNonWhitespace(str, 1)) 132 | if s then return r end 133 | return nil 134 | end -------------------------------------------------------------------------------- /lib_music.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_music.cpp' 2 | __allow 'music' 3 | require './common/internal_import' 4 | 5 | ffi.cdef [[ 6 | typedef struct { 7 | lua_string_ref __cover_id; 8 | lua_string_ref __source_id; 9 | lua_string_ref __artist; 10 | lua_string_ref __album; 11 | lua_string_ref __title; 12 | const int albumTracksCount; 13 | const int trackNumber; 14 | const int trackDuration; 15 | const int trackPosition; 16 | const int __track_phase; 17 | const char __data_phase; 18 | const bool isPlaying; 19 | const bool hasCover; 20 | } musicdata; 21 | ]] 22 | 23 | local _cps 24 | local _cpw 25 | 26 | ffi.metatype('musicdata', { 27 | __tostring = function () return _cpw.coverID end, 28 | __index = function (_, key) 29 | if key == 'title' then return _cpw.title end 30 | if key == 'album' then return _cpw.album end 31 | if key == 'artist' then return _cpw.artist end 32 | if key == 'sourceID' then return _cpw.sourceID end 33 | end, 34 | __newindex = function() error('This item is read-only', 2) end 35 | }) 36 | 37 | __definitions() 38 | 39 | return function() 40 | if _cps == nil then 41 | _cps = ffi.new('musicdata') 42 | _cpw = {} 43 | end 44 | local r = ffi.C.lj_get_music_data__music(_cps) 45 | if r == 2 or _cpw.title == nil then 46 | _cpw.title = __util.strrefr(_cps.__title) 47 | _cpw.album = __util.strrefr(_cps.__album) 48 | _cpw.artist = __util.strrefr(_cps.__artist) 49 | _cpw.sourceID = __util.strrefr(_cps.__source_id) 50 | end 51 | if _cpw.coverID == nil then 52 | _cpw.coverID = __util.strrefr(_cps.__cover_id) 53 | end 54 | return _cps 55 | end 56 | -------------------------------------------------------------------------------- /lib_numlut.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_extras_numlut.cpp' 2 | require './common/internal_import' 3 | 4 | ffi.cdef [[ 5 | typedef struct { 6 | uint columns; 7 | float* calculated; 8 | } numlut; 9 | ]] 10 | 11 | ---Meant to quickly interpolate between tables of values, some of them could be colors set in HSV. Example: 12 | ---``` 13 | ---local lut = ac.Lut([[ 14 | --- -100 | 0.00, 350, 0.37, 1.00, 3.00, 1.00, 1.00, 3.60,500.00, 0.04 15 | --- -90 | 1.00, 10, 0.37, 1.00, 3.00, 1.00, 1.00, 3.60,500.00, 0.04 16 | --- -20 | 1.00, 10, 0.37, 1.00, 3.00, 1.00, 1.00, 3.60,500.00, 0.04 17 | ---]], { 2 }) 18 | ---assert(lut:calculate(-95)[1] == 0.5) 19 | ---``` 20 | ---@class ac.Lut 21 | ---@explicit-contructor ac.Lut 22 | ffi.metatype('numlut', { __index = { 23 | ---Interpolate for a given input, return a newly created table. Note: consider using `:calculateTo()` instead to avoid re-creating tables, it would work much more efficiently. 24 | ---@param input number 25 | ---@return number[] 26 | calculate = function (s, input) 27 | ffi.C.lj_numlut_calculate(s, input) 28 | local ret = {} 29 | for i = 1, s.columns do 30 | ret[i] = s.calculated[i - 1] 31 | end 32 | return ret 33 | end, 34 | ---Interpolate for a given input, write result to a given table. 35 | ---@param output number[] 36 | ---@param input number 37 | ---@return number[] @Same table as was provided in arguments. 38 | calculateTo = function (s, output, input) 39 | ffi.C.lj_numlut_calculate(s, input) 40 | for i = 1, s.columns do 41 | output[i] = s.calculated[i - 1] 42 | end 43 | return output 44 | end 45 | } }) 46 | 47 | __definitions() 48 | 49 | return function (data, hsvColumns) 50 | local hsvRowsData = '' 51 | for i = 1, #hsvColumns do 52 | hsvRowsData = i > 1 and (hsvRowsData .. ',' .. (hsvColumns[i] - 1)) or (hsvRowsData .. (hsvColumns[i] - 1)) 53 | end 54 | local created = ffi.C.lj_numlut_new(data and tostring(data) or "", hsvRowsData) 55 | return ffi.gc(created, ffi.C.lj_numlut_gc) 56 | end -------------------------------------------------------------------------------- /lib_numlut_jit.lua: -------------------------------------------------------------------------------- 1 | require './common/internal_import' 2 | 3 | -- Obsolete LuaJIT-only implementation 4 | local LuaJit_rgb = rgb() 5 | local LuaJit_hsv = hsv() 6 | local function LuaJit_rgbToHsv(data, i) 7 | LuaJit_rgb.r = data[i] 8 | LuaJit_rgb.g = data[i + 1] 9 | LuaJit_rgb.b = data[i + 2] 10 | data[i] = LuaJit_rgb:hue() 11 | data[i + 1] = LuaJit_rgb:saturation() 12 | data[i + 2] = LuaJit_rgb:value() 13 | end 14 | local function LuaJit_hsvToRgb(data, i) 15 | LuaJit_hsv.h = data[i] 16 | LuaJit_hsv.s = data[i + 1] 17 | LuaJit_hsv.v = data[i + 2] 18 | local r = LuaJit_hsv:rgb() 19 | data[i] = r.r 20 | data[i + 1] = r.g 21 | data[i + 2] = r.b 22 | end 23 | local function LuaJit_prepareData(data, rows, hsvRows) 24 | local hsvCount = #hsvRows 25 | for i = 1, rows do 26 | for j = 1, hsvCount do 27 | LuaJit_hsvToRgb(data[i].output, hsvRows[j]) 28 | end 29 | end 30 | end 31 | local function LutJit_findLeft(rows, count_base, input) 32 | local count_search = count_base; 33 | local index = 0 34 | while count_search > 0 do 35 | local step = math.floor(count_search / 2) 36 | if rows[index + step + 1].input > input then 37 | count_search = step; 38 | else 39 | index = index + step + 1; 40 | count_search = count_search - step + 1; 41 | end 42 | end 43 | return rows[index > 0 and index or 1], rows[index < count_base and index + 1 or index] 44 | end 45 | local function LutJit_distanceToDiv(next, previous) 46 | return math.max(next.input - previous.input, 0.0000001) 47 | end 48 | local function LutJit_finalize(self, data) 49 | for i = 1, #self.hsvRows do 50 | LuaJit_rgbToHsv(data, self.hsvRows[i]) 51 | end 52 | end 53 | local function LutJit_setSingle(self, output, item) 54 | for i = 1, self.columns do 55 | output[i] = item.output[i] 56 | end 57 | LutJit_finalize(self, output) 58 | end 59 | local function LutJit_setLerp(self, output, a, b, interpolation) 60 | for i = 1, self.columns do 61 | output[i] = math.lerp(a.output[i], b.output[i], interpolation) 62 | end 63 | LutJit_finalize(self, output) 64 | end 65 | 66 | ---Meant to quickly interpolate between tables of values, some of them could be colors set in HSV. Example: 67 | ---``` 68 | ---local lutJit = ac.LutJit:new{ data = { 69 | --- { input = -100, output = { 0.00, 350, 0.37, 1.00, 3.00, 1.00, 1.00, 3.60,500.00, 0.04 } }, 70 | --- { input = -90, output = { 1.00, 10, 0.37, 1.00, 3.00, 1.00, 1.00, 3.60,500.00, 0.04 } }, 71 | --- { input = -20, output = { 1.00, 10, 0.37, 1.00, 3.00, 1.00, 1.00, 3.60,500.00, 0.04 } }, 72 | --- }, hsvRows = { 2 }} 73 | ---assert(lutJit:calculate(-95)[1] == 1) 74 | ---``` 75 | ---Obsolete. Use `ac.Lut` instead, with faster C++ implementation. 76 | ---@class ac.LutJit 77 | ---@deprecated 78 | ac.LutJit = {} 79 | 80 | ---Creates new ac.LuaJit instance. Deprecated, use `ac.Lut` instead. 81 | ---@deprecated 82 | ---@param data any 83 | ---@param hsvRows integer[] @ 1-based indices of columns (not rows) storing HSV values in them. 84 | ---@return table 85 | function ac.LutJit:new(o, data, hsvRows) 86 | o = o or {} 87 | setmetatable(o, self) 88 | self.__index = self 89 | o.data = o.data or data 90 | o.hsvRows = o.hsvRows or hsvRows or {} 91 | o.rows = #o.data 92 | o.columns = o.rows > 0 and #o.data[1].output or 0 93 | LuaJit_prepareData(o.data, o.rows, o.hsvRows) 94 | return o 95 | end 96 | 97 | ---Computes a new value. Deprecated, use `ac.Lut` instead. 98 | ---@deprecated 99 | ---@param input number 100 | ---@return number[] 101 | function ac.LutJit:calculate(input) 102 | local ret = {} 103 | self:calculateTo(ret, input) 104 | return ret 105 | end 106 | 107 | ---Computes a new value to a preexisting HSV value. Deprecated, use `ac.Lut` instead. 108 | ---@deprecated 109 | ---@param output number[] 110 | ---@param input number 111 | ---@return number[] @Same table as was provided in arguments. 112 | function ac.LutJit:calculateTo(output, input) 113 | if self.rows == 0 then return {} end 114 | 115 | local previous, next = LutJit_findLeft(self.data, self.rows, input); 116 | if next.input == previous.input then 117 | LutJit_setSingle(self, output, next) 118 | else 119 | local interpolation = math.max(input - previous.input, 0) / LutJit_distanceToDiv(next, previous); 120 | LutJit_setLerp(self, output, previous, next, math.saturate(interpolation)); 121 | end 122 | return output 123 | end 124 | 125 | __definitions() 126 | 127 | return ac.LutJit -------------------------------------------------------------------------------- /lib_onlineevent.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_extras_connectonline.cpp' 2 | require './common/internal_import' 3 | 4 | local _created = {} 5 | 6 | __definitions() 7 | 8 | return function (layout, callback, namespace, udp, params) 9 | local layoutStr = __util.__si_build(layout) 10 | if type(layoutStr) ~= 'string' then error('Layout is required and should be a table or a string', 2) end 11 | 12 | if not __allowIO__ and namespace == ac.SharedNamespace.Global then error('Script of this type can’t use global namespace', 2) end 13 | 14 | local key, baseFlags 15 | if type(namespace) == 'table' and type(namespace.__key) == 'number' then 16 | -- Some Lua things (with apps or online API) can send messages with a custom type 17 | key = namespace.__key 18 | baseFlags = 4 19 | namespace = nil 20 | else 21 | key = ffi.C.lj_connectonline_key(layoutStr, type(namespace) == 'string' and namespace or nil) 22 | baseFlags = 0 23 | end 24 | 25 | local created = _created[tonumber(key)] 26 | if created ~= nil then 27 | table.insert(created.callbacks, callback) 28 | return created.sendFn, created.accessFn 29 | end 30 | 31 | local s_name = __util.__si_ffi(layoutStr, true) 32 | local size = ffi.sizeof(s_name) 33 | if not size or size > 175 and not ac.getSim().directMessagingAvailable then 34 | error(string.format('Structure is too large (%d bytes; limit is 175 bytes)', size), 2) 35 | end 36 | 37 | if udp == true or type(udp) == 'table' then 38 | baseFlags = baseFlags + 2 39 | if not ac.getSim().directUDPMessagingAvailable then 40 | error('UDP messages are not available with this server', 2) 41 | end 42 | end 43 | 44 | local writeObj, writeProxy, accessedDirectly 45 | local accessFn = function () 46 | if not writeProxy then 47 | writeObj = ffi.new(s_name) 48 | writeProxy = __util.__si_proxy(layout, writeObj) 49 | elseif not accessedDirectly then 50 | ffi.fill(writeObj, size) 51 | end 52 | return writeProxy 53 | end 54 | 55 | local readObj = ffi.new(s_name) 56 | created = { 57 | sendFn = function (args, repeatForNewConnections, target) 58 | local p = accessFn() 59 | if args and args ~= p then 60 | for k, v in pairs(args) do 61 | p[k] = v 62 | end 63 | end 64 | return ffi.C.lj_connectonline_send(key, writeObj, size, baseFlags + (repeatForNewConnections == true and 1 or 0), 65 | tonumber(target) or -1, type(udp) == 'table' and tonumber(udp.range) or 0) 66 | end, 67 | accessFn = function () 68 | accessedDirectly = true 69 | return accessFn() 70 | end, 71 | readProxy = __util.__si_proxy(layout, readObj), 72 | callbacks = { callback }, 73 | } 74 | 75 | if type(params) == 'table' and not not params.processPostponed then 76 | baseFlags = baseFlags + 8 77 | end 78 | 79 | _created[tonumber(key)] = created 80 | ffi.C.lj_connectonline_listen(key, baseFlags, readObj, size, __util.setCallback(function (carIndex) 81 | local car = ac.getCar(carIndex) 82 | for i = 1, #created.callbacks do 83 | __util.cbCall(created.callbacks[i], car, created.readProxy) 84 | end 85 | end)) 86 | 87 | return created.sendFn, created.accessFn 88 | end 89 | -------------------------------------------------------------------------------- /lib_smoothing.lua: -------------------------------------------------------------------------------- 1 | local _dt = 0.0167 2 | local mt = { 3 | __tostring = function(v) 4 | return string.format('(%s, s=%f)', v.val, v.smooth) 5 | end, 6 | __index = { 7 | update = function(v, x) 8 | v.val = math.applyLag(v.val, x, 1 - 1 / v.smooth, _dt) 9 | end, 10 | updateIfNew = function(v, x) 11 | if x ~= v.lastValue then 12 | v.val = math.applyLag(v.val, x, 1 - 1 / v.smooth, _dt) 13 | v.lastValue = x 14 | end 15 | end 16 | } 17 | } 18 | 19 | __definitions() 20 | 21 | return { 22 | c = function (v, s) 23 | return setmetatable({ val = v or 0, lastValue = v or 0, smooth = s or 100 }, mt) 24 | end, 25 | s = function (dt) 26 | _dt = math.applyLag(_dt, dt, 0.9, _dt) 27 | end 28 | } -------------------------------------------------------------------------------- /lib_social.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_extras_misc.cpp' 2 | __allow 'sociallib' 3 | require './common/internal_import' 4 | 5 | ffi.cdef [[ 6 | typedef struct { 7 | uint64_t __key; 8 | rgbm color; 9 | bool friend; 10 | bool muted; 11 | uint8_t _pad; 12 | uint8_t _extraTags_count; 13 | uint8_t _extraTags_items[12]; 14 | } socialdata; 15 | ]] 16 | 17 | __definitions() 18 | 19 | local _sddt = {} 20 | local _sdch = {} 21 | local _sdet = {} 22 | 23 | local function _sdca(d, i) 24 | local e = _sdch[d] 25 | if not e then 26 | if not _t_socialdata then 27 | _t_socialdata = ffi.typeof('socialdata') 28 | end 29 | e = _t_socialdata() 30 | _sdch[d] = e 31 | end 32 | ffi.C.lj_socialdata_sync__sociallib(e, i, d) 33 | return e 34 | end 35 | 36 | local function _sdea(i) 37 | local g = tonumber(i) 38 | if not _sdet[g] then 39 | _sdet[g] = __util.native('lj_socialdata_gettag', g) 40 | end 41 | return _sdet[g] or nil 42 | end 43 | 44 | local function _sdip(s, i) 45 | i = i + 1 46 | if i <= #s then return i, s[i] end 47 | end 48 | 49 | local _sdmt = { 50 | __tostring = function (s) 51 | local t = {} 52 | if s.__d.friend then t[#t + 1] = 'friend' end 53 | if s.__d.muted then t[#t + 1] = 'muted' end 54 | return string.format('%s: [color=%s, %s]', s.__n, s.__d.color, table.concat(t, ', ')) 55 | end, 56 | __len = function (s) 57 | return (s.__d.muted and 1 or 0) + (s.__d.friend and 1 or 0) + s.__d._extraTags_count 58 | end, 59 | __ipairs = function(s) 60 | return _sdip, s, 0 61 | end, 62 | __index = function (s, key) 63 | ffi.C.lj_socialdata_sync__sociallib(s.__d, s.__i, s.__n) 64 | if key == 'friend' then return s.__d.friend end 65 | if key == 'muted' then return s.__d.muted end 66 | if key == 'color' then return s.__d.color end 67 | if type(key) == 'number' then 68 | if s.__d.muted then 69 | if key == 1 then return _sdea(-2) end 70 | key = key - 1 71 | end 72 | if s.__d.friend then 73 | if key == 1 then return _sdea(-1) end 74 | key = key - 1 75 | end 76 | if key > 0 and key <= s.__d._extraTags_count then 77 | return _sdea(s.__d._extraTags_items[key - 1]) 78 | end 79 | return nil 80 | end 81 | return false 82 | end, 83 | __newindex = function (s, key, value) 84 | if key == 'friend' then 85 | if s.__d.friend == not not value then return end 86 | s.__d.friend = not s.__d.friend 87 | elseif key == 'muted' then 88 | if s.__d.muted == not not value then return end 89 | s.__d.muted = not s.__d.muted 90 | elseif key == 'color' then 91 | if s.__d.color == value or not rgbm.isrgbm(value) then return end 92 | s.__d.color = value 93 | if ac.setDriverChatNameColor then ac.setDriverChatNameColor(s.__i, value) end 94 | return 95 | else return end 96 | __util.native('lj_socialdata_upsync', s.__d, s.__n) 97 | end 98 | } 99 | 100 | function ac.DriverTags(driverName) 101 | local r = _sddt[driverName] 102 | if not r then 103 | local i = ac.getCarByDriverName(driverName) 104 | r = setmetatable({__n = driverName, __i = i, __d = _sdca(driverName, i)}, _sdmt) 105 | _sddt[driverName] = r 106 | end 107 | return r 108 | end 109 | 110 | ---Checks if a user is tagged as a friend. Uses CSP and CM databases. Deprecated, use `ac.DriverTags` instead. 111 | ---@deprecated 112 | ---@param driverName string @Driver name. 113 | ---@return boolean 114 | function ac.isTaggedAsFriend(driverName) 115 | return _sdca(driverName, -1).friend 116 | end 117 | 118 | ---Tags user as a friend (or removes the tag if `false` is passed). Deprecated, use `ac.DriverTags` instead. 119 | ---@deprecated 120 | ---@param driverName string @Driver name. 121 | ---@param isFriend boolean? @Default value: `true`. 122 | function ac.tagAsFriend(driverName, isFriend) 123 | local e = _sdca(driverName, -1) 124 | if e.friend == (isFriend ~= false) then return end 125 | e.friend = isFriend ~= false 126 | __util.native('lj_socialdata_upsync', e, driverName) 127 | end 128 | -------------------------------------------------------------------------------- /lib_web.lua: -------------------------------------------------------------------------------- 1 | __source 'lua/api_web.cpp' 2 | __namespace 'web' 3 | require './common/internal_import' 4 | 5 | local function encodeHeaders(headers) 6 | if headers == nil then return nil end 7 | local ret = nil 8 | for key, value in pairs(headers) do 9 | ret = (ret ~= nil and ret..'\n' or '')..key..'='..(value ~= nil and (type(value) == 'boolean' and (value and '1' or '0') or tostring(value)) or '') 10 | end 11 | return ret 12 | end 13 | 14 | local function parseHeaders(headers) 15 | local ret = {} 16 | if type(headers) == 'string' then 17 | for k, v in string.gmatch(headers, "([%a%d%-]+): ([%g ]+)\r\n") do 18 | ret[k:lower()] = v 19 | end 20 | end 21 | return ret 22 | end 23 | 24 | local function requestCallback(callback) 25 | if callback == nil then return 0 end 26 | return __util.expectReply(function (err, status, headers, response) 27 | callback(err, { status = status, headers = parseHeaders(headers), body = response }) 28 | end) 29 | end 30 | 31 | local function request(method, url, headers, data, callback) 32 | if url == nil then error('URL is required', 2) end 33 | 34 | local ret 35 | if type(data) == 'table' then 36 | if url == 'http://not-an-url' and data.filename == 'b:/nonexistent' then 37 | setTimeout(callback, 0) 38 | return 39 | end 40 | 41 | ret = ffi.C.lj_http_request_file__web(__util.str(method), __util.str(url), encodeHeaders(headers), data.filename, requestCallback(callback)) 42 | else 43 | ret = ffi.C.lj_http_request__web(__util.str(method), __util.str(url), encodeHeaders(headers), __util.blob(data), requestCallback(callback)) 44 | end 45 | 46 | if ret == false then 47 | setTimeout(function () 48 | callback('Invalid arguments', nil) 49 | end, 0) 50 | end 51 | end 52 | 53 | __definitions() 54 | 55 | return { 56 | request = request, 57 | socket = function(url, headers, callback, params) 58 | if type(headers) == 'function' then 59 | headers, callback, params = nil, headers, callback 60 | end 61 | local encodedParams = nil 62 | local dataEncode = nil 63 | local onError = 0 64 | local onClose = 0 65 | if type(params) == 'table' then 66 | encodedParams = {reconnect = not not params.reconnect} 67 | local dataDecode 68 | if params.encoding == 'json' then 69 | dataEncode, dataDecode = JSON.stringify, JSON.parse 70 | elseif params.encoding == 'lson' then 71 | dataEncode, dataDecode = function (data) return stringify(data, true, 40) end, stringify.tryParse 72 | else 73 | encodedParams.binary_mode = params.encoding == 'binary' 74 | end 75 | if dataDecode and callback then 76 | local callbackBak = callback 77 | callback = function (data) 78 | callbackBak(dataDecode(data)) 79 | end 80 | end 81 | if params.onError then 82 | onError = __util.setCallback(params.onError) 83 | end 84 | if params.onClose then 85 | onClose = __util.setCallback(params.onClose) 86 | end 87 | end 88 | local socket = ffi.C.lj_http_websocket__web(__util.str(url), encodeHeaders(headers), __util.setCallback(callback), onError, onClose, encodedParams and __util.json(encodedParams)) 89 | return setmetatable({ 90 | close = function () 91 | ffi.C.lj_http_websocket_send__web(socket, nil) 92 | end 93 | }, { 94 | __call = dataEncode 95 | and function (_, data) ffi.C.lj_http_websocket_send__web(socket, __util.blob(dataEncode(data))) end 96 | or function (_, data) if data ~= nil and (type(data) ~= 'string' or #data > 0) then ffi.C.lj_http_websocket_send__web(socket, __util.blob(data)) end end 97 | }) 98 | end 99 | } -------------------------------------------------------------------------------- /pfx_fireworks/ac_fireworks.lua: -------------------------------------------------------------------------------- 1 | ffi.cdef [[ 2 | typedef struct { 3 | int __id; 4 | vec3 pos; 5 | vec3 velocity; 6 | 7 | float sparkingIntensity; 8 | float sparkingIntensityShot; 9 | float sparkingLifespan; 10 | float sparkingSize; 11 | float sparkingStretch; 12 | float sparkingSpeedMin; 13 | float sparkingSpeedMax; 14 | vec3 sparkingDir; 15 | float sparkingDirSpreadXZ; 16 | float sparkingDirSpreadY; 17 | rgb sparkingColorA; 18 | rgb sparkingColorB; 19 | float sparkingPosSpread; 20 | int sparkingSpreadFlags; 21 | float sparkingLifespanSpread; 22 | float sparkingSizeSpread; 23 | float sparkingBrightness; 24 | 25 | vec3 glowOffset; 26 | float glowBrightness; 27 | float glowSize; 28 | rgb glowColor; 29 | 30 | vec3 smokingPosOffset; 31 | float smokingIntensity; 32 | float smokingIntensityShot; 33 | float smokingLifespan; 34 | float smokingSize; 35 | float smokingOpacity; 36 | float smokingSpeedRandomMin; 37 | float smokingSpeedRandomMax; 38 | rgb smokingColor; 39 | float smokingPosSpread; 40 | vec3 smokingVelocityOffset; 41 | 42 | float pushingForce; 43 | } firework; 44 | ]] 45 | 46 | ---@return ac.Firework 47 | function ac.Firework() 48 | local created = ffi.C.lj_firework_new() 49 | return ffi.gc(created, ffi.C.lj_firework_gc) 50 | end 51 | 52 | ---@class ac.Firework 53 | ---@field pos vec3 54 | ---@field velocity vec3 55 | ---@field sparkingIntensity number 56 | ---@field sparkingIntensityShot number 57 | ---@field sparkingLifespan number 58 | ---@field sparkingSize number 59 | ---@field sparkingStretch number 60 | ---@field sparkingSpeedMin number 61 | ---@field sparkingSpeedMax number 62 | ---@field sparkingDir vec3 63 | ---@field sparkingDirSpreadXZ number 64 | ---@field sparkingDirSpreadY number 65 | ---@field sparkingColorA rgb 66 | ---@field sparkingColorB rgb 67 | ---@field sparkingPosSpread number 68 | ---@field sparkingSpreadFlags integer 69 | ---@field sparkingLifespanSpread number 70 | ---@field sparkingSizeSpread number 71 | ---@field sparkingBrightness number 72 | ---@field glowOffset vec3 73 | ---@field glowBrightness number 74 | ---@field glowSize number 75 | ---@field glowColor rgb 76 | ---@field smokingPosOffset vec3 77 | ---@field smokingIntensity number 78 | ---@field smokingIntensityShot number 79 | ---@field smokingLifespan number 80 | ---@field smokingSize number 81 | ---@field smokingOpacity number 82 | ---@field smokingSpeedRandomMin number 83 | ---@field smokingSpeedRandomMax number 84 | ---@field smokingColor rgb 85 | ---@field smokingPosSpread number 86 | ---@field smokingVelocityOffset vec3 87 | ---@field pushingForce number 88 | ffi.metatype('firework', { 89 | __index = {} 90 | }) 91 | -------------------------------------------------------------------------------- /pfx_fireworks/ac_lists.d.lua: -------------------------------------------------------------------------------- 1 | --[[? return inc('.templates/list.tpl.lua', [ 'ac.fireworks', 'ac.Firework' ]); ?]] 2 | -------------------------------------------------------------------------------- /pfx_fireworks/ac_lists.lua: -------------------------------------------------------------------------------- 1 | ac.fireworks = __util.boundArray(ffi.typeof('firework*'), ffi.C.lj_set_fireworks) 2 | 3 | ---Adds a firework to the list of active fireworks. 4 | ---@param item ac.Firework 5 | function ac.addFirework(item) return ac.fireworks:pushWhereFits(item) end 6 | 7 | ---Removes a firework from the list of active fireworks. 8 | ---@param item ac.Firework 9 | function ac.removeFirework(item) return ac.fireworks:erase(item) end 10 | -------------------------------------------------------------------------------- /tests/run_compilation.lua: -------------------------------------------------------------------------------- 1 | if true then 2 | return (((100) * (((100))))) 3 | end 4 | 5 | if false then 6 | return 'remove me please' 7 | end 8 | 9 | for i = 1, 10 do 10 | goto ccc 11 | end 12 | ::ccc:: 13 | 14 | do 15 | end 16 | 17 | return ('hello'..'world') -------------------------------------------------------------------------------- /tests/run_compilation.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-custom-shaders-patch/acc-lua-sdk/07d7bf9c1d3e6a8514a0b044987460da620a9783/tests/run_compilation.raw -------------------------------------------------------------------------------- /tests/test_const.lua: -------------------------------------------------------------------------------- 1 | local MY_FIRST_CONST = const(17) 2 | local MY_FIRST_FLAG = const(1) 3 | local HAS_FLAG = const(function (x, FLAG) 4 | return bit.band(x, FLAG) ~= 0 5 | end) 6 | local ADD = const(function (x, y) 7 | return x + y 8 | end) 9 | 10 | expect(MY_FIRST_CONST, 17) 11 | expect(MY_FIRST_FLAG, 1) 12 | expect(HAS_FLAG(MY_FIRST_CONST, MY_FIRST_FLAG), true) 13 | expect(ADD(MY_FIRST_CONST, MY_FIRST_FLAG), 18) 14 | -------------------------------------------------------------------------------- /tests/test_enum_jit.lua: -------------------------------------------------------------------------------- 1 | local ffi = require('ffi') 2 | 3 | -- ---_@class MyEnum 4 | -- ---_@class MyEnum.En 5 | -- ---_@field BAZ MyEnum 6 | -- local enum = ffi.new[[ 7 | -- struct{ 8 | -- static const int FOO = 0; 9 | -- static const int BAR = 1; 10 | -- static const int BAZ = 7; 11 | -- static const int QUX = 8; 12 | -- static const int QUUX = 12; 13 | -- } 14 | -- ]] 15 | 16 | local enum = { 17 | BAZ = 7 18 | } 19 | 20 | ---@alias MyEnum 21 | ---| 'enum.BAZ' # BAR 22 | 23 | 24 | 25 | expect(enum.BAZ, 7) 26 | 27 | 28 | ---@param arg MyEnum 29 | function fn(arg) end 30 | 31 | fn(enum.BAZ) 32 | fn(enum.BAZ) 33 | fn(10) 34 | fn('enum.BAZ') 35 | -------------------------------------------------------------------------------- /tests/test_function.lua: -------------------------------------------------------------------------------- 1 | require('common/function') 2 | 3 | 4 | local function sum(a, b, c, d) 5 | return a + b + c + d 6 | end 7 | 8 | expect(sum(1, 10, 100, 1000), 1111) 9 | expect(sum:bind(2)(20, 200, 2000), 2222) 10 | expect(sum:bind(3, 30)(300, 3000), 3333) 11 | expect(sum:bind(4, 40, 400)(4000), 4444) 12 | expect(sum:bind(5, 50, 500, 5000)(), 5555) 13 | 14 | 15 | expect((sum ^ 10)(1, 100, 1000), 1111) 16 | expect((function () return 17 end ^ 10)(1, 100, 1000), 17) 17 | expect((function (a) return a end ^ 10)(1, 100, 1000), 10) 18 | expect((function (a, b) return a + b end ^ 10)(1, 100, 1000), 11) 19 | expect((function (a, b, c) return a + b + c end ^ 10)(1, 100, 1000), 111) -------------------------------------------------------------------------------- /tests/test_internal.lua: -------------------------------------------------------------------------------- 1 | require('common/internal') 2 | require('common/math') 3 | 4 | expect(__util.json(false), 'false') 5 | expect(__util.json(nil), 'null') 6 | expect(__util.json({ 'hello', 'world' }), '["hello","world"]') 7 | expect(__util.json({ 'he"llo', 'wo\nr\tl\rd' }), '["he\\"llo","wo\\nr\\tl\\rd"]') 8 | expectOneOf(__util.json({ key = 123, key2 = math.NaN }), { 9 | '{\"key\":123,\"key2\":null}', 10 | '{\"key2\":null,\"key\":123}' 11 | }) 12 | 13 | local t = { 1, 2 } 14 | table.insert(t, t) 15 | expect(__util.json(t), '[1,2,null]') -------------------------------------------------------------------------------- /tests/test_json.lua: -------------------------------------------------------------------------------- 1 | __definitions = function() end 2 | JSON.parse = require('lib_jsonparse') 3 | 4 | expect(JSON.stringify({{a = 5}, {b = 6}}), '[{"a":5},{"b":6}]') 5 | expect(JSON.parse('5'), 5) 6 | expect(JSON.parse('-17'), -17) 7 | expect(JSON.parse('true'), true) 8 | expect(JSON.parse('false'), false) 9 | expect(JSON.parse('null'), nil) 10 | expect(JSON.parse('[ 1, 1 ]'), { 1, 1 }) 11 | expect(JSON.parse('[ 1 1 ]'), { 1, 1 }) 12 | expect(JSON.parse('[ true, true ]'), { true, true }) 13 | expect(JSON.parse('[, true, true ]'), { true, true }) 14 | expect(JSON.parse('[,,,, 1, 2,, 3 ]'), { 1, 2, 3 }) 15 | expect(JSON.parse('"value"'), 'value') 16 | expect(JSON.parse('"val\nue"'), 'val\nue') 17 | expect(JSON.parse('"value'), 'value') 18 | expect(JSON.parse('"\\utest"'), 'test') 19 | expect(JSON.parse('"\\ud83d\\ude02"'), '😂') 20 | expect(JSON.parse('"va\\u00f8ue"'), 'vaøue') 21 | expect(JSON.parse('"va\\nlu\\te"'), 'va\nlu\te') 22 | expect(JSON.parse('[1,2,//test\n,3]'), {1,2,3}) 23 | expect(JSON.parse('[1,2,3,]'), {1,2,3}) 24 | expect(JSON.parse('[ ]'), {}) 25 | expect(JSON.parse(' [ 1,\t2\n,\r3 ] '), {1,2,3}) 26 | expect(JSON.parse('{"a":5,"b":6}'), {a=5, b=6}) 27 | expect(JSON.parse('{"a":5,"b":6,}'), {a=5, b=6}) 28 | expect(JSON.parse('//comment\n{"a":5,"b":6,}'), {a=5, b=6}) 29 | expect(JSON.parse('/*comment*/{"a":5,"b":6,}'), {a=5, b=6}) 30 | expect(JSON.parse(' {\t"a"\n: 5, "b " : 6 }'), {a=5, ['b ']=6}) 31 | expect(JSON.parse('{"a"//test\n:5,,"b":16}'), {a=5, b=16}) 32 | expect(JSON.parse('{"a"://test\n5,,"b":26}'), {a=5, b=26}) 33 | expect(JSON.parse('{,, "a" : 5,, "b":6, }'), {a=5, b=6}) 34 | expect(JSON.parse('{,, "a" 5 "b":6, }'), {a=5, b=6}) 35 | expect(JSON.parse('{,, true : 5,, "b":106, }'), {['true']=5, b=106}) 36 | expect(JSON.parse('{,, true//test\n : 5,, "b":116, }'), {['true']=5, b=116}) 37 | expect(JSON.parse('{,, a : 5,, "b":126, }'), {a=5, b=126}) 38 | expect(JSON.parse('{,, a : 5,, "b":136, } some trash'), {a=5, b=136}) 39 | expect(JSON.parse('some trash'), nil) 40 | expect(JSON.parse('[some trash'), {}) 41 | 42 | local traffic = '{"lanes":[{"loop":null}],"areas":[124]}' 43 | -- print(stringify(JSON.parse(traffic))) 44 | expect(#JSON.parse(traffic).lanes, 1) 45 | -------------------------------------------------------------------------------- /tests/test_luashader.lua: -------------------------------------------------------------------------------- 1 | __definitions = function() end 2 | require('lib_shader') 3 | 4 | local function verifyPattern(template, params, texSlots, ret) 5 | local r = _lsfg(template, params, texSlots, ret) 6 | if type(loadstring(r)()) ~= 'function' then 7 | error('Does not compute: '..r) 8 | end 9 | return r 10 | end 11 | 12 | expect(verifyPattern({}, {}, {}), 'return function(d, params, C)\ 13 | end') 14 | 15 | expect(verifyPattern({}, {values = {}}, {}), 'return function(d, params, C)\ 16 | end') 17 | 18 | expect(verifyPattern({}, {values = {key = 1}}, {}), 'return function(d, params, C)\ 19 | local cb, values = d.d, params.values\ 20 | cb.key = values.key or 0\ 21 | end') 22 | 23 | expect(verifyPattern({}, {values = {mat = mat4x4()}}, {}), 'return function(d, params, C)\ 24 | local cb, values = d.d, params.values\ 25 | if values.mat ~= nil then cb.mat = values.mat cb.mat:transposeSelf() end\ 26 | end') 27 | 28 | local inp = {values = {mat = mat4x4.identity()}, directValuesExchange = true} 29 | local ret = {d = {mat = 1}} 30 | expect(verifyPattern({}, inp, {}, ret), 'return function(d, params, C)\ 31 | if type(params.values) == \"table\" then local cb, values = d.d, params.values\ 32 | if values.mat ~= nil then cb.mat = values.mat cb.mat:transposeSelf() end\ 33 | params.__values_bak = params.values params.values = cb end\ 34 | end') 35 | expect(ret.d.mat.row4.w, 1) 36 | expect(inp.values == ret.d, true) 37 | 38 | expect(verifyPattern({}, {values = {}}, {'txDiffuse'}), 'return function(d, params, C)\ 39 | local textures = params.textures\ 40 | C.lj_cshader_settexture_slot_1(1, tostring(textures.txDiffuse))\ 41 | end') 42 | 43 | expect(verifyPattern({}, {values = {}}, {'txA', 'txB'}), 'return function(d, params, C)\ 44 | local textures = params.textures\ 45 | C.lj_cshader_settexture_slot_2(1, tostring(textures.txA), tostring(textures.txB))\ 46 | end') 47 | 48 | expect(verifyPattern({}, {values = {}}, {'txA', 'txB', 'txC'}), 'return function(d, params, C)\ 49 | local textures = params.textures\ 50 | C.lj_cshader_settexture_slot_3(1, tostring(textures.txA), tostring(textures.txB), tostring(textures.txC))\ 51 | end') 52 | 53 | expect(verifyPattern({}, {values = {}}, {'txA', 'txB', 'txC', 'txD'}), 'return function(d, params, C)\ 54 | local textures = params.textures\ 55 | C.lj_cshader_settexture_slot_4(1, tostring(textures.txA), tostring(textures.txB), tostring(textures.txC), tostring(textures.txD))\ 56 | end') 57 | 58 | expect(verifyPattern({}, {values = {}}, {'txA', 'txB', 'txC', 'txD', 'txE'}), 'return function(d, params, C)\ 59 | local textures = params.textures\ 60 | C.lj_cshader_settexture_slot_5(1, tostring(textures.txA), tostring(textures.txB), tostring(textures.txC), tostring(textures.txD), tostring(textures.txE))\ 61 | end') 62 | 63 | expect(verifyPattern({}, {values = {}}, {'txA', 'txB', 'txC', 'txD', 'txE', 'txF'}), 'return function(d, params, C)\ 64 | local textures = params.textures\ 65 | C.lj_cshader_settexture_slot_5(1, tostring(textures.txA), tostring(textures.txB), tostring(textures.txC), tostring(textures.txD), tostring(textures.txE))\ 66 | C.lj_cshader_settexture_slot_1(6, tostring(textures.txF))\ 67 | end') 68 | 69 | expect(verifyPattern({}, {values = {}}, {[3] = 'txA', [4] = 'txB', [5] = 'txC', [6] = 'txD', [7] = 'txE', [8] = 'txF', [9] = 'txG'}), 'return function(d, params, C)\ 70 | local textures = params.textures\ 71 | C.lj_cshader_settexture_slot_5(3, tostring(textures.txA), tostring(textures.txB), tostring(textures.txC), tostring(textures.txD), tostring(textures.txE))\ 72 | C.lj_cshader_settexture_slot_1(8, tostring(textures.txF))\ 73 | C.lj_cshader_settexture_slot_1(9, tostring(textures.txG))\ 74 | end') 75 | 76 | expect(verifyPattern({delayed = true}, {values = {}}, {[3] = 'txA', [4] = 'txB', [5] = 'txC', [6] = 'txD', [7] = 'txE', [8] = 'txF', [9] = 'txG'}), 'return function(d, params, C)\ 77 | local textures = params.textures\ 78 | C.lj_cshader_delaytexture_slot_5(d.s, 3, tostring(textures.txA), tostring(textures.txB), tostring(textures.txC), tostring(textures.txD), tostring(textures.txE))\ 79 | C.lj_cshader_delaytexture_slot_1(d.s, 8, tostring(textures.txF))\ 80 | C.lj_cshader_delaytexture_slot_1(d.s, 9, tostring(textures.txG))\ 81 | end') 82 | 83 | expect(verifyPattern({}, {values = {}}, {'txBase.1'}), 'return function(d, params, C)\ 84 | local textures = params.textures\ 85 | C.lj_cshader_settexture_slot_1(1, tostring(textures[\"txBase.1\"]))\ 86 | end') 87 | 88 | -- expect(type(loadstring([[ 89 | -- return function(d, params, C) 90 | -- C.lj_cshader_settexture_slot_1(0, tostring(params.textures.txBase.1)) 91 | -- cb, values = d.d, params.values 92 | -- if values.gBlurRadius ~= nil then cb.gBlurRadius = values.gBlurRadius 93 | -- end 94 | -- d.blend_mode = tonumber(params.blendMode) or 0 95 | -- end 96 | -- ]])()), 'string') -------------------------------------------------------------------------------- /tests/test_table.lua: -------------------------------------------------------------------------------- 1 | require('common/table') 2 | 3 | expect(table.join({ 1, 2, 3 }), '1,2,3') 4 | expect(table.join({ [0] = 1, 2, 3 }), '1,2,3') 5 | expect(table.join({ a=1 }), 'a=1') 6 | expect(table.slice({ 1, 2, 3 }, 1, nil, 3)[1], 1) 7 | expect(table.slice({ 1, 2, 3 }, 2, nil, 3)[1], 2) 8 | expect(table.slice({ 1, 2, 3 }, 3, nil, 3)[1], 3) 9 | expect(table.slice({ [0] = 1, 2, 3 }, 2, nil, 3)[1], 3) 10 | expect(table.reverse({1, 2, 3}), {3, 2, 1}) 11 | expect(table.reverse({1, 2, 3, 4}), {4, 3, 2, 1}) 12 | expect(table.reverse({[0] = 1, 2, 3}), {3, 2, 1}) 13 | expect(table.reverse({[0] = 1, 2, 3, 4}), {4, 3, 2, 1}) 14 | 15 | expect(table.contains({ [0] = 1, 2, 3 }, 1), true) 16 | expect(table.contains({ [0] = 1, 2, 3 }, 0), false) 17 | expect(table.removeItem({ [0] = 1, 2, 3 }, 1), true) 18 | 19 | expect(table.slice({ 1, 2, 3 }, 1, nil, 3), { 1 }) 20 | expect(table.slice({ 1, 2, 3, 4, 5, 6 }, 1, nil, 3), { 1, 4 }) 21 | expect(table.slice({ 1, 2, 3, 4, 5, 6 }, 3, nil, 3), { 3, 6 }) 22 | 23 | expect(table.map({ 1, 2, 3 }, function (i) return i * 2 end), {2, 4, 6}) 24 | expect(table.map({ 1, 2, 3 }, function (i) return i * 2, i end), {2, 4, 6}) 25 | expect(table.map({ 1, 2, 3 }, function (i) return i, i * 2 end), {[2]=1, [4]=2, [6]=3}) 26 | expect(table.map({ 1, 2, 3 }, function (i) return i ~= 2 and i or nil end), {1, 3}) 27 | expect(table.map({ [0] = 1, 2, 3 }, function (i) return i ~= 2 and i or nil end), {1, 3}) 28 | 29 | expect(table.isArray({}), true) 30 | expect(table.isArray({ 1, 2 }), true) 31 | expect(table.isArray({ [1] = 1 }), true) 32 | expect(table.isArray({ [1] = 1, [2] = 2 }), true) 33 | expect(table.isArray({ [1] = 1, [2] = 2, [3] = 3 }), true) 34 | expect(table.isArray({ [1] = 1, [2] = 2, [3] = 3, [4] = 4 }), true) 35 | expect(table.isArray({ [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5 }), true) 36 | expect(#{ [1] = 1, [2] = 2 }, 2) 37 | expect(table.isArray({ [2] = 2 }), false) -- difference is my and OpenResty logic 38 | expect(require('table.isarray')({ [2] = 2 }), true) -- I guess {[2]=2} is an array inside, but what does it matter if # returns 0? 39 | expect(#{ [2] = 2 }, 0) 40 | expect(table.isArray({ k = 'v' }), false) 41 | expect(table.isArray({ 1, 2, k = 'v' }), false) 42 | 43 | expect(table.isArray({ [1] = 1, [999] = 2 }), false) 44 | expect(#{ [1] = 1, [999] = 2 }, 1) 45 | expect(next({ [1] = 1, [999] = 2 }), 999) 46 | expect(next({ [1] = 1, [999] = 2 }, 999), 1) 47 | expect(next({ [1] = 1, [999] = 2 }, 1), nil) 48 | 49 | expect(table.isArray({ [3] = 1, [2] = 2, [1] = 3 }), true) 50 | 51 | -- expect(table.isArray({ [1] = 1, [2] = 2, [3] = 3 }), true) 52 | expect(table.isArray({ [0] = 1, [1] = 2, [2] = 3 }), true) 53 | 54 | expect(table.findFirst({1,2,3,0.3,4}, function (v) return v % 2 == 0 end), 2) 55 | expect(table.maxEntry({1,2,3,0.3,4}), 4) 56 | expect(table.minEntry({1,2,3,0.3,4}), 0.3) 57 | expect(table.distinct({1,2,3,3,4}), {1,2,3,4}) 58 | expect(table.distinct({[0]=1,2,3,3,4}), {1,2,3,4}) 59 | -- expect(table.distinct({k=1,v=2,t=2}), {k=1,v=2}) 60 | expect(table.chain({1, 2}, {3, 4}), {1,2,3,4}) 61 | expect(table.chain({1, 2}, {3, 4}, {{5, 6}}), {1,2,3,4,{5,6}}) 62 | expect(table.flatten{ {1, 2}, {3, 4} }, {1,2,3,4}) 63 | expect(table.flatten({ {1, 2}, {3, {4, 5}} }), {1,2,3,{4,5}}) 64 | expect(table.flatten({ {1, 2}, {3, {4, 5}} }, 1), {1,2,3,{4,5}}) 65 | expect(table.flatten({ {1, 2}, {3, {4, 5}} }, 2), {1,2,3,4,5}) 66 | 67 | expect(table.same({}, {}), true) 68 | expect(table.same({}, nil), false) 69 | expect(table.same(nil, nil), true) 70 | expect(table.same(nil, {}), false) 71 | expect(table.same({1,2}, {1,2}), true) 72 | expect(table.same({key=1,2}, {key=1,2}), true) 73 | expect(table.same({key=1,2}, {key=1.1,2}), false) 74 | expect(table.same({key=1.1,2,{a=5}}, {key=1.1,2,{a=5}}), true) 75 | expect(table.same({[0]=1, 2}, {1}), false) 76 | expect(table.same({[0]=1, 2}, {[0]=1, 2}), true) 77 | 78 | local x = 0 79 | local function counter() 80 | x = x + 1 81 | return x 82 | end 83 | 84 | local ordered = { 85 | k1 = counter(), 86 | k2 = counter(), 87 | k3 = counter(), 88 | k4 = counter(), 89 | k5 = counter(), 90 | k6 = counter(), 91 | k7 = counter(), 92 | k8 = counter(), 93 | k9 = counter(), 94 | k10 = counter(), 95 | k11 = counter(), 96 | } 97 | expect(ordered.k1, 1) 98 | expect(ordered.k2, 2) 99 | expect(ordered.k10, 10) -------------------------------------------------------------------------------- /tests/test_vec.lua: -------------------------------------------------------------------------------- 1 | expect((vec2(10, 12) + 4).y, 16) 2 | expect(vec2(10, 12) > nil, false) 3 | expect(vec2(10, 12) < nil, false) 4 | expect(vec2(10, 12) == nil, false) 5 | expect(nil == vec2(10, 12), false) 6 | expect(nil == ffi.cast('vec2*', 0)[0], false) 7 | expect(vec2.isvec2(ffi.cast('vec2*', 0)[0]), true) 8 | expectError(function () return 1 + nil end, 'attempt to perform arithmetic on a nil value') 9 | expectError(function () return nil + nil end, 'attempt to perform arithmetic on a nil value') 10 | expectError(function () return vec2() + nil end, '.+ attempt to perform arithmetic on local') 11 | expectError(function () return nil + vec2() end, '.+ attempt to index local') 12 | 13 | expect(vec2 == ffi.typeof(vec2()), true) 14 | 15 | expect(getmetatable(vec2).__stringify, nil) 16 | 17 | local t = {} 18 | t[tostring(vec2)] = true 19 | expect(tostring(vec2), tostring(ffi.typeof(vec2(10, 12)))) 20 | expect(t[tostring(ffi.typeof(vec2(10, 12)))], true) 21 | 22 | expect(vec2.isvec2(ffi.new(ffi.typeof(vec2()))), true) 23 | -- print(tostring(ffi.typeof(vec2()))) -------------------------------------------------------------------------------- /wfx_impl/ac_cloudscovers.lua: -------------------------------------------------------------------------------- 1 | ffi.cdef [[ 2 | typedef struct { 3 | rgb colorMultiplier; 4 | float opacityMultiplier; 5 | 6 | rgb colorExponent; 7 | float opacityExponent; 8 | 9 | float opacityCutoff; 10 | float opacityFade; 11 | float texOffsetX; 12 | float texRemapY; 13 | 14 | float fogMultZenith; 15 | float fogMultDelta; 16 | float fogRangeInv; 17 | float fogMultExponent; 18 | 19 | float shadowRadius; 20 | float shadowOpacityMultiplier; 21 | float maskOpacityMultiplier; 22 | float texScaleX; 23 | 24 | bool ignoreTextureAlpha; 25 | } cloudscover; 26 | ]] 27 | 28 | 29 | ---Special cloud-like thing for drawing 360° cloud textures. 30 | ---@class ac.SkyCloudsCover 31 | ---@field colorMultiplier rgb 32 | ---@field opacityMultiplier number 33 | ---@field colorExponent rgb 34 | ---@field opacityExponent number 35 | ---@field opacityCutoff number 36 | ---@field opacityFade number 37 | ---@field texOffsetX number 38 | ---@field texRemapY number 39 | ---@field fogMultZenith number 40 | ---@field fogMultDelta number 41 | ---@field fogRangeInv number 42 | ---@field fogMultExponent number 43 | ---@field shadowRadius number 44 | ---@field shadowOpacityMultiplier number 45 | ---@field maskOpacityMultiplier number 46 | ---@field texScaleX number 47 | ---@field ignoreTextureAlpha boolean @If set to `true`, texture alpha is only used for main rendering pass, not for casting shadow. Default value: `false`. 48 | ---@constructor fun(): ac.SkyCloudsCover 49 | ffi.metatype('cloudscover', { 50 | __index = { 51 | 52 | ---@param filename string 53 | ---@param maxSize number? @If non-zero, sets a maximum size for MIPs to load. Any MIPs larger than that will be skipped. Only works with BC6H/BC7 compression, or with DXT1/3/5/RGBA8888 if using new loader option is enabled. 54 | setTexture = function (s, filename, maxSize) ffi.C.lj_cloudscover_set_texture__impl(s, tostring(filename), tonumber(maxSize) or 0) end, 55 | 56 | getTextureState = ffi.C.lj_cloudscover_get_texture_state__impl, 57 | 58 | ---@param filename string 59 | ---@param maxSize number? @If non-zero, sets a maximum size for MIPs to load. Any MIPs larger than that will be skipped. Only works with BC6H/BC7 compression, or with DXT1/3/5/RGBA8888 if using new loader option is enabled. 60 | setMaskTexture = function (s, filename, maxSize) ffi.C.lj_cloudscover_set_mask_texture__impl(s, tostring(filename), tonumber(maxSize) or 0) end, 61 | 62 | getMaskTextureState = ffi.C.lj_cloudscover_get_mask_texture_state__impl, 63 | 64 | setFogParams = function (s, fogHorizon, fogZenith, fogExponent, fogRangeMult) 65 | s.fogMultZenith = tonumber(fogZenith) or 1 66 | s.fogMultDelta = (tonumber(fogHorizon) or 1) - s.fogMultZenith 67 | s.fogRangeInv = 1 / math.max(0.01, tonumber(fogRangeMult) or 1) 68 | s.fogMultExponent = tonumber(fogExponent) or 1 69 | end 70 | } 71 | }) 72 | function ac.SkyCloudsCover() return ffi.gc(ffi.C.lj_cloudscover_new__impl(), ffi.C.lj_cloudscover_gc__impl) end 73 | -------------------------------------------------------------------------------- /wfx_impl/ac_customtonemapping.lua: -------------------------------------------------------------------------------- 1 | local _sp_ctt = {template = 'tonemapping.fx', __cache = {}, startingTextureSlot = 4, delayed = true} 2 | 3 | ---Override current tonemapping function. If you’re using a table and need texture coordinates, add `__CSP_PROVIDE_TEXCOORDS = true` define. 4 | ---@param v ac.TonemapFunction|string|{textures: table, values: table, defines: table, shader: string, cacheKey: integer} 5 | function ac.setPpTonemapFunction(v) 6 | if type(v) == 'table' then 7 | local dc = __util.setShaderParams2(v, _sp_ctt) 8 | if not dc then return end 9 | ffi.C.lj_setPpTonemapFunction_dynamic__impl(dc) 10 | elseif type(v) == 'string' then 11 | ffi.C.lj_setPpTonemapFunction_str__impl(v) 12 | else 13 | ffi.C.lj_setPpTonemapFunction_base__impl(__util.cast_enum(v, 0, 14, 0)) 14 | end 15 | end 16 | 17 | -------------------------------------------------------------------------------- /wfx_impl/ac_gradients.lua: -------------------------------------------------------------------------------- 1 | ffi.cdef [[ 2 | typedef struct { 3 | vec3 direction; 4 | float sizeFull, sizeStart; 5 | bool isAdditive; 6 | bool isIncludedInCalculate; 7 | 8 | struct { 9 | rgb color; 10 | float exponent; 11 | }; 12 | } extragradient; 13 | ]] 14 | 15 | ffi.metatype('extragradient', { __index = {} }) 16 | 17 | ---@class ac.SkyExtraGradient 18 | ---@field direction vec3 19 | ---@field sizeFull number 20 | ---@field sizeStart number 21 | ---@field isAdditive boolean 22 | ---@field isIncludedInCalculate boolean 23 | ---@field color rgb 24 | ---@field exponent number 25 | ---@constructor fun(t: { direction: vec3, sizeFull: number, sizeStart: number, isAdditive: boolean, isIncludedInCalculate: boolean, color: rgb, exponent: number }?): ac.SkyExtraGradient 26 | 27 | function ac.SkyExtraGradient(t) 28 | local r = ffi.C.lj_extragradient_new__impl() 29 | if type(t) == 'table' then 30 | if vec3.isvec3(t.direction) then r.direction = t.direction end 31 | if rgb.isrgb(t.color) then r.color = t.color end 32 | if type(t.sizeFull) == 'number' then r.sizeFull = t.sizeFull end 33 | if type(t.sizeStart) == 'number' then r.sizeStart = t.sizeStart end 34 | if type(t.exponent) == 'number' then r.exponent = t.exponent end 35 | if type(t.isAdditive) == 'boolean' then r.isAdditive = t.isAdditive end 36 | if type(t.isIncludedInCalculate) == 'boolean' then r.isIncludedInCalculate = t.isIncludedInCalculate end 37 | end 38 | return ffi.gc(r, ffi.C.lj_extragradient_gc__impl) 39 | end -------------------------------------------------------------------------------- /wfx_impl/ac_lightpollution.lua: -------------------------------------------------------------------------------- 1 | ---@class ac.LightPollution 2 | ---@field position vec3 3 | ---@field radius number @Radius in meters. 4 | ---@field tint rgb 5 | ---@field density number 6 | ffi.cdef [[ typedef struct { vec3 position; float radius; rgb tint; float density; } light_pollution; ]] 7 | ac.LightPollution = ffi.metatype('light_pollution', { 8 | __index = {}, 9 | __tostring = function(v) 10 | return string.format('(position=%s, radius=%f, density=%f, tint=%s)', v.position, v.radius, v.density, v.tint) 11 | end, 12 | }) -------------------------------------------------------------------------------- /wfx_impl/ac_lists.d.lua: -------------------------------------------------------------------------------- 1 | --[[? return inc('.templates/list.tpl.lua', 2 | [ 'ac.weatherClouds', 'ac.SkyCloudBase' ], 3 | [ 'ac.weatherCloudsCovers', 'ac.SkyCloudsCover' ], 4 | [ 'ac.skyExtraGradients', 'ac.SkyExtraGradient' ], 5 | [ 'ac.weatherColorCorrections', 'ac.ColorCorrectionBase' ]); ?]] 6 | -------------------------------------------------------------------------------- /wfx_impl/ac_lists.lua: -------------------------------------------------------------------------------- 1 | ac.weatherClouds = __util.boundArray(ffi.typeof('cloud*'), ffi.C.lj_set_clouds__impl) 2 | ac.weatherCloudsCovers = __util.boundArray(ffi.typeof('cloudscover*'), ffi.C.lj_set_cloudscovers__impl) 3 | ac.skyExtraGradients = __util.boundArray(ffi.typeof('extragradient*'), ffi.C.lj_set_gradients__impl) 4 | ac.weatherColorCorrections = __util.boundArray(ffi.typeof('void*'), ffi.C.lj_set_corrections__impl) 5 | 6 | ac.addWeatherCloud = function(cloud) return ac.weatherClouds:pushWhereFits(cloud) end 7 | ac.addWeatherCloudCover = function(cloud) return ac.weatherCloudsCovers:pushWhereFits(cloud) end 8 | ac.addSkyExtraGradient = function(gradient) return ac.skyExtraGradients:pushWhereFits(gradient) end 9 | ac.addWeatherColorCorrection = function(cc) return ac.weatherColorCorrections:pushWhereFits(cc) end 10 | ac.removeWeatherCloud = function(cloud) return ac.weatherClouds:erase(cloud) end 11 | ac.removeWeatherCloudCover = function(cloud) return ac.weatherCloudsCovers:erase(cloud) end 12 | ac.removeSkyExtraGradient = function(gradient) return ac.skyExtraGradients:erase(gradient) end 13 | ac.removeWeatherColorCorrection = function(cc) return ac.weatherColorCorrections:erase(cc) end 14 | -------------------------------------------------------------------------------- /wfx_impl/ac_obsolete.lua: -------------------------------------------------------------------------------- 1 | ---@deprecated 2 | ac.SHADOWS_ON = 1 3 | 4 | ---@deprecated 5 | ac.SHADOWS_OFF = 0 6 | 7 | ---@deprecated 8 | function ac.setSkySunSize(v) end 9 | 10 | ---@deprecated 11 | function ac.setSunAngle(v) end 12 | 13 | ---@deprecated 14 | function ac.setCustomSunDirection(v) end 15 | 16 | ---@deprecated 17 | function ac.setReflectionsLuminanceBoost(v) end 18 | 19 | ---@deprecated 20 | function ac.setSkyExtraGradient(id, gradient) 21 | if id < 0 or id >= 32 then error('Gradient ID should be within 0-31 range', 2) end 22 | ac.skyExtraGradients:set(id + 1, gradient) 23 | end 24 | 25 | ---@deprecated 26 | ac.addSkyExtraGradients = ac.addSkyExtraGradient 27 | -------------------------------------------------------------------------------- /wfx_impl/ac_particlematerials.lua: -------------------------------------------------------------------------------- 1 | ---@class ac.ParticlesMaterial 2 | ---@field emissiveBlend number 3 | ---@field diffuse number 4 | ---@field ambient number 5 | ffi.cdef [[ typedef struct { float emissiveBlend, diffuse, ambient; } particles_material; ]] 6 | ac.ParticlesMaterial = ffi.metatype('particles_material', { __index = {} }) 7 | -------------------------------------------------------------------------------- /wfx_impl/ac_postprocessing.lua: -------------------------------------------------------------------------------- 1 | ---Sets a callback which will be called when AC tries to apply YEBIS post-processing. At this point final LDR 2 | ---render target is already bound, input textures can be accessed as `dynamic::pp::hdr` and `dynamic::pp::depth`. 3 | ---Could be a good place to add extra effects to HDR buffer or even replace YEBIS with a custom call. Or you can 4 | ---just alter values in `params`: this is the structure that will be passed to YEBIS as post-processing settings. 5 | ---@param callback fun(params: ac.PostProcessingParameters, exposure: number, mainPass: boolean, updateExposure: boolean, rtSize: vec2): nil|boolean|ui.ExtraCanvas @Callback function. Return `true` to stop YEBIS. Return an extra canvas and it’ll be used as an HDR input to YEBIS instead (make sure it’s in HDR format and with the same resolution). Make sure to check `mainPass` parameter: if it’s `false`, you’re rendering an extra canvas with YEBIS post-processing mode, so maybe tune down features and apply the most basic stuff (and don’t use YEBIS antialiasing in your canvases here). If you’re doing autoexposure, don’t change things if `updateExposure` is set to `false` (this usually means you got a second eye in VR, don’t change brightness for it). 6 | ---@return ac.Disposable 7 | function ac.onPostProcessing(callback) 8 | return __util.lazy('lib_postprocessing')(callback) 9 | end --------------------------------------------------------------------------------