Files
Zachary Gladfelter 230b4ba6e4 Trainer: teleport to the pause-map waypoint, onto the surface there
PRACTICE > TELEPORT now shows the waypoint the player placed on the
game's pause map, with a "Go to map waypoint" button. The console gets
`trainer waypoint [info]` and `trainer ground <x> <z>`.

Reading the waypoint (trainer_waypoint.cpp)
- It is a point of interest in the map's registry, the one ReSkate's
  party markers register into (addr::native_party::map_manager), with
  kind DingoMapPOIType_Waypoint (3) and its world matrix at @0.
- The registry is walked only while the TELEPORT card is drawn (the
  page notes it, like the class list) or the command runs, every
  250 ms at most. A player who never opens the card pays nothing.
- A registry the map has filled without a waypoint means it was
  removed; an empty or unreadable one keeps the last waypoint seen. A
  new level forgets it.

Landing on the surface (trainer_landing.h)
- The waypoint's height is not the ground: teleporting to it dropped
  the skater through the map. local_ground_height() casts the park
  editor's downward ray (editor::cast_surface) through the client
  physics world and takes the topmost hit.
- The first ray starts at 1500 or 500 above the hint. A custom map
  can sit higher than that (a waypoint at Y 12 over ground at Y 1939
  was seen in game), so a miss is cast again from 20000.
- If collision there is still streaming in, the teleport goes to the
  hint's height and a 12 s watch puts the skater on the surface when
  it arrives (at most 6 corrections; the teleport is re-sent up to 3
  times if it never arrives).
- Teleports still go through go_to(), so they follow the host's
  noclip / teleport permission in a session.

The decisions (ray spans, the watch, remember/forget) have no game in
them and are tested by trainer_landing (DINGOSDK_BUILD_TRAINER_TESTS).
README and CHANGELOG updated.
2026-10-07 05:08:31 -04:00

47 lines
1.5 KiB
C++

#include "trainer.h"
#include <atomic>
#include <mutex>
// The snapshots the menu and HUD read. Kept apart from trainer.cpp so the overlay library
// links without the game-facing half.
namespace dingosdk::trainer {
namespace {
struct Feed {
std::mutex mutex;
std::shared_ptr<const View> view = std::make_shared<const View>();
Telemetry telemetry;
std::atomic<bool> class_list_shown{};
std::atomic<bool> teleport_card_shown{};
};
Feed &feed() {
static auto *value = new Feed;
return *value;
}
} // namespace
std::shared_ptr<const View> view() noexcept {
auto &f = feed();
std::lock_guard lock(f.mutex);
return f.view;
}
Telemetry telemetry() noexcept {
auto &f = feed();
std::lock_guard lock(f.mutex);
return f.telemetry;
}
void publish(std::shared_ptr<const View> next) noexcept {
if (!next) return;
auto &f = feed();
std::lock_guard lock(f.mutex);
f.view = std::move(next);
}
void publish(const Telemetry &next) noexcept {
auto &f = feed();
std::lock_guard lock(f.mutex);
f.telemetry = next;
}
void note_class_list_shown() noexcept { feed().class_list_shown.store(true, std::memory_order_release); }
bool take_class_list_shown() noexcept { return feed().class_list_shown.exchange(false, std::memory_order_acq_rel); }
void note_teleport_card_shown() noexcept { feed().teleport_card_shown.store(true, std::memory_order_release); }
bool take_teleport_card_shown() noexcept { return feed().teleport_card_shown.exchange(false, std::memory_order_acq_rel); }
} // namespace dingosdk::trainer