mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-23 18:45:57 +02:00
Plugins register modes (card, replaces, order, flag, players) and publish documents through reskate.throwdowns.ui: the setup page, the waiting card, HUD slots themed on the game's HUD_<theme>_* widgets, the match panel, flags and match restrictions. SmokeyDawg58's native plumbing (throwdown_menu.cpp and its .inl files) renders them and posts throwdown_ui events back. kit::flag is the one native flag registration. General services added for it: reskate.files, reskate.ui.dialog, reskate.skater push/hold/bone_cam, reskate.camera.pad_flight, reskate.physics competition, reskate.session.world, reskate.tricks feed/gesture, and world entities. His ten modes are now Lua in his plugin: the rules, referee and wire (td/rules, referee, wire), and every page and HUD document (td/ui*.lua). Extension/Modes and reskate.modes are gone, along with every race and 1-UP branch in the SDK. The plugin test drives each mode through kit events. A native setup page is never written to while it closes into flag placement: its rows are not republished, the stock page is put back once the exit is over, and the stock Players slider is left alone while a plugin flag is out. This fixes the crashes on the Deathrace button and on 1-UP's Confirm.
68 lines
2.9 KiB
C++
68 lines
2.9 KiB
C++
#pragma once
|
|
#include "Engine/Core/Platform/memory.h"
|
|
#include "Engine/Game/Build/addresses.h"
|
|
#include "Engine/Game/Build/20260929/client_source_spawn.h"
|
|
#include "Engine/Game/Build/20260929/engine.h"
|
|
#include "Engine/Game/UI/game_view.h"
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cmath>
|
|
#include <numbers>
|
|
#include <optional>
|
|
|
|
// World positions on screen, through the camera as it is now (as the nametags read it). The plugin host projects with it on the client thread (world_to_screen) and the
|
|
// overlay on the render thread (world shapes), each with the camera of its own moment.
|
|
namespace dingosdk::plugin_camera {
|
|
inline std::atomic<std::uintptr_t>& image_base() {
|
|
static std::atomic<std::uintptr_t> value{};
|
|
return value;
|
|
}
|
|
|
|
// The latest game view, with the camera's own matrix and field of view read again when the
|
|
// camera is one this build's types say it is: the game moves its camera after the update the view
|
|
// was taken in.
|
|
inline std::optional<GameView> camera_now() {
|
|
auto view = latest_game_view();
|
|
if (!view) return std::nullopt;
|
|
const auto base = image_base().load(std::memory_order_relaxed);
|
|
if (!base || !view->camera || (view->camera & 7)) return view;
|
|
std::uintptr_t vtable{};
|
|
if (!memory::peek(view->camera, vtable) ||
|
|
(vtable != base + addr::engine::camera_vtable && vtable != base + addr::client_source_spawn::free_camera_vtable))
|
|
return view;
|
|
std::array<float, 16> world{};
|
|
float fov{};
|
|
if (!memory::peek(view->camera + 0x50, world) || !memory::peek(view->camera + 0xac, fov)) return view;
|
|
if (!std::isfinite(fov) || fov <= 1 || fov >= 175) return view;
|
|
for (const auto value : world)
|
|
if (!std::isfinite(value) || std::abs(value) > 1e7f) return view;
|
|
view->world = world;
|
|
view->vertical_fov = fov;
|
|
return view;
|
|
}
|
|
|
|
struct Projector {
|
|
std::array<float, 16> m{};
|
|
float focal{}, width{}, height{};
|
|
// x, y in pixels and the depth in front of the camera, or empty behind it (or too close).
|
|
std::optional<std::array<float, 3>> project(const std::array<float, 3>& p) const {
|
|
const std::array<float, 3> d{p[0] - m[12], p[1] - m[13], p[2] - m[14]};
|
|
const auto dot = [&](int row) { return d[0] * m[row] + d[1] * m[row + 1] + d[2] * m[row + 2]; };
|
|
const float depth = -dot(8);
|
|
if (depth < 0.05f) return std::nullopt;
|
|
return std::array<float, 3>{width * 0.5f + dot(0) * focal / depth, height * 0.5f - dot(4) * focal / depth, depth};
|
|
}
|
|
};
|
|
// The camera now, for a screen of this size; empty without a camera.
|
|
inline std::optional<Projector> projector(float width, float height) {
|
|
const auto view = camera_now();
|
|
if (!view || !(view->vertical_fov > 1 && view->vertical_fov < 175) || width <= 0 || height <= 0) return std::nullopt;
|
|
Projector p;
|
|
p.m = view->world;
|
|
p.width = width;
|
|
p.height = height;
|
|
p.focal = height / (2.0f * std::tan(view->vertical_fov * std::numbers::pi_v<float> / 360.0f));
|
|
return p;
|
|
}
|
|
}
|