mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-24 01:45:58 +02:00
Plugins: what the Filmer needs as a plugin: carry, who sees a pose, a step in the camera cycle
PR 343's Filmer becomes a Luau plugin mod (kept in contrib/plugins/filmer) on general APIs instead of
its own C++:
- reskate.skater.carry: something held out in one or both hands and aimed, the body crouched (feet
kept) and hunched over it, turned on the spot after it (Extension/Skater/carry.cpp, from the PR's
filmer_hold.cpp, with tests)
- shown = "everyone" | "others" | "me" on skater.pose, reach and carry: the pose sent to the other
players and the one drawn here are split when asked
- reskate.camera.cycle and the camera_cycle event: a plugin's camera as the last step of the camera
height button's cycle, after first person
- reskate.session.nametags(false) holds the names over the other players off
- board.hide(bone, {others = true}) hides it in the board the others are sent too (a sent-pose filter)
- outfit.set takes hand wear slots; input.bind takes an action with no key of its own
This commit is contained in:
1 parent
8ede361755
commit
118c489fdb
25 files changed
+1449
-89
No files matched your search
@@ -225,6 +225,20 @@ struct WorldApi {
|
||||
need(L, plugin, "camera", "reskate.camera.held");
|
||||
return result(L, game(plugin).camera_status(plugin.id()));
|
||||
}
|
||||
// camera.cycle(state): the plugin's camera as a step of the camera height button's cycle: nil (out
|
||||
// of it), "ready" (in it, off) or "active" (in it, on). For 300 ms, so every tick.
|
||||
static int camera_cycle(lua_State* L) {
|
||||
auto& plugin = plugin_of(L);
|
||||
need(L, plugin, "camera", "reskate.camera.cycle");
|
||||
auto state = Game::CycleCamera::out;
|
||||
if (!lua_isnoneornil(L, 1)) {
|
||||
const std::string_view s = luaL_checkstring(L, 1);
|
||||
if (s == "ready") state = Game::CycleCamera::ready;
|
||||
else if (s == "active") state = Game::CycleCamera::active;
|
||||
else luaL_argerror(L, 1, "a camera's place in the cycle is nil, \"ready\" or \"active\"");
|
||||
}
|
||||
return result(L, game(plugin).camera_cycle(plugin.id(), state));
|
||||
}
|
||||
static int camera_get(lua_State* L) {
|
||||
auto& plugin = plugin_of(L);
|
||||
need(L, plugin, "camera", "reskate.camera.get");
|
||||
@@ -727,6 +741,15 @@ struct WorldApi {
|
||||
game(plugin).set_nametag(plugin.id(), player, std::move(text), colour_at(L, 3, 0xffffffff));
|
||||
return 0;
|
||||
}
|
||||
// session.nametags(shown): false holds the names over the other players off (until true, or the
|
||||
// plugin stops); chat bubbles still show.
|
||||
static int nametags(lua_State* L) {
|
||||
auto& plugin = plugin_of(L);
|
||||
need(L, plugin, "session.nametags", "reskate.session.nametags");
|
||||
luaL_checktype(L, 1, LUA_TBOOLEAN);
|
||||
game(plugin).hide_nametags(plugin.id(), !lua_toboolean(L, 1));
|
||||
return 0;
|
||||
}
|
||||
// 0xRRGGBBAA from Lua, as the overlay draws it (IM_COL32: 0xAABBGGRR).
|
||||
static std::uint32_t colour_at(lua_State* L, int index, std::uint32_t fallback) {
|
||||
if (lua_isnoneornil(L, index)) return fallback;
|
||||
@@ -1223,9 +1246,100 @@ struct WorldApi {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
// skater.reach({hand = "right"|"left", at = vector, rotation = {x, y, z, w}?, weight = 0..1}): the
|
||||
// hand joint taken to `at` in the world (turned to `rotation`), in the pose drawn and sent; for
|
||||
// 0.3 s, so every tick. nil lets the arm go.
|
||||
// A `shown` field: who sees a pose, a carry or a reach ("everyone", "others" or "me").
|
||||
static Game::Shown shown_field(lua_State* L, int table, const char* what) {
|
||||
lua_getfield(L, table, "shown");
|
||||
Game::Shown shown = Game::Shown::everyone;
|
||||
if (!lua_isnil(L, -1)) {
|
||||
const std::string_view s = luaL_checkstring(L, -1);
|
||||
if (s == "others") shown = Game::Shown::others;
|
||||
else if (s == "me") shown = Game::Shown::me;
|
||||
else if (s != "everyone") luaL_error(L, "%s's shown is \"everyone\", \"others\" or \"me\"", what);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return shown;
|
||||
}
|
||||
// A number field from 0 or `low` to `high`, `fallback` when not given.
|
||||
static float number_field(lua_State* L, int table, const char* key, float fallback, double low, double high, const char* message) {
|
||||
lua_getfield(L, table, key);
|
||||
float value = fallback;
|
||||
if (!lua_isnil(L, -1)) {
|
||||
const double v = luaL_checknumber(L, -1);
|
||||
if (!(v >= low && v <= high)) luaL_error(L, "%s", message);
|
||||
value = static_cast<float>(v);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return value;
|
||||
}
|
||||
// skater.carry({at = vector, rotation = {x, y, z, w}, hand, both, front, grip, weight, crouch, hunch,
|
||||
// still, turn, step, step_phase, push_straightens, shown}): something held out in the hands and
|
||||
// aimed, its front at `at`; for 0.3 s, so every tick. nil lets it go.
|
||||
static int carry(lua_State* L) {
|
||||
auto& plugin = plugin_of(L);
|
||||
need(L, plugin, "skater.animation", "reskate.skater.carry");
|
||||
if (lua_isnoneornil(L, 1)) return result(L, game(plugin).skater_carry(plugin.id(), std::nullopt));
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
Game::Carry goal;
|
||||
lua_getfield(L, 1, "hand");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
const std::string hand = luaL_checkstring(L, -1);
|
||||
if (hand != "right" && hand != "left") luaL_error(L, "a carry's hand is \"right\" or \"left\"");
|
||||
goal.left = hand == "left";
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "both");
|
||||
goal.both = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "at");
|
||||
goal.front = position_of(L, lua_gettop(L), "the carry's point");
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "rotation");
|
||||
if (lua_isnil(L, -1)) luaL_error(L, "a carry needs a rotation {x, y, z, w}: the way the thing held points");
|
||||
goal.aim = quaternion_at(L, lua_gettop(L));
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "front");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
goal.front_from_grip = position_of(L, lua_gettop(L), "a carry's front");
|
||||
for (const auto v : goal.front_from_grip)
|
||||
if (std::abs(v) > 1) luaL_error(L, "a carry's front is at most 1 m from its grip on each axis");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "grip");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
const int grip = lua_gettop(L);
|
||||
std::pair<std::array<float, 4>, Vec3> worn{{0, 0, 0, 1}, {}};
|
||||
lua_getfield(L, grip, "rotation");
|
||||
if (!lua_isnil(L, -1)) worn.first = quaternion_at(L, lua_gettop(L));
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, grip, "offset");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
worn.second = position_of(L, lua_gettop(L), "a grip's offset");
|
||||
for (const auto v : worn.second)
|
||||
if (std::abs(v) > 0.5f) luaL_error(L, "a grip's offset is at most 0.5 m from the hand on each axis");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
goal.worn = worn;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
goal.weight = number_field(L, 1, "weight", 1, 0, 1, "a carry's weight is 0 to 1");
|
||||
goal.crouch = number_field(L, 1, "crouch", 0, 0, 0.6, "a carry's crouch is 0 to 0.6 m");
|
||||
goal.hunch = number_field(L, 1, "hunch", 0, 0, 1.2, "a carry's hunch is 0 to 1.2 radians");
|
||||
goal.turn = number_field(L, 1, "turn", 0, -3.2, 3.2, "a carry's turn is -pi to pi radians");
|
||||
goal.step = number_field(L, 1, "step", 0, 0, 1, "a carry's step is 0 to 1");
|
||||
goal.step_phase = number_field(L, 1, "step_phase", 0, -1e6, 1e6, "a carry's step_phase is a finite number");
|
||||
lua_getfield(L, 1, "still");
|
||||
goal.still = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "push_straightens");
|
||||
goal.push_straightens = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
goal.shown = shown_field(L, 1, "a carry");
|
||||
return result(L, game(plugin).skater_carry(plugin.id(), goal));
|
||||
}
|
||||
// skater.reach({hand = "right"|"left", at = vector, rotation = {x, y, z, w}?, weight = 0..1, shown}):
|
||||
// the hand joint taken to `at` in the world (turned to `rotation`), in the pose drawn and sent (or
|
||||
// one of them); for 0.3 s, so every tick. nil lets the arm go.
|
||||
static int reach(lua_State* L) {
|
||||
auto& plugin = plugin_of(L);
|
||||
need(L, plugin, "skater.animation", "reskate.skater.reach");
|
||||
@@ -1252,6 +1366,7 @@ struct WorldApi {
|
||||
goal.weight = static_cast<float>(w);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
goal.shown = shown_field(L, 1, "a reach");
|
||||
return result(L, game(plugin).skater_reach(plugin.id(), goal));
|
||||
}
|
||||
// A skater's joint: a number 0..394 of the animation skeleton, or one of a few names.
|
||||
@@ -1334,7 +1449,13 @@ struct WorldApi {
|
||||
}
|
||||
const double weight = luaL_optnumber(L, 2, 1);
|
||||
if (!(weight >= 0 && weight <= 1)) luaL_argerror(L, 2, "a weight is 0 to 1");
|
||||
return result(L, game(plugin).skater_pose(plugin.id(), offsets, static_cast<float>(weight)));
|
||||
// options = {shown = "everyone" | "others" | "me"}
|
||||
auto shown = Game::Shown::everyone;
|
||||
if (!lua_isnoneornil(L, 3)) {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
shown = shown_field(L, 3, "a pose");
|
||||
}
|
||||
return result(L, game(plugin).skater_pose(plugin.id(), offsets, static_cast<float>(weight), shown));
|
||||
}
|
||||
// skater.joint(joint): {position, rotation, scale} of one of the local skater's joints, or nil.
|
||||
static int skater_joint(lua_State* L) {
|
||||
@@ -1540,7 +1661,15 @@ struct WorldApi {
|
||||
static int board_hide(lua_State* L) {
|
||||
auto& plugin = plugin_of(L);
|
||||
need(L, plugin, "board", "reskate.board.hide");
|
||||
return result(L, game(plugin).board_hide(plugin.id(), bone_number(L, 1)));
|
||||
// options = {others = true}: hidden from the other players too
|
||||
bool everyone = false;
|
||||
if (!lua_isnoneornil(L, 2)) {
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
lua_getfield(L, 2, "others");
|
||||
everyone = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return result(L, game(plugin).board_hide(plugin.id(), bone_number(L, 1), everyone));
|
||||
}
|
||||
static int board_clear(lua_State* L) {
|
||||
auto& plugin = plugin_of(L);
|
||||
@@ -1596,11 +1725,13 @@ struct WorldApi {
|
||||
need(L, plugin, "outfit", "reskate.outfit.set");
|
||||
const auto slot = checked_text(L, 1, 48, "a slot's name");
|
||||
const auto item = checked_text(L, 2, 200, "an item's name");
|
||||
// Only the face and skin layers, and eyewear (Extension/Skater/outfit_overlay.h).
|
||||
// Only the face and skin layers, eyewear and hand wear (Extension/Skater/outfit_overlay.h): the
|
||||
// game has no menu for hand wear, so a camera or a glove a plugin puts in the hand goes there.
|
||||
std::string lowered = slot;
|
||||
for (auto& c : lowered) c = static_cast<char>(c >= 'A' && c <= 'Z' ? c + 32 : c);
|
||||
if (lowered.find("tattoo") == std::string::npos && lowered.find("moles") == std::string::npos && lowered.find("eyewear") == std::string::npos)
|
||||
return result(L, "only tattoo, moles and eyewear slots");
|
||||
if (lowered.find("tattoo") == std::string::npos && lowered.find("moles") == std::string::npos && lowered.find("eyewear") == std::string::npos &&
|
||||
lowered.find("hand") == std::string::npos)
|
||||
return result(L, "only tattoo, moles, eyewear and hand wear slots");
|
||||
std::vector<float> placement;
|
||||
bool covers{};
|
||||
if (!lua_isnoneornil(L, 3)) {
|
||||
@@ -2179,10 +2310,11 @@ struct WorldApi {
|
||||
auto& plugin = plugin_of(L);
|
||||
need(L, plugin, "input", "reskate.input.bind");
|
||||
const auto action = checked_text(L, 1, 48, "an action's name");
|
||||
const auto key = checked_text(L, 2, 60, "a key");
|
||||
if (action.empty()) luaL_argerror(L, 1, "an action needs a name");
|
||||
const auto combo = combo_of(key);
|
||||
if (!combo) luaL_error(L, "\"%s\" is not a key: 0-9, A-Z, Space, F1-F12 with Ctrl, Shift or Alt; or Pad.A, Pad.LB+Pad.RB...", key.c_str());
|
||||
// nil: no key of its own; the player can put it on one in the Binds page.
|
||||
const auto key = lua_isnoneornil(L, 2) ? std::string{} : checked_text(L, 2, 60, "a key");
|
||||
const auto combo = key.empty() ? 0u : combo_of(key);
|
||||
if (!key.empty() && !combo) luaL_error(L, "\"%s\" is not a key: 0-9, A-Z, Space, F1-F12 with Ctrl, Shift or Alt; or Pad.A, Pad.LB+Pad.RB...", key.c_str());
|
||||
if (!plugin.binds_.contains(action) && plugin.binds_.size() >= 16) luaL_error(L, "at most 16 actions");
|
||||
if (const auto why = game(plugin).bind(plugin.id(), action, combo); !why.empty()) return result(L, why);
|
||||
plugin.binds_.insert(action);
|
||||
@@ -2500,10 +2632,10 @@ void WorldApi::install(Plugin& plugin, lua_State* L) {
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "reskate.permissions");
|
||||
add_functions(L, "skater", {{"state", state}, {"noclip", noclip}, {"boost", boost}, {"freeze", freeze}, {"wipeout", wipeout}, {"xray", xray},
|
||||
{"shape_launch", shape_launch}, {"hide", hide_skater}, {"reach", reach}, {"hand", hand}, {"joint", skater_joint}, {"pose", skater_pose}});
|
||||
{"shape_launch", shape_launch}, {"hide", hide_skater}, {"reach", reach}, {"carry", carry}, {"hand", hand}, {"joint", skater_joint}, {"pose", skater_pose}});
|
||||
add_functions(L, "board", {{"motion", board_motion}, {"bones", board_bones}, {"set", board_set}, {"hide", board_hide}, {"clear", board_clear},
|
||||
{"park", board_park}, {"fit", board_fit}, {"toss", board_toss}});
|
||||
add_functions(L, "camera", {{"look", camera_look}, {"set", camera_set}, {"follow", camera_follow}, {"release", camera_release}, {"held", camera_held}, {"get", camera_get}});
|
||||
add_functions(L, "camera", {{"look", camera_look}, {"set", camera_set}, {"follow", camera_follow}, {"release", camera_release}, {"held", camera_held}, {"get", camera_get}, {"cycle", camera_cycle}});
|
||||
add_functions(L, "trainer", {{"set_point", set_point}, {"back", back}});
|
||||
add_functions(L, "hud", {{"hide", hide_hud}, {"corner", hud_corner}});
|
||||
add_functions(L, "time", {{"slow", slow}, {"speed", speed}});
|
||||
@@ -2514,7 +2646,7 @@ void WorldApi::install(Plugin& plugin, lua_State* L) {
|
||||
add_functions(L, "world", {{"set_time", set_time}, {"set_control", set_control}, {"set_park", set_park}, {"pick", world_pick}});
|
||||
add_functions(L, "travel", {{"locations", locations}, {"levels", levels}, {"go", go}, {"load", load}});
|
||||
add_functions(L, "ai", {{"spawn", ai_spawn}, {"clear", ai_clear}, {"count", ai_count}});
|
||||
add_functions(L, "session", {{"skaters", skaters}, {"tag", tag}, {"joint", session_joint}, {"touch", session_touch},
|
||||
add_functions(L, "session", {{"skaters", skaters}, {"tag", tag}, {"nametags", nametags}, {"joint", session_joint}, {"touch", session_touch},
|
||||
{"touching", session_touching}, {"hide", session_hide}, {"hidden", session_hidden},
|
||||
{"raycast", session_raycast}});
|
||||
add_functions(L, "voice", {{"set_volume", voice_volume}});
|
||||
|
||||
Reference in new issue
Block a user