Files
ReSkate/Extension/Skater/Test/trick_watch_tests.cpp
T
AyeZeeBB a3e75ba10e Plugins: the reskate-test and reskate-hostile plugins, and trick reading and a facing teleport in the core
- contrib/plugins/reskate-test calls every plugin API and says what worked: ptest all, ptest
  <module>, ptest list. Its 45 checks cover the base set, timers, the level, package files, JSON,
  require, the console, storage and the sandbox. It is the example to copy for a plugin.
- contrib/plugins/reskate-hostile attacks the sandbox, one attack per command (hostile list):
  loops, memory and buffer bombs, caught errors, error and log floods, timers, recursion, an
  unload that never ends, and 17 ways out. Each must end stopped or refused.
- dingosdk_plugin_tests runs both from the repository: every reskate-test check passes, and every
  hostile attack is stopped or refused with nothing escaping.
- Taken from Domination (PR 279) for the plugin API to use: the trick watch (when a trick starts,
  lands and what it scored, or a slam breaks it; Domination's spots left out) in
  Extension/Skater/trick_watch.h with its tests, the score HUD reader in score_hud.h, and
  teleport_local_skater_heading, whose hunks are the PR's own. Neither is wired to anything yet.
2026-10-19 12:08:26 -05:00

107 lines
4.4 KiB
C++

// Reading tricks from the score HUD's values, apart from the game: a trick's start, its points
// once it lands, a slam, and a trick whose points never come. Moved with the trick watch from
// Domination (PR 279), which tests its spots on top.
#include "Extension/Skater/trick_watch.h"
#include <cmath>
#include <iostream>
#include <stdexcept>
using namespace dingosdk::tricks;
namespace {
void check(bool value, const char* message) { if (!value) throw std::runtime_error(message); }
TrickSignals tick(bool action, float score, float multiplier, bool slam = false, bool wipeout = false) {
TrickSignals in;
in.action = action;
in.line_score = score;
in.multiplier = multiplier;
in.slam = slam;
in.wipeout = wipeout;
return in;
}
// The last event over `ticks` updates.
TrickEvent feed(TrickWatch& watch, const TrickSignals& in, int ticks) {
TrickEvent last;
for (int i = 0; i < ticks; ++i) {
const auto e = watch.update(in);
if (e.kind != TrickEvent::Kind::none) last = e;
}
return last;
}
using K = TrickEvent::Kind;
void a_trick_starts_then_lands_with_its_points() {
TrickWatch w;
feed(w, tick(false, 0, 0), 5);
check(w.update(tick(true, 0, 0)).kind == K::started, "A trick starts the tick ActionActive goes on");
check(feed(w, tick(true, 0, 0), 30).kind == K::none && w.in_trick(), "and says it once");
check(w.update(tick(false, 0, 0)).kind == K::none && w.waiting(), "Its points are not in the tick it ends");
const auto e = w.update(tick(false, 11, 1.5f));
check(e.kind == K::landed && std::abs(e.points - 11) < 0.01f && std::abs(e.multiplier - 1.5f) < 0.01f,
"Landed: 11 points, the first trick of a line");
}
void a_second_trick_in_a_line_scores_its_own_points() {
TrickWatch w;
feed(w, tick(false, 0, 0), 5);
feed(w, tick(true, 0, 0), 30);
w.update(tick(false, 0, 0));
w.update(tick(false, 11, 1.5f));
feed(w, tick(true, 11, 1.5f), 20);
w.update(tick(false, 11, 1.5f));
const auto f = w.update(tick(false, 708, 2.0f));
const float points = (708 / 2.0f - 11 / 1.5f) * 2.0f;
check(f.kind == K::landed && std::abs(f.points - points) < 0.01f, "The multiplier's rise scales only the new trick's points");
feed(w, tick(true, 708, 2.0f), 20);
w.update(tick(false, 708, 2.0f));
const auto g = w.update(tick(false, 958, 2.0f));
check(g.kind == K::landed && std::abs(g.points - 250) < 0.01f, "and the next one its own again");
}
void a_slam_ends_the_trick_for_nothing() {
TrickWatch w;
feed(w, tick(false, 0, 1), 2);
feed(w, tick(true, 0, 1), 10);
feed(w, tick(false, 300, 1), 2);
feed(w, tick(true, 300, 1), 10);
const auto e = w.update(tick(true, 300, 1, true));
check(e.kind == K::broken && !w.in_trick(), "A slam ends the trick going on");
check(feed(w, tick(false, 0, 0), 30).kind == K::none, "which scores nothing");
check(feed(w, tick(false, 0, 0, false, true), 2).kind == K::none, "A wipeout with nothing going on says nothing");
feed(w, tick(true, 0, 1), 5);
check(w.update(tick(false, 0, 1, false, true)).kind == K::broken, "and one while the points are awaited breaks it");
}
void a_trick_whose_points_never_come_is_skipped() {
TrickWatch w(5);
feed(w, tick(false, 50, 1), 2);
feed(w, tick(true, 50, 1), 10);
const auto none = feed(w, tick(false, 50, 1), 10);
check(!w.waiting(), "Waiting gives up after its ticks");
feed(w, tick(true, 50, 1), 10);
const auto e = feed(w, tick(false, 90, 1), 2);
check(none.kind == K::none && e.kind == K::landed && std::abs(e.points - 40) < 0.01f, "and the next trick is still read");
}
void the_line_ending_is_not_a_trick() {
TrickWatch w;
feed(w, tick(false, 500, 2), 5);
check(feed(w, tick(false, 0, 0), 5).kind == K::none, "The line's score going back to 0 is no event");
check(feed(w, tick(false, 100, 1), 5).kind == K::none, "nor is a rise with no trick before it");
}
}
int main() {
try {
a_trick_starts_then_lands_with_its_points();
a_second_trick_in_a_line_scores_its_own_points();
a_slam_ends_the_trick_for_nothing();
a_trick_whose_points_never_come_is_skipped();
the_line_ending_is_not_a_trick();
std::cout << "trick watch tests passed\n";
return 0;
} catch (const std::exception& error) {
std::cerr << "trick watch test failed: " << error.what() << '\n';
return 1;
}
}