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:
AyeZeeBB committed 2026-10-22 18:46:49 -05:00
1 parent 8ede361755
commit 118c489fdb
25 files changed
+1449 -89

No files matched your search

@@ -58,12 +58,49 @@ void presses() {
expect(high && !first_person, "the third press of each round returns to the high camera");
}
}
// With a plugin's camera in the cycle: high -> low -> first person -> the plugin's camera -> high, and
// without first person (or on foot, where it cannot start) high -> low -> the plugin's camera -> high.
void plugin() {
for (const bool with_first_person : {true, false}) {
bool high = true, first_person = false, on = false;
const auto press = [&] {
CycleState state{with_first_person, first_person, !first_person && !on, true, on, !on};
const auto write = cycle_height_write(state, !high);
high = write.high;
if (write.step == CycleStep::enter) first_person = true;
if (write.step == CycleStep::leave) first_person = false;
if (write.plugin == CycleStep::enter) on = true;
if (write.plugin == CycleStep::leave) on = false;
};
for (int round = 0; round < 3; ++round) {
expect(high && !first_person && !on, "each round starts on the high camera");
press();
expect(!high && !first_person && !on, "first press: low camera");
if (with_first_person) {
press();
expect(!high && first_person && !on, "second press: first person");
}
press();
expect(!high && !first_person && on, "then the plugin's camera, the low camera behind it");
press();
}
expect(high && !first_person && !on, "out of the plugin's camera: the high camera");
}
// No plugin in the cycle: the first-person cycle as before.
expect(same(cycle_height_write({true, true, false, false, false, true}, true), true, CycleStep::leave),
"without a plugin's camera, first person -> high");
// Turned on by the plugin itself over the high camera: the button still takes it out to high.
const auto out = cycle_height_write({false, false, true, true, true, false}, false);
expect(out.high && out.plugin == CycleStep::leave, "a plugin's camera on by itself: the button leaves it for high");
}
} // namespace
int main() {
off();
steps();
presses();
plugin();
if (failures) {
std::printf("%d first-person cycle test(s) failed\n", failures);
return 1;
+33 -6
View File
@@ -1,4 +1,5 @@
#pragma once
#include <atomic>
#include <cstdint>
namespace dingosdk::first_person {
@@ -7,20 +8,46 @@ namespace dingosdk::first_person {
// (true is the high camera). With the option on, ReSkate turns that into high -> low -> first
// person -> high by changing the value written: the step into first person keeps the low camera
// behind it, and the step out of first person always lands on the high camera.
//
// A plugin's camera can be in the cycle too, last (reskate.camera.cycle): high -> low -> first
// person -> the plugin's camera -> high (without first person in it, or where first person cannot
// start: high -> low -> the plugin's camera -> high). Its step keeps the low camera behind it as well.
enum class CycleStep : std::uint8_t { none, enter, leave };
struct CycleState {
bool enabled{}; // the option is on and the local skater is in control
bool first_person{}; // first person has the camera
bool can_enter{}; // first person can take the camera now: on the board, no Freecam
bool plugin_enabled{}; // a plugin's camera is in the cycle now
bool plugin{}; // that camera is on
bool plugin_can_enter{}; // that camera is off and can start
};
struct CycleWrite {
bool high{}; // the height the game keeps
CycleStep step{}; // what first person does
bool high{}; // the height the game keeps
CycleStep step{}; // what first person does
CycleStep plugin{}; // what the plugin's camera does
};
constexpr CycleWrite cycle_height_write(const CycleState& state, bool high) noexcept {
if (!state.enabled) return {high, CycleStep::none};
if (state.first_person) return {true, CycleStep::leave};
if (high && state.can_enter) return {false, CycleStep::enter};
return {high, CycleStep::none};
// The plugin's camera is last: out of it, the high camera.
if (state.plugin_enabled && state.plugin) return {true, CycleStep::none, CycleStep::leave};
if (state.enabled && state.first_person) {
if (state.plugin_enabled && state.plugin_can_enter) return {false, CycleStep::leave, CycleStep::enter};
return {true, CycleStep::leave, CycleStep::none};
}
if (high) { // the game steps low -> high: first person, else the plugin's camera, comes in between
if (state.enabled && state.can_enter) return {false, CycleStep::enter, CycleStep::none};
if (state.plugin_enabled && state.plugin_can_enter) return {false, CycleStep::none, CycleStep::enter};
}
return {high, CycleStep::none, CycleStep::none};
}
// The plugin's half of the link (the plugin runtime keeps it). Each tick the plugin in the cycle has
// its flags published; the height write (on the thread that handles the button) leaves the step it
// asks of the plugin's camera in `request`, taken on the plugin runtime's next tick.
enum PluginFlag : std::uint8_t { plugin_flag_enabled = 1, plugin_flag_on = 2, plugin_flag_can_enter = 4 };
struct PluginLink {
std::atomic<std::uint8_t> flags{};
std::atomic<std::uint8_t> request{}; // a CycleStep
std::atomic<std::uint64_t> published{}; // GetTickCount64 when the flags were published
};
inline PluginLink plugin_link{};
}