mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-26 16:16:41 +01:00
Throwdowns: fixes the docs review found in the kit and its services
- match.teleports lets through only the publishing plugin's own teleports, and only while its match runs (its HUD is active, or the native flag is one of its modes); it was merged across every plugin and checked no match. - A mode's timer of 0 or past an hour no longer makes its card do nothing: the native flag takes it as 1 to 3600 s. flag.begin's seconds are checked as 1 to 3600, as the flag takes them. - skater.push and hold(true) return false with a reason without the stock physics, where they were dropped unseen. - telemetry's slam and struck are news since each plugin's own last read; reading them cleared them for every plugin. - A banner or callout of a new match shows whatever serial it starts from, and a document without the slot no longer resets its serial. - SmokeyDawg58's placement no longer divides input.wheel's whole notches by WHEEL_DELTA again.
This commit is contained in:
1 parent
efeae7e07d
commit
bf693dbe5c
17 files changed
+81
-42
No files matched your search
@@ -63,7 +63,7 @@ struct FakeGame final : Game {
|
||||
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;
|
||||
std::optional<SkaterTelemetry> skater_telemetry() override { return telemetry; }
|
||||
std::optional<SkaterTelemetry> skater_telemetry(std::string_view) override { return telemetry; }
|
||||
std::uint64_t world_id() override { return 77; }
|
||||
std::string world_map() override { return "test_map"; }
|
||||
std::string pad_flight(std::string_view plugin, bool on) override {
|
||||
@@ -398,7 +398,7 @@ struct TestEnvironment final : Environment {
|
||||
std::vector<HttpResponse> take_http_responses() override { return std::exchange(responses, {}); }
|
||||
FakeGame fake;
|
||||
Game& game() override { return fake; }
|
||||
Teleport teleport(const Vec3& position, float heading) override {
|
||||
Teleport teleport(std::string_view, const Vec3& position, float heading) override {
|
||||
if (teleport_forbidden) return Teleport::forbidden;
|
||||
if (teleport_unavailable || !local_skater) return Teleport::unavailable;
|
||||
teleports.emplace_back(position, heading);
|
||||
|
||||
@@ -526,7 +526,7 @@ struct ApiFunctions {
|
||||
}
|
||||
const char* refusal = nullptr;
|
||||
if (++plugin.teleports_ > plugin.limits_.teleports_per_second) refusal = "asked more than twice in a second";
|
||||
else switch (plugin.services_.teleport(at, heading)) {
|
||||
else switch (plugin.services_.teleport(plugin.id(), at, heading)) {
|
||||
case Teleport::sent: break;
|
||||
case Teleport::forbidden: refusal = "the host has turned teleporting off"; break;
|
||||
case Teleport::unavailable: refusal = "teleporting is unavailable now (no skater, or a load)"; break;
|
||||
|
||||
@@ -61,7 +61,7 @@ void field_vec(lua_State* L, const char* name, const Vec3& v) {
|
||||
// skater.telemetry(): the trainer's telemetry, its last jump, the physics watch and the bail's body.
|
||||
int telemetry(lua_State* L) {
|
||||
auto& plugin = allowed(L, "skater.read", "reskate.skater.telemetry");
|
||||
const auto t = game_of(plugin).skater_telemetry();
|
||||
const auto t = game_of(plugin).skater_telemetry(plugin.id());
|
||||
if (!t) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
|
||||
@@ -134,7 +134,7 @@ struct KitApi {
|
||||
if (!lua_isnoneornil(L, 3)) {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
options.players = static_cast<unsigned>(number_field(L, 3, "players", options.players, 1, 10));
|
||||
options.seconds = static_cast<unsigned>(number_field(L, 3, "seconds", options.seconds, 0, 3600));
|
||||
options.seconds = static_cast<unsigned>(number_field(L, 3, "seconds", options.seconds, 1, 3600));
|
||||
options.setup_page = bool_field(L, 3, "setup_page", options.setup_page);
|
||||
options.cursor_height = static_cast<float>(number_field(L, 3, "cursor_height", options.cursor_height, 0, 100000));
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ struct WorldApi {
|
||||
if (!plugin.trainer_point_) return result(L, "no save point is set");
|
||||
if (!within(plugin.services_.now_ms(), plugin.teleport_second_, plugin.teleports_, plugin.limits_.teleports_per_second))
|
||||
return result(L, "asked more than twice in a second");
|
||||
switch (plugin.services_.teleport(plugin.trainer_point_->first, plugin.trainer_point_->second)) {
|
||||
switch (plugin.services_.teleport(plugin.id(), plugin.trainer_point_->first, plugin.trainer_point_->second)) {
|
||||
case Teleport::sent: return result(L, {});
|
||||
case Teleport::forbidden: return result(L, "the host has turned teleporting off");
|
||||
default: return result(L, "teleporting is unavailable now");
|
||||
|
||||
@@ -319,7 +319,8 @@ public:
|
||||
virtual throwdowns::kit::flag::State throwdown_flag_state() { return {}; }
|
||||
virtual void throwdown_flag_leave(std::string_view /*plugin*/) {}
|
||||
// ---- general services for a game layer in Lua (plugin_api_services.cpp) ----------------------
|
||||
virtual std::optional<SkaterTelemetry> skater_telemetry() { return std::nullopt; }
|
||||
// `plugin`: whose read it is (slam and struck are news since that plugin's last read).
|
||||
virtual std::optional<SkaterTelemetry> skater_telemetry(std::string_view /*plugin*/) { return std::nullopt; }
|
||||
// The pad's XInput button bits, LT 0x10000 and RT 0x20000 (empty: no pad). `menu`: read while
|
||||
// ReSkate's menu is open too.
|
||||
virtual std::optional<std::uint32_t> pad_bits(bool /*menu*/) { return std::nullopt; }
|
||||
|
||||
@@ -568,9 +568,12 @@ public:
|
||||
void dialog_close(std::string_view plugin, std::string_view id) override { overlay::close_plugin_dialog(plugin, id); }
|
||||
// ---- skater.push / hold --------------------------------------------------------------------------
|
||||
// A velocity the next on-board physics step writes (Extension/Skater/skater_push.h), and a hold
|
||||
// that keeps the board still.
|
||||
// that keeps the board still. Both are written on the game's stock physics only (client_noclip.cpp):
|
||||
// without it they would be dropped unseen, so they say so.
|
||||
static constexpr char needs_stock_physics[] = "needs the game's stock physics: reskate.physics.competition(true) first";
|
||||
std::string skater_push(std::string_view, const Vec3& velocity) override {
|
||||
if (!::dingosdk::skater_push::valid(velocity)) return "a push is horizontal (y 0) and at most 120 m/s";
|
||||
if (!competition_stock_physics()) return std::string("a push ") + needs_stock_physics;
|
||||
::dingosdk::skater_push::request(velocity);
|
||||
return {};
|
||||
}
|
||||
@@ -583,6 +586,7 @@ public:
|
||||
return {};
|
||||
}
|
||||
std::string skater_hold(std::string_view plugin, bool on) override {
|
||||
if (on && !competition_stock_physics()) return std::string("a hold ") + needs_stock_physics;
|
||||
if (on) hold_owner_ = plugin;
|
||||
else if (hold_owner_ != plugin) return {};
|
||||
else hold_owner_.clear();
|
||||
@@ -738,7 +742,7 @@ public:
|
||||
skater_animation::tick(base, local_skater_component(base, client), allowed);
|
||||
}
|
||||
// ---- general services for a game layer in Lua ------------------------------------------------------
|
||||
std::optional<SkaterTelemetry> skater_telemetry() override {
|
||||
std::optional<SkaterTelemetry> skater_telemetry(std::string_view plugin) override {
|
||||
const auto t = trainer::telemetry();
|
||||
const auto w = watched_physics_state();
|
||||
SkaterTelemetry m;
|
||||
@@ -757,8 +761,14 @@ public:
|
||||
j.spin, j.spin_rate, j.flip, j.board_turn, j.state, j.takeoff, j.landing};
|
||||
m.watch = {w.valid, w.ragdoll_known, w.ragdoll, w.state, w.previous, w.previous_seconds, w.changes, w.wipeouts, w.runouts};
|
||||
m.teleport_pending = local_skater_teleport_pending();
|
||||
m.slam = bonecam::bone_cam_slam();
|
||||
m.struck = bonecam::bone_cam_struck();
|
||||
// Each plugin hears of a slam or a strike once, whoever else reads the telemetry; its first
|
||||
// read only takes its place.
|
||||
const auto slams = bonecam::bone_cam_slams(), strikes = bonecam::bone_cam_strikes();
|
||||
auto seen = telemetry_seen_.find(plugin);
|
||||
if (seen == telemetry_seen_.end()) seen = telemetry_seen_.emplace(std::string(plugin), std::pair{slams, strikes}).first;
|
||||
m.slam = slams != seen->second.first;
|
||||
m.struck = strikes != seen->second.second;
|
||||
seen->second = {slams, strikes};
|
||||
m.body = bonecam::bail_body_position();
|
||||
return m;
|
||||
}
|
||||
@@ -967,6 +977,7 @@ public:
|
||||
hud_corner::release(plugin);
|
||||
game_speed::ask(plugin, 1.0f);
|
||||
grind_exit::release(plugin);
|
||||
if (const auto seen = telemetry_seen_.find(plugin); seen != telemetry_seen_.end()) telemetry_seen_.erase(seen);
|
||||
plugin_feed::release_tags(std::string(plugin));
|
||||
if (std::erase_if(binds_, [&](const auto& item) { return item.first.first == plugin; })) publish_binds();
|
||||
}
|
||||
@@ -995,6 +1006,8 @@ private:
|
||||
std::vector<std::string> installed;
|
||||
};
|
||||
std::map<std::string, VariantRead, std::less<>> variant_reads_;
|
||||
// The Bone Cam's slam and strike counts each plugin last read with its telemetry.
|
||||
std::map<std::string, std::pair<std::uint64_t, std::uint64_t>, std::less<>> telemetry_seen_;
|
||||
using BindKey = std::pair<std::string, std::string>;
|
||||
struct Bind {
|
||||
std::uint32_t own{};
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
virtual bool builtin_command(std::string_view word) = 0;
|
||||
// The game (Services, plugin_vm.h). Where there is none, there is no skater and nothing to hit.
|
||||
virtual std::optional<SkaterState> skater() { return std::nullopt; }
|
||||
virtual Teleport teleport(const Vec3&, float) { return Teleport::unavailable; }
|
||||
virtual Teleport teleport(std::string_view /*plugin*/, const Vec3&, float) { return Teleport::unavailable; }
|
||||
virtual std::optional<Vec3> raycast(const Vec3&, const Vec3&) { return std::nullopt; }
|
||||
virtual std::optional<ScoreState> score() { return std::nullopt; }
|
||||
virtual void toast(std::string_view /*plugin*/, LogLevel, std::string_view) {}
|
||||
@@ -126,7 +126,9 @@ private:
|
||||
std::filesystem::path storage_file(std::string_view plugin) override;
|
||||
std::string claim_command(std::string_view plugin, std::string_view name) override;
|
||||
std::optional<SkaterState> skater() override { return environment_.skater(); }
|
||||
Teleport teleport(const Vec3& position, float heading) override { return environment_.teleport(position, heading); }
|
||||
Teleport teleport(std::string_view plugin, const Vec3& position, float heading) override {
|
||||
return environment_.teleport(plugin, position, heading);
|
||||
}
|
||||
std::optional<Vec3> raycast(const Vec3& from, const Vec3& to) override { return environment_.raycast(from, to); }
|
||||
std::optional<ScoreState> score() override { return environment_.score(); }
|
||||
void toast(std::string_view plugin, LogLevel level, std::string_view text) override { environment_.toast(plugin, level, text); }
|
||||
|
||||
@@ -73,10 +73,15 @@ public:
|
||||
return SkaterState{t.position, heading, t.speed, t.vertical, t.airborne, t.air_time};
|
||||
}
|
||||
// As the console's tp: refused in a session whose host has turned teleporting (and noclip) off.
|
||||
Teleport teleport(const Vec3& position, float heading) override {
|
||||
Teleport teleport(std::string_view plugin, const Vec3& position, float heading) override {
|
||||
// A throwdown mode's own moves (line-ups, recoveries, returns to the start) are the game's:
|
||||
// allowed wherever its match runs and asks for them (the kit's `match.teleports`).
|
||||
if (!session_noclip_allowed() && !throwdowns::kit::match().teleports) return Teleport::forbidden;
|
||||
// allowed while that plugin's own match runs and asks for them (the kit's `match.teleports`),
|
||||
// never for another plugin's calls.
|
||||
if (!session_noclip_allowed()) {
|
||||
const auto flag = throwdowns::kit::flag::state();
|
||||
const bool mine = flag.owned && flag.mode.size() > plugin.size() && flag.mode.starts_with(plugin) && flag.mode[plugin.size()] == '/';
|
||||
if (!throwdowns::kit::allows_teleports(plugin, mine)) return Teleport::forbidden;
|
||||
}
|
||||
return teleport_local_skater_heading(position, heading) ? Teleport::sent : Teleport::unavailable;
|
||||
}
|
||||
std::optional<Vec3> raycast(const Vec3& from, const Vec3& to) override { return local_surface_hit(from, to); }
|
||||
|
||||
@@ -135,7 +135,8 @@ public:
|
||||
virtual std::string claim_command(std::string_view plugin, std::string_view name) = 0;
|
||||
// The local skater, or empty without one (the menu, a load).
|
||||
virtual std::optional<SkaterState> skater() = 0;
|
||||
virtual Teleport teleport(const Vec3& position, float heading) = 0;
|
||||
// `plugin`: whose call it is (a throwdown match of its own may let it through, kit::allows_teleports).
|
||||
virtual Teleport teleport(std::string_view plugin, const Vec3& position, float heading) = 0;
|
||||
// Where the line first meets the world's collision, or empty when it is clear (or not loaded).
|
||||
virtual std::optional<Vec3> raycast(const Vec3& from, const Vec3& to) = 0;
|
||||
// The score HUD now, or empty without one.
|
||||
|
||||
@@ -135,11 +135,11 @@ struct State {
|
||||
// The hips' recent speeds, to notice the body stopping dead (the ground, a pole, a wall).
|
||||
std::array<std::pair<std::uint64_t, float>, 24> hips_speeds{};
|
||||
std::size_t hips_next{};
|
||||
std::atomic<bool> slam_seen{};
|
||||
std::atomic<std::uint64_t> slams{};
|
||||
std::uint64_t slam_time{};
|
||||
// The head, chest, shoulders or hips striking an object at speed (bone_cam_struck), and when
|
||||
// any part last did.
|
||||
std::atomic<bool> struck_seen{};
|
||||
std::atomic<std::uint64_t> strikes{};
|
||||
std::uint64_t struck_time{};
|
||||
std::array<std::uint8_t, watch_count> level{};
|
||||
std::array<std::uint8_t, static_cast<std::size_t>(BoneSprite::count)> hurt{};
|
||||
@@ -336,7 +336,7 @@ void find_impacts(State &s, std::uint64_t now, bool injuring = true) {
|
||||
// the body hit something. Once in a while at most: on foot the measured speed
|
||||
// jitters, and one slam must not be counted thirty times.
|
||||
if (fastest >= 8.0f && speed < 2.5f && body_stopped && now - s.slam_time > 1500) {
|
||||
s.slam_seen.store(true);
|
||||
s.slams.fetch_add(1);
|
||||
s.slam_time = now;
|
||||
}
|
||||
s.hips_speeds[s.hips_next++ % s.hips_speeds.size()] = {now, speed};
|
||||
@@ -360,7 +360,7 @@ void find_impacts(State &s, std::uint64_t now, bool injuring = true) {
|
||||
// only counts once a bail is on).
|
||||
const auto j = watches[w].joint;
|
||||
const bool body = j == joint::head || j == joint::spine2 || j == joint::hips || j == joint::right_arm || j == joint::left_arm;
|
||||
if (body && now - s.struck_time > 1000) s.struck_seen.store(true);
|
||||
if (body && now - s.struck_time > 1000) s.strikes.fetch_add(1);
|
||||
s.struck_time = now;
|
||||
}
|
||||
const bool touching = on_ground || against || struck;
|
||||
@@ -606,9 +606,9 @@ void tick_bone_cam(std::uintptr_t base, std::uintptr_t client, bool playing) noe
|
||||
|
||||
void bone_cam_bail() noexcept { state().bail_pending.store(true); }
|
||||
|
||||
bool bone_cam_slam() noexcept { return state().slam_seen.exchange(false); }
|
||||
std::uint64_t bone_cam_slams() noexcept { return state().slams.load(); }
|
||||
|
||||
bool bone_cam_struck() noexcept { return state().struck_seen.exchange(false); }
|
||||
std::uint64_t bone_cam_strikes() noexcept { return state().strikes.load(); }
|
||||
|
||||
std::string bone_cam_setting() {
|
||||
switch (state().setting) {
|
||||
|
||||
@@ -70,13 +70,13 @@ void bone_cam_bail() noexcept;
|
||||
// and strikes are followed then even with the Bone Cam off. An owner's ask goes with release.
|
||||
void bone_cam_game(std::string_view owner, bool on);
|
||||
void bone_cam_release(std::string_view owner);
|
||||
// Whether the skater's body stopped dead (lost 7 m/s within a third of a second) since the last
|
||||
// call: the ground, a pole, a wall. Only followed while the Bone Cam would show or a game asks.
|
||||
bool bone_cam_slam() noexcept;
|
||||
// Whether the skater's head, chest, shoulders or hips struck an object at speed (a pole, a tree, a
|
||||
// rail: nearly stopped while the body was still flying) since the last call. Followed as
|
||||
// bone_cam_slam is.
|
||||
bool bone_cam_struck() noexcept;
|
||||
// How many times the skater's body has stopped dead (lost 7 m/s within a third of a second): the
|
||||
// ground, a pole, a wall. A count, so each reader keeps its own place (a reader that cleared a flag
|
||||
// hid the slam from every other). Only followed while the Bone Cam would show or a game asks.
|
||||
std::uint64_t bone_cam_slams() noexcept;
|
||||
// How many times the skater's head, chest, shoulders or hips struck an object at speed (a pole, a
|
||||
// tree, a rail: nearly stopped while the body was still flying). Followed as bone_cam_slams is.
|
||||
std::uint64_t bone_cam_strikes() noexcept;
|
||||
// The X-ray's snapshot, with the latest camera (any thread).
|
||||
BoneCam bone_cam();
|
||||
// Fresh native hips position while the normal on-board telemetry is absent.
|
||||
|
||||
@@ -516,6 +516,12 @@ Match match() noexcept {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
bool allows_teleports(std::string_view plugin, bool flag_mine) noexcept {
|
||||
auto& s = store();
|
||||
std::lock_guard lock(s.mutex);
|
||||
const auto entry = s.plugins.find(plugin);
|
||||
return entry != s.plugins.end() && entry->second.documents.match.teleports && (entry->second.documents.hud.active || flag_mine);
|
||||
}
|
||||
bool hidden(std::uint64_t player) noexcept {
|
||||
if (!player) return false;
|
||||
auto& s = store();
|
||||
|
||||
@@ -222,6 +222,10 @@ std::optional<Documents> documents_for(std::string_view mode);
|
||||
std::optional<Documents> active();
|
||||
// What every running match asks, merged (any plugin's `true` holds).
|
||||
Match match() noexcept;
|
||||
// Whether `plugin`'s own match lets its teleports through where the session turned noclip off: its
|
||||
// `match.teleports`, while that match runs (its HUD is up, or `flag_mine`: the native flag is one of
|
||||
// its modes, which the caller knows from flag::state()).
|
||||
bool allows_teleports(std::string_view plugin, bool flag_mine) noexcept;
|
||||
bool hidden(std::uint64_t player) noexcept;
|
||||
|
||||
// ---- events -----------------------------------------------------------------------------------
|
||||
|
||||
@@ -1138,8 +1138,12 @@ void kit_hud(const Context& c) {
|
||||
auto& s=state();auto& h=s.kit_hud;const auto now=GetTickCount64();
|
||||
const auto docs=kit::active();
|
||||
const auto generation=docs && docs->hud.active?std::hash<std::string>{}(docs->plugin)|1:0;
|
||||
// A match that ended or another plugin's: everything goes, including a pending GO.
|
||||
if(h.generation!=generation) {kit_hud_publish(c,{});h.generation=generation;h.go_until=0;h.was_counting=false;}
|
||||
// A match that ended or another plugin's: everything goes, including a pending GO, and a banner
|
||||
// or callout of the new one shows whatever serial it starts from.
|
||||
if(h.generation!=generation) {
|
||||
kit_hud_publish(c,{});h.generation=generation;h.go_until=0;h.was_counting=false;
|
||||
h.callout_serial=h.banner_serial=~0ULL;h.callout_until=h.banner_until=0;
|
||||
}
|
||||
if(!generation) {s.hud_doc.reset();kit_hud_publish(c,{});s.hud_ready.store(false);return;}
|
||||
const auto& hud=docs->hud;
|
||||
s.hud_doc=hud;
|
||||
@@ -1199,15 +1203,16 @@ void kit_hud(const Context& c) {
|
||||
if(hud.list.present && !hud.list.items.empty())piece("list",[&]{kit_list(c,hud,styled);append(h.list_item);});
|
||||
}
|
||||
if(!hud.warning.text.empty())piece("warning",[&]{kit_warning(c,hud);append(h.warning_item);});
|
||||
// A callout and a banner each show for their time after a new serial.
|
||||
if(hud.callout.serial!=h.callout_serial) {
|
||||
// A callout and a banner each show for their time after a new serial. A document without the
|
||||
// slot leaves the serial as it was: the same one coming back is not news.
|
||||
if(!hud.callout.text.empty() && hud.callout.serial!=h.callout_serial) {
|
||||
h.callout_serial=hud.callout.serial;
|
||||
if(!hud.callout.text.empty())h.callout_until=now+(hud.callout.ms?hud.callout.ms:2500),h.callout_text=hud.callout.text;
|
||||
h.callout_until=now+(hud.callout.ms?hud.callout.ms:2500);h.callout_text=hud.callout.text;
|
||||
}
|
||||
if(now<h.callout_until)piece("callout",[&]{kit_callout(c,hud);append(h.callout_item);});
|
||||
if(hud.banner.serial!=h.banner_serial) {
|
||||
if(!hud.banner.text.empty() && hud.banner.serial!=h.banner_serial) {
|
||||
h.banner_serial=hud.banner.serial;
|
||||
if(!hud.banner.text.empty())h.banner_until=now+(hud.banner.ms?hud.banner.ms:3000);
|
||||
h.banner_until=now+(hud.banner.ms?hud.banner.ms:3000);
|
||||
}
|
||||
if(now<h.banner_until && !hud.banner.text.empty())piece("banner",[&]{kit_banner(c,hud);append(h.banner_item);});
|
||||
if(editor)piece("editor",[&] {
|
||||
|
||||
@@ -252,11 +252,14 @@ void mode_open(const std::string& id) {
|
||||
options.players=static_cast<unsigned>(std::max(1,d->players));
|
||||
options.setup_page=true;
|
||||
options.cursor_height=d->cursor_height; // the registration's, e.g. a flag that may stand on anything
|
||||
// The native registration's limit is 1 s to an hour. A mode's timer of 0 (none) or past an hour
|
||||
// keeps its card working: the plugin runs its own clock.
|
||||
if(const auto setup=current_setup(d->id);setup && setup->timer.present)
|
||||
options.seconds=static_cast<unsigned>(std::max(0.f,setup->timer.value));
|
||||
options.seconds=static_cast<unsigned>(std::clamp(std::round(setup->timer.value),1.f,3600.f));
|
||||
kit::set_current(d->id);
|
||||
if(!kit::flag::begin(d->id,game,options)) {
|
||||
logging::log(logging::Level::info,logging::Channel::ui,"Throwdowns: {} not opened, another flag is busy.",d->id);
|
||||
logging::log(logging::Level::info,logging::Channel::ui,
|
||||
"Throwdowns: {} not opened: there is no world yet, or another flag is being placed or waits.",d->id);
|
||||
return;
|
||||
}
|
||||
kit::post(d->id,"open",{std::to_string(game)});
|
||||
|
||||
@@ -9,12 +9,11 @@ local RA = require("td/h/race_arrow_placement")
|
||||
-- game_modes.cpp:1445.
|
||||
local pad_up, pad_down, pad_left, pad_right = 0x1, 0x2, 0x4, 0x8
|
||||
|
||||
-- Windows virtual keys (WinUser.h) and WHEEL_DELTA.
|
||||
-- Windows virtual keys (WinUser.h).
|
||||
local VK_LBUTTON, VK_BACK, VK_RETURN, VK_ESCAPE = 0x01, 0x08, 0x0D, 0x1B
|
||||
local VK_PRIOR, VK_NEXT, VK_END = 0x21, 0x22, 0x23
|
||||
local VK_OEM_4, VK_OEM_6 = 0xDB, 0xDD
|
||||
local KEY_F, KEY_G, KEY_R, KEY_X, KEY_Z = 0x46, 0x47, 0x52, 0x58, 0x5A
|
||||
local WHEEL_DELTA = 120
|
||||
|
||||
local band, bor, bnot = bit32.band, bit32.bor, bit32.bnot
|
||||
|
||||
@@ -200,7 +199,7 @@ function M.run_placing(s, now)
|
||||
local mouse = s.free_place
|
||||
local keys = {held(VK_RETURN) or (mouse and held(VK_LBUTTON)), held(VK_BACK), held(VK_END) or (mouse and held(KEY_F)), held(VK_ESCAPE)}
|
||||
local wheel = reskate.input.wheel() -- only gathered while the free camera flies
|
||||
local notches = wheel / WHEEL_DELTA
|
||||
local notches = wheel -- reskate.input.wheel counts whole notches already
|
||||
local struck = {false, false, false, false}
|
||||
for i = 1, #keys do struck[i] = keys[i] and not s.keys_previous[i] end
|
||||
s.keys_previous = keys
|
||||
|
||||
Reference in new issue
Block a user