mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-26 05:56:39 +01: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
@@ -45,6 +45,13 @@ struct FakeGame final : Game {
|
||||
return {};
|
||||
}
|
||||
void outfit_clear(std::string_view plugin, std::string_view slot) override { calls.push_back(std::format("outfit clear {} {}", plugin, slot)); }
|
||||
std::string camera_cycle(std::string_view plugin, CycleCamera place) override {
|
||||
calls.push_back(std::format("cycle {} {}", plugin, place == CycleCamera::out ? "out" : place == CycleCamera::ready ? "ready" : "active"));
|
||||
return {};
|
||||
}
|
||||
std::vector<CycleNews> cycle_news;
|
||||
std::vector<CycleNews> take_camera_cycle() override { return std::exchange(cycle_news, {}); }
|
||||
void hide_nametags(std::string_view plugin, bool hidden) override { calls.push_back(std::format("nametags {} {}", plugin, hidden ? "off" : "on")); }
|
||||
std::set<std::string, std::less<>> installed; // items found by their whole ending, as Pack/<ending>
|
||||
std::string outfit_find(std::string_view ending) override {
|
||||
if (installed.contains(ending)) return "Pack/" + std::string(ending);
|
||||
@@ -60,7 +67,10 @@ struct FakeGame final : Game {
|
||||
calls.push_back(std::format("board {} {} {:.1f} {}", plugin, bone, value.position[1], world ? "world" : "local"));
|
||||
return {};
|
||||
}
|
||||
std::string board_hide(std::string_view plugin, int bone) override { calls.push_back(std::format("board hide {} {}", plugin, bone)); return {}; }
|
||||
std::string board_hide(std::string_view plugin, int bone, bool everyone) override {
|
||||
calls.push_back(std::format("board hide {} {}{}", plugin, bone, everyone ? " everyone" : ""));
|
||||
return {};
|
||||
}
|
||||
void board_park(std::string_view plugin, std::vector<int> bones) override { calls.push_back(std::format("board park {} {}", plugin, bones.size())); }
|
||||
// The general services a game layer in Lua reads and drives (plugin_api_services.cpp).
|
||||
std::optional<SkaterTelemetry> telemetry;
|
||||
@@ -150,8 +160,18 @@ struct FakeGame final : Game {
|
||||
return {};
|
||||
}
|
||||
std::optional<MouseMotion> mouse_motion(std::string_view) override { return MouseMotion{3, -2, 1}; }
|
||||
std::string skater_pose(std::string_view plugin, const std::vector<JointOffset>& offsets, float weight) override {
|
||||
static const char* shown_name(Shown shown) { return shown == Shown::others ? "others" : shown == Shown::me ? "me" : "everyone"; }
|
||||
std::string skater_carry(std::string_view plugin, std::optional<Carry> c) override {
|
||||
calls.push_back(c ? std::format("carry {} {}{} {:.1f} {:.1f} {:.1f} front {:.2f} grip {} w {:.2f} crouch {:.2f} hunch {:.2f} turn {:.2f} step {:.2f}{}{} {}",
|
||||
plugin, c->left ? "left" : "right", c->both ? " both" : "", c->front[0], c->front[1], c->front[2],
|
||||
c->front_from_grip[2], c->worn.has_value(), c->weight, c->crouch, c->hunch, c->turn, c->step,
|
||||
c->still ? " still" : "", c->push_straightens ? " push" : "", shown_name(c->shown))
|
||||
: std::format("carry {} none", plugin));
|
||||
return {};
|
||||
}
|
||||
std::string skater_pose(std::string_view plugin, const std::vector<JointOffset>& offsets, float weight, Shown shown) override {
|
||||
std::string text = std::format("pose {} {:.2f}", plugin, weight);
|
||||
if (shown != Shown::everyone) text += std::format(" {}", shown_name(shown));
|
||||
auto sorted = offsets;
|
||||
std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) { return a.joint < b.joint; });
|
||||
for (const auto& o : sorted) text += std::format(" {}:{:.2f},{:.2f},{:.2f},{:.2f}x{:.2f}", o.joint, o.rotation[0], o.rotation[1], o.rotation[2], o.rotation[3], o.scale);
|
||||
@@ -181,7 +201,8 @@ struct FakeGame final : Game {
|
||||
}
|
||||
std::string skater_reach(std::string_view plugin, std::optional<Reach> r) override {
|
||||
calls.push_back(r ? std::format("reach {} {} {:.1f} {:.1f} {:.1f} {} {:.2f}", plugin, r->left ? "left" : "right", r->at[0], r->at[1], r->at[2],
|
||||
r->rotation.has_value(), r->weight)
|
||||
r->rotation.has_value(), r->weight) +
|
||||
(r->shown != Shown::everyone ? std::format(" {}", shown_name(r->shown)) : std::string{})
|
||||
: std::format("reach {} none", plugin));
|
||||
return {};
|
||||
}
|
||||
@@ -1357,6 +1378,71 @@ void plugins_reach_cruise_and_read_the_mouse() {
|
||||
"skater.animation, skater.read, skater.control, input and overlay are needed\n" + environment.all());
|
||||
}
|
||||
|
||||
// What the Filmer's port needs besides: something carried (seen by the others only), a pose only you
|
||||
// see, a step in the camera height cycle and its event, the nametags held off, the board hidden from
|
||||
// everyone, and a camcorder in the hand wear slot.
|
||||
void plugins_carry_and_take_a_step_in_the_camera_cycle() {
|
||||
TestEnvironment environment;
|
||||
auto& game = environment.fake;
|
||||
Host host(environment, Side::client);
|
||||
const std::string main = R"(
|
||||
local steps = {}
|
||||
reskate.on("camera_cycle", function(step) table.insert(steps, step) end)
|
||||
assert(reskate.skater.carry({at = vector.create(1, 2, 3), rotation = {0, 1, 0, 0}, both = true, front = vector.create(0, -0.143, -0.248),
|
||||
grip = {rotation = {0.5, -0.5, 0.5, 0.5}, offset = vector.create(0.12, 0.04, -0.03)}, crouch = 0.22, hunch = 1.0, turn = 0.5,
|
||||
step = 0.5, step_phase = 1.25, still = true, push_straightens = true, weight = 0.75, shown = "others"}))
|
||||
assert(reskate.skater.carry({hand = "left", at = vector.create(0, 1, 0), rotation = {0, 0, 0, 1}}))
|
||||
assert(reskate.skater.carry(nil))
|
||||
assert(not pcall(reskate.skater.carry, {at = vector.create(0, 1, 0)}), "a carry needs a rotation")
|
||||
assert(not pcall(reskate.skater.carry, {at = vector.create(0, 1, 0), rotation = {0, 0, 0, 1}, crouch = 1}), "a crouch is 0 to 0.6 m")
|
||||
assert(not pcall(reskate.skater.carry, {at = vector.create(0, 1, 0), rotation = {0, 0, 0, 1}, shown = "them"}), "shown is everyone, others or me")
|
||||
assert(reskate.skater.pose({[48] = {scale = 0}}, 1, {shown = "me"}))
|
||||
assert(reskate.skater.reach({at = vector.create(1, 2, 3), shown = "others"}))
|
||||
assert(reskate.camera.cycle("ready") and reskate.camera.cycle("active") and reskate.camera.cycle(nil))
|
||||
assert(not pcall(reskate.camera.cycle, "on"), "ready or active")
|
||||
reskate.session.nametags(false)
|
||||
reskate.session.nametags(true)
|
||||
assert(reskate.board.hide(2, {others = true}))
|
||||
assert(reskate.outfit.set("handwear_l", "Own_HandwearGlove_Gen_GloveFingerless_00001"))
|
||||
local ok, why = reskate.outfit.set("shoes", "Own_Shoes_00001")
|
||||
assert(not ok and why == "only tattoo, moles, eyewear and hand wear slots", why)
|
||||
assert(reskate.input.bind("toggle") and reskate.input.bind("slap", "Pad.LB"), "an action with no key of its own, and one with")
|
||||
reskate.on("tick", function()
|
||||
if #steps > 0 then reskate.log("steps " .. table.concat(steps, " ")) steps = {} end
|
||||
end)
|
||||
reskate.log("carried")
|
||||
)";
|
||||
host.apply({package("filmer", {{manifest_file, manifest(R"("skater.animation", "camera", "session.nametags", "board", "outfit", "input")")},
|
||||
{"plugin/main.lua", main}})});
|
||||
ticks(host, environment, 1);
|
||||
running(host, "filmer", environment);
|
||||
check(environment.logged("[filmer] carried"), "The calls work\n" + environment.all());
|
||||
check(game.called("carry filmer right both 1.0 2.0 3.0 front -0.25 grip true w 0.75 crouch 0.22 hunch 1.00 turn 0.50 step 0.50 still push others") &&
|
||||
game.called("carry filmer left 0.0 1.0 0.0 front 0.00 grip false w 1.00 crouch 0.00 hunch 0.00 turn 0.00 step 0.00 everyone") &&
|
||||
game.called("carry filmer none"),
|
||||
"Carried in both hands for the others, in one, and let go\n" + environment.all());
|
||||
check(game.called("pose filmer 1.00 me 48:0.00,0.00,0.00,1.00x0.00") && game.called("reach filmer right 1.0 2.0 3.0 false 1.00 others"),
|
||||
"A pose only you see, a reach only the others see\n" + environment.all());
|
||||
check(game.called("cycle filmer ready") && game.called("cycle filmer active") && game.called("cycle filmer out"), "In the cycle, on, and out");
|
||||
check(game.called("nametags filmer off") && game.called("nametags filmer on"), "The nametags held off and given back");
|
||||
check(game.called("board hide filmer 2 everyone"), "The board hidden from everyone");
|
||||
check(game.called("outfit filmer handwear_l Own_HandwearGlove_Gen_GloveFingerless_00001 0 false"), "A camcorder in the hand wear slot");
|
||||
check(game.binds.contains("filmer/toggle") && game.binds["filmer/toggle"] == 0 && game.binds["filmer/slap"] == combo_of("Pad.LB"),
|
||||
"An action the player puts on a key themselves");
|
||||
// The button's steps reach the plugin in the cycle, and nobody else.
|
||||
game.cycle_news = {{"filmer", true}, {"someone", false}, {"filmer", false}};
|
||||
ticks(host, environment, 2);
|
||||
check(environment.logged("[filmer] steps enter leave"), "The camera cycle's steps as events\n" + environment.all());
|
||||
// Each needs its permission.
|
||||
host.apply({package("sneaky", {{manifest_file, manifest()}, {"plugin/main.lua", R"(
|
||||
reskate.log(tostring(pcall(reskate.skater.carry, {at = vector.create(0, 0, 0), rotation = {0, 0, 0, 1}})) .. " " ..
|
||||
tostring(pcall(reskate.camera.cycle, "ready")) .. " " .. tostring(pcall(reskate.session.nametags, false)) .. " " ..
|
||||
tostring(pcall(reskate.on, "camera_cycle", function() end)))
|
||||
)"}})});
|
||||
ticks(host, environment, 1);
|
||||
check(environment.logged("[sneaky] false false false false"), "skater.animation, camera and session.nametags are needed\n" + environment.all());
|
||||
}
|
||||
|
||||
// Reading a skater's joints, and the physics tuning read and set.
|
||||
void plugins_read_joints_and_tune_physics() {
|
||||
TestEnvironment environment;
|
||||
@@ -3227,6 +3313,77 @@ void the_throwdowns_page_lists_the_stock_cards_and_plugin_modes() {
|
||||
check(again.revision() > before && again.definitions().size() == 3 && again.definitions()[0].id == "native.skate", "S.K.A.T.E. is back when it goes");
|
||||
}
|
||||
|
||||
// The Filmer (contrib/plugins/filmer): an AI filmer follows a skater riding a carving line, then you film
|
||||
// someone, with your skater carrying the camcorder for the others; all within the 2 ms a tick a plugin has.
|
||||
void filmer_follows_and_films() {
|
||||
TestEnvironment environment;
|
||||
auto& game = environment.fake;
|
||||
const std::string plugin = "filmer";
|
||||
Host host(environment, Side::client);
|
||||
host.apply({contributed(plugin)});
|
||||
running(host, plugin, environment);
|
||||
SkaterTelemetry t;
|
||||
t.skater = true;
|
||||
t.physics_state = 100;
|
||||
t.speed = 6;
|
||||
game.telemetry = t;
|
||||
environment.local_skater = SkaterState{{0, 0.9f, 0}, 0, 6, 0, false, 0};
|
||||
host.set_level("sanvan");
|
||||
const auto ride = [&](int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto& skater = *environment.local_skater;
|
||||
static int step = 0;
|
||||
skater.heading = 0.4f * std::sin(static_cast<float>(++step) * 0.012f);
|
||||
skater.position[0] += std::sin(skater.heading) * 6.0f * 0.0167f;
|
||||
skater.position[2] += std::cos(skater.heading) * 6.0f * 0.0167f;
|
||||
game.telemetry->position = skater.position;
|
||||
game.telemetry->heading = skater.heading * 57.29578f;
|
||||
ticks(host, environment, 1);
|
||||
}
|
||||
};
|
||||
check(host.run_console("filmer mode follow"), "filmer mode follow");
|
||||
ride(900);
|
||||
running(host, plugin, environment);
|
||||
check(!environment.logged(".lua:"), "The Filmer follows without an error\n" + environment.all());
|
||||
check(game.called("camera filmer"), "It shows its camera\n" + environment.all());
|
||||
check(game.called("look filmer 1.00 100"), "with the VX look over the fisheye\n" + environment.all());
|
||||
check(game.called("cycle filmer active"), "in the camera height cycle, on");
|
||||
{
|
||||
// Where the AI filmer's lens is: behind the skater (never in front), a filmer's distance off, low.
|
||||
float x{}, y{}, z{};
|
||||
bool found = false;
|
||||
for (auto it = game.calls.rbegin(); it != game.calls.rend() && !found; ++it)
|
||||
if (it->starts_with("camera filmer ")) found = static_cast<bool>(std::istringstream(it->substr(14)) >> x >> y >> z);
|
||||
const auto& at = environment.local_skater->position;
|
||||
const float heading = environment.local_skater->heading;
|
||||
const float dx = x - at[0], dz = z - at[2];
|
||||
const float away = std::sqrt(dx * dx + dz * dz), along = dx * std::sin(heading) + dz * std::cos(heading);
|
||||
std::cout << "Filmer follow: the lens " << away << " m from the skater, " << along << " m along, " << y << " m up\n";
|
||||
check(found && away > 0.8f && away < 6.0f && along < 0.0f && y > 0.0f && y < 1.5f,
|
||||
"The AI filmer rides behind the skater, a filmer's distance off, low\n" + environment.all());
|
||||
}
|
||||
const auto follow_ms = status_of(host, plugin).tick_ms;
|
||||
std::cout << "Filmer follow: " << follow_ms << " ms a tick (its budget is 2 ms)\n";
|
||||
check(follow_ms < 2.0, "The Filmer follows within its 2 ms a tick");
|
||||
// You film the skater ahead of you, holding the camcorder out for the others to see.
|
||||
environment.lines.clear();
|
||||
check(host.run_console("filmer mode film"), "filmer mode film");
|
||||
ride(600);
|
||||
running(host, plugin, environment);
|
||||
check(!environment.logged(".lua:"), "You film without an error\n" + environment.all());
|
||||
check(game.called("carry filmer right both") && game.called(" push others") && game.called("hide skater filmer on") &&
|
||||
game.called("board hide filmer 2"),
|
||||
"Your skater carries the camcorder for the others, and you, looking through it, do not see your skater or board\n" + environment.all());
|
||||
check(game.called("hide filmer false false false stick true false true"), "The right stick and its click are the camera's");
|
||||
const auto film_ms = status_of(host, plugin).tick_ms;
|
||||
std::cout << "Filmer film: " << film_ms << " ms a tick\n";
|
||||
check(film_ms < 2.0, "You film within the 2 ms a tick");
|
||||
check(host.run_console("filmer slap") && environment.printed("Clip 1 marked"), "A slap marks a clip\n" + printed_lines(environment));
|
||||
check(host.run_console("filmer mode off"), "filmer mode off");
|
||||
ticks(host, environment, 30);
|
||||
check(game.called("carry filmer none") && game.called("camera release filmer"), "Off: the camcorder put down and the camera given back\n" + environment.all());
|
||||
}
|
||||
|
||||
void smokey_throwdowns_register_and_play_one_up() {
|
||||
namespace kit = dingosdk::throwdowns::kit;
|
||||
TestEnvironment environment;
|
||||
@@ -3665,6 +3822,7 @@ int main() {
|
||||
run(plugins_place_world_entities);
|
||||
run(plugins_have_what_a_prop_hunt_needs);
|
||||
run(plugins_reach_cruise_and_read_the_mouse);
|
||||
run(plugins_carry_and_take_a_step_in_the_camera_cycle);
|
||||
run(plugins_read_joints_and_tune_physics);
|
||||
run(plugins_type_and_use_the_room);
|
||||
run(plugins_touch_hide_and_ray_at_players);
|
||||
@@ -3697,6 +3855,7 @@ int main() {
|
||||
run(hall_of_meat_scores_a_bail);
|
||||
run(grind_exits_pick_the_side);
|
||||
run(smokey_throwdowns_register_and_play_one_up);
|
||||
run(filmer_follows_and_films);
|
||||
run(plugins_drive_the_throwdown_kit);
|
||||
run(fit_check_turns_the_board_and_saves_it);
|
||||
run(item_tabs_keep_tabs_and_wear_on_top);
|
||||
|
||||
Reference in new issue
Block a user