From 531d733f16e29beade9b60ed5c9cfbfc386e22fc Mon Sep 17 00:00:00 2001 From: Felix Date: Sun, 7 Mar 2021 23:31:07 +0100 Subject: [PATCH] Update g-force calculation --- ride-length.js | 211 +++++++++++++++++++++++++------- src/native-calculations.ts | 4 +- src/openrct2.d.ts | 121 ++++++++++-------- src/ride-length.ts | 49 ++++++-- src/ride-measurements-window.ts | 122 +++++++++++++++--- src/ride-measurements.ts | 42 ++++--- 6 files changed, 399 insertions(+), 150 deletions(-) diff --git a/ride-length.js b/ride-length.js index e03a9ec..8ec3f55 100644 --- a/ride-length.js +++ b/ride-length.js @@ -1686,7 +1686,7 @@ break; case TrackElemType.HalfLoopUp: case TrackElemType.FlyerHalfLoopUp: - vertFactor = (((-(trackProgress - 155))) / 2) + 28; + vertFactor = (((-(trackProgress - 155)) & 0xFFFF) / 2) + 28; // 6d763E break; case TrackElemType.HalfLoopDown: @@ -1866,7 +1866,7 @@ break; case TrackElemType.LeftLargeHalfLoopUp: case TrackElemType.RightLargeHalfLoopUp: - vertFactor = (((-(trackProgress - 311))) / 4) + 46; + vertFactor = (((-(trackProgress - 311)) & 0xFFFF) / 4) + 46; // 6d7666 break; case TrackElemType.RightLargeHalfLoopDown: @@ -2353,15 +2353,18 @@ this.time = new MaxValue(); } RideMeasurements.prototype.update = function () { - var car = this.currentFrontCar; - if (car == null) + var cars = this.getRideCars; + if (cars == null || cars.length == 0) return; - if (car.status == "waiting_to_depart" && car.status != this.lastCarStatus) { - this.newRound(); + for (var _i = 0, cars_1 = cars; _i < cars_1.length; _i++) { + var car = cars_1[_i]; + if (car.status == "waiting_to_depart" && car.status != this.lastCarStatus) { + this.newRound(); + } + this.lastCarStatus = car.status; + this.updateMeasurementsLength(car); + this.updateMeasurementsGForce(car); } - this.lastCarStatus = car.status; - this.updateMeasurementsLength(car); - this.updateMeasurementsGForce(car); }; RideMeasurements.prototype.updateMeasurementsLength = function (car) { var acceleration = car.acceleration; @@ -2386,14 +2389,18 @@ this.averageSpeed.current = this.averageSpeed.current + Math.abs(car.velocity); this.time.current++; } - var gForces = getGForces(trackElement.trackType, car.spriteType, car.bankRotation, car.trackProgress, car.velocity); + var gForces = car.gForces ? { + gForceVert: car.gForces.verticalG, + gForceLateral: car.gForces.lateralG, + } : getGForces(trackElement.trackType, // incorrect value + car.spriteType, car.bankRotation, car.trackProgress, car.velocity); var verticalG = gForces.gForceVert + this.previousVerticalG; var lateralG = gForces.gForceLateral + this.previousLateralG; verticalG /= 2; lateralG /= 2; this.previousVerticalG = verticalG; this.previousLateralG = lateralG; - if (verticalG <= 0) { + if ((verticalG & 0xFFFFFFFF) <= 0) { this.totalAirTime.current++; } if (verticalG > this.maxVerticalPosG.current) { @@ -2407,6 +2414,10 @@ } }; RideMeasurements.prototype.selectRide = function (index) { + if (index == null) { + this.selectedRide = null; + return; + } this.selectedRide = this.rides[index]; this.reset(); }; @@ -2450,17 +2461,19 @@ enumerable: false, configurable: true }); - Object.defineProperty(RideMeasurements.prototype, "currentFrontCar", { + Object.defineProperty(RideMeasurements.prototype, "getRideCars", { get: function () { if (this.selectedRide == null) return null; var vehicleId = this.selectedRide.vehicles[0]; if (vehicleId != 0 && !vehicleId) return null; - var cars = map.getAllEntities("car"); + var cars = map.getAllEntities("car") + .filter(function (entity) { return entity.id == vehicleId; }) + .map(function (entity) { return entity; }); if (cars.length == 0) return null; - return cars.filter(function (car) { return car.id == vehicleId; })[0]; + return cars; }, enumerable: false, configurable: true @@ -2603,13 +2616,29 @@ configurable: true }); RideMeasurementsWindow.prototype.getLabelWidget = function (type) { - var _a; - return (_a = this.uiWindow) === null || _a === void 0 ? void 0 : _a.findWidget(type.toString()); + var _a, _b; + for (var _i = 0, _c = (_b = (_a = this.uiWindow) === null || _a === void 0 ? void 0 : _a.widgets) !== null && _b !== void 0 ? _b : []; _i < _c.length; _i++) { + var widget = _c[_i]; + if (widget.name == type.toString()) { + return widget; + } + } + }; + RideMeasurementsWindow.prototype.getValueLabelWidget = function (type) { + var _a, _b; + for (var _i = 0, _c = (_b = (_a = this.uiWindow) === null || _a === void 0 ? void 0 : _a.widgets) !== null && _b !== void 0 ? _b : []; _i < _c.length; _i++) { + var widget = _c[_i]; + if (widget.name == type.toString() + "-value") { + return widget; + } + } }; RideMeasurementsWindow.prototype.setValue = function (type, text) { var _a; var label = (_a = this.uiWindow) === null || _a === void 0 ? void 0 : _a.findWidget(type.toString() + "-value"); - label.text = text; + if (label) { + label.text = text; + } }; Object.defineProperty(RideMeasurementsWindow.prototype, "dropdownContent", { set: function (content) { @@ -2623,7 +2652,7 @@ classification: "my.window", width: windowWidth, height: windowHeight, - title: "Ride length preview", + title: "Ride Measurements Preview", onClose: onClose, widgets: [ { @@ -2635,14 +2664,24 @@ type: "dropdown", items: this.dropdownHeadline, selectedIndex: 0, - onChange: onSelectRide + onChange: onSelectRide, + }, + { + name: "hint_label", + type: "label", + width: 145, + height: 20, + x: 25, + y: 60, + text: "", + isVisible: false }, - this.label(Measurements.excitment), - this.value(Measurements.excitment), - this.label(Measurements.intensity), - this.value(Measurements.intensity), - this.label(Measurements.nausea), - this.value(Measurements.nausea), + this.label(Measurements.excitment, true), + this.value(Measurements.excitment, true), + this.label(Measurements.intensity, true), + this.value(Measurements.intensity, true), + this.label(Measurements.nausea, true), + this.value(Measurements.nausea, true), this.label(Measurements.maxSpeed), this.value(Measurements.maxSpeed), this.label(Measurements.averageSpeed), @@ -2659,14 +2698,62 @@ this.value(Measurements.lateralGs), this.label(Measurements.airTime), this.value(Measurements.airTime), - this.label(Measurements.drops), - this.value(Measurements.drops), - this.label(Measurements.highestDrop), - this.value(Measurements.highestDrop), + this.label(Measurements.drops, true), + this.value(Measurements.drops, true), + this.label(Measurements.highestDrop, true), + this.value(Measurements.highestDrop, true), ] }); }; - RideMeasurementsWindow.prototype.label = function (type) { + RideMeasurementsWindow.prototype.hideValues = function () { + for (var measurement in Measurements) { + var label = this.getLabelWidget(Number(measurement)); + var value = this.getValueLabelWidget(Number(measurement)); + if (label) { + label.isVisible = false; + } + if (value) { + value.isVisible = false; + } + } + }; + RideMeasurementsWindow.prototype.showValues = function () { + for (var measurement in Measurements) { + var label = this.getLabelWidget(Number(measurement)); + var value = this.getValueLabelWidget(Number(measurement)); + if (label) { + label.isVisible = true; + } + if (value) { + value.isVisible = true; + } + } + this.updateWindow(); + }; + RideMeasurementsWindow.prototype.showHint = function (text) { + var _a, _b; + for (var _i = 0, _c = (_b = (_a = this.uiWindow) === null || _a === void 0 ? void 0 : _a.widgets) !== null && _b !== void 0 ? _b : []; _i < _c.length; _i++) { + var widget = _c[_i]; + if (widget.name == "hint_label") { + widget.text = text; + widget.isVisible = true; + return; + } + } + this.updateWindow(); + }; + RideMeasurementsWindow.prototype.hideHint = function () { + var _a, _b; + for (var _i = 0, _c = (_b = (_a = this.uiWindow) === null || _a === void 0 ? void 0 : _a.widgets) !== null && _b !== void 0 ? _b : []; _i < _c.length; _i++) { + var widget = _c[_i]; + if (widget.name == "hint_label") { + widget.isVisible = false; + return; + } + } + }; + RideMeasurementsWindow.prototype.label = function (type, isDisabled) { + if (isDisabled === void 0) { isDisabled = false; } return { name: type.toString(), type: "label", @@ -2674,10 +2761,12 @@ height: 20, x: 5, y: 50 + 10 * getIndex(type), - text: getName(type) + ":" + text: getName(type) + ":", + isDisabled: isDisabled }; }; - RideMeasurementsWindow.prototype.value = function (type) { + RideMeasurementsWindow.prototype.value = function (type, isDisabled) { + if (isDisabled === void 0) { isDisabled = false; } return { name: type.toString() + "-value", type: "label", @@ -2685,28 +2774,42 @@ height: 20, x: 150, y: 50 + 10 * getIndex(type), - text: "-" + text: "-", + isDisabled: isDisabled }; }; + RideMeasurementsWindow.prototype.updateWindow = function () { + // This is a hack because the window doesn't get + // updated when labels are made visible/invisible + if (this.uiWindow) { + this.uiWindow.x += 1; + this.uiWindow.x -= 1; + } + }; return RideMeasurementsWindow; }()); + // TODO: // Doesn't support rides with multiple stations - // G calculation is off - // Ghost trains need to be enabled + // Show imperial measurements + // Add Reset Button registerPlugin({ - name: "Ride Info", - version: "1.0", + name: "Live Ride Measurements", + version: "1.0.0", authors: ["Felix Janus"], licence: "MIT", - type: "local", + type: "remote", + minApiVersion: 1, main: function () { - ui.registerMenuItem("Ride length", function () { + if (!ui) { + return; + } + ui.registerMenuItem("Live Ride Measurements", function () { openRideMeasurementsWindow(); }); - console.clear(); - ui.closeAllWindows(); - openRideMeasurementsWindow(); + // console.clear() + // ui.closeAllWindows() + // openRideMeasurementsWindow() } }); function openRideMeasurementsWindow() { @@ -2714,12 +2817,25 @@ var rideMeasurements = new RideMeasurements(); var rideNames = rideMeasurements.rideNames; var tickHook = context.subscribe("interval.tick", function () { - if (rideMeasurements.selectedRide == null) + if (rideMeasurements.selectedRide == null) { + rideMeasurementsWindow.hideValues(); + rideMeasurementsWindow.hideHint(); return; - var car = rideMeasurements.currentFrontCar; - if (car == null) + } + var cars = rideMeasurements.getRideCars; + if (cars == null || cars.length == 0) { + rideMeasurementsWindow.hideValues(); + rideMeasurementsWindow.showHint("Please enable ghost trains."); return; + } + rideMeasurementsWindow.showValues(); + rideMeasurementsWindow.hideHint(); rideMeasurements.update(); + if (rideMeasurements.selectedRide != null) { + rideMeasurementsWindow.setValue(Measurements.excitment, (rideMeasurements.selectedRide.excitement / 100).toFixed(2).toString()); + rideMeasurementsWindow.setValue(Measurements.intensity, (rideMeasurements.selectedRide.intensity / 100).toFixed(2).toString()); + rideMeasurementsWindow.setValue(Measurements.nausea, (rideMeasurements.selectedRide.nausea / 100).toFixed(2).toString()); + } rideMeasurementsWindow.setValue(Measurements.maxSpeed, mphToKmph((rideMeasurements.maxSpeed.value * 9) >> 18) + " km/h"); rideMeasurementsWindow.setValue(Measurements.rideLength, (rideMeasurements.maxLength.value >> 16) + " m"); rideMeasurementsWindow.setValue(Measurements.positiveGs, (rideMeasurements.maxVerticalPosG.value / 100).toFixed(2) + " g"); @@ -2729,7 +2845,10 @@ rideMeasurementsWindow.setValue(Measurements.averageSpeed, mphToKmph(((rideMeasurements.averageSpeed.value / rideMeasurements.time.value) * 9) >> 18) + " km/h"); rideMeasurementsWindow.setValue(Measurements.rideTime, (rideMeasurements.time.value) + " secs"); }); - rideMeasurementsWindow.open(tickHook.dispose, function (index) { + rideMeasurementsWindow.open(function () { + rideMeasurements.selectRide(null); + tickHook.dispose(); + }, function (index) { rideMeasurements.selectRide(index - 1); }); rideMeasurementsWindow.dropdownContent = rideNames; diff --git a/src/native-calculations.ts b/src/native-calculations.ts index 0e8cc0b..f1cda0f 100644 --- a/src/native-calculations.ts +++ b/src/native-calculations.ts @@ -247,7 +247,7 @@ function getGForces( break case TrackElemType.HalfLoopUp: case TrackElemType.FlyerHalfLoopUp: - vertFactor = (((-(trackProgress - 155))) / 2) + 28 + vertFactor = (((-(trackProgress - 155)) & 0xFFFF) / 2) + 28 // 6d763E break case TrackElemType.HalfLoopDown: @@ -427,7 +427,7 @@ function getGForces( break case TrackElemType.LeftLargeHalfLoopUp: case TrackElemType.RightLargeHalfLoopUp: - vertFactor = (((-(trackProgress - 311))) / 4) + 46 + vertFactor = (((-(trackProgress - 311)) & 0xFFFF) / 4) + 46 // 6d7666 break case TrackElemType.RightLargeHalfLoopDown: diff --git a/src/openrct2.d.ts b/src/openrct2.d.ts index 0919402..c760aa1 100644 --- a/src/openrct2.d.ts +++ b/src/openrct2.d.ts @@ -22,32 +22,32 @@ declare global { * Global context for accessing all other APIs. */ /** APIs for cheats. */ - var cheats: Cheats + var cheats: Cheats; /** APIs for interacting with the stdout console. */ - var console: Console + var console: Console; /** Core APIs for plugins. */ - var context: Context + var context: Context; /** APIs for getting or setting the in-game date. */ - var date: GameDate + var date: GameDate; /** APIs for manipulating the map. */ - var map: GameMap + var map: GameMap; /** APIs for managing the server or interacting with the server or clients. */ - var network: Network + var network: Network; /** APIs for the park and management of it. */ - var park: Park + var park: Park; /** APIs for the current scenario. */ - var scenario: Scenario + var scenario: Scenario; /** * APIs for creating and editing title sequences. * These will only be available to clients that are not running headless mode. */ - var titleSequenceManager: TitleSequenceManager + var titleSequenceManager: TitleSequenceManager; /** * APIs for controlling the user interface. * These will only be available to servers and clients that are not running headless mode. * Plugin writers should check if ui is available using `typeof ui !== 'undefined'`. */ - var ui: Ui + var ui: Ui; /** * Registers the plugin. This may only be called once. @@ -106,6 +106,14 @@ declare global { direction: number; } + /** + * + */ + interface GForces { + lateralG: number; + verticalG: number; + } + /** * A rectangular area specified using two coordinates. */ @@ -1153,6 +1161,11 @@ declare global { */ trackLocation: CoordsXYZD; + /** + * + */ + gForces: GForces; + /** * The progress on the current track piece, in steps. */ @@ -1858,12 +1871,12 @@ declare global { * Whether to browse a file for loading or saving. Saving will prompt the user * before overwriting a file. */ - type: "load"; + type: 'load'; /** * The type of file to browse for. */ - fileType: "game" | "heightmap"; + fileType: 'game' | 'heightmap'; /** * The pre-selected file to load by default if the user clicks OK. @@ -1891,8 +1904,8 @@ declare global { */ interface ScenarioFile { id: number; - category: "beginner" | "challenging" | "expert" | "real" | "other" | "dlc" | "build_your_own"; - sourceGame: "rct1" | "rct1_aa" | "rct1_ll" | "rct2" | "rct2_ww" | "rct2_tt" | "real" | "other"; + category: 'beginner' | 'challenging' | 'expert' | 'real' | 'other' | 'dlc' | 'build_your_own'; + sourceGame: 'rct1' | 'rct1_aa' | 'rct1_ll' | 'rct2' | 'rct2_ww' | 'rct2_tt' | 'real' | 'other'; path: string; internalName: string; name: string; @@ -2035,7 +2048,7 @@ declare global { } interface ButtonWidget extends WidgetBase { - type: "button"; + type: 'button'; /** * Whether the button has a 3D border. * By default, text buttons have borders and image buttons do not but it can be overridden. @@ -2048,36 +2061,36 @@ declare global { } interface CheckboxWidget extends WidgetBase { - type: "checkbox"; + type: 'checkbox'; text?: string; isChecked?: boolean; onChange?: (isChecked: boolean) => void; } interface ColourPickerWidget extends WidgetBase { - type: "colourpicker"; + type: 'colourpicker'; colour?: number; onChange?: (colour: number) => void; } interface CustomWidget extends WidgetBase { - type: "custom"; + type: 'custom'; onDraw?: (this: CustomWidget, g: GraphicsContext) => void; } interface DropdownWidget extends WidgetBase { - type: "dropdown"; + type: 'dropdown'; items?: string[]; selectedIndex?: number; onChange?: (index: number) => void; } interface GroupBoxWidget extends WidgetBase { - type: "groupbox"; + type: 'groupbox'; } interface LabelWidget extends WidgetBase { - type: "label"; + type: 'label'; text?: string; textAlign?: TextAlignment; onChange?: (index: number) => void; @@ -2101,7 +2114,7 @@ declare global { } interface ListViewItemSeperator { - type: "seperator"; + type: 'seperator'; text?: string; } @@ -2113,7 +2126,7 @@ declare global { } interface ListView extends WidgetBase { - type: "listview"; + type: 'listview'; scrollbars?: ScrollbarType; isStriped?: boolean; showColumnHeaders?: boolean; @@ -2128,7 +2141,7 @@ declare global { } interface SpinnerWidget extends WidgetBase { - type: "spinner"; + type: 'spinner'; text?: string; onDecrement?: () => void; @@ -2137,14 +2150,14 @@ declare global { } interface TextBoxWidget extends WidgetBase { - type: "textbox"; + type: 'textbox'; text?: string; maxLength?: number; onChange?: (text: string) => void; } interface ViewportWidget extends WidgetBase { - type: "viewport"; + type: 'viewport'; viewport?: Viewport; } @@ -2266,9 +2279,9 @@ declare global { listen(port: number, host?: string): Listener; close(): Listener; - on(event: "connection", callback: (socket: Socket) => void): Listener; + on(event: 'connection', callback: (socket: Socket) => void): Listener; - off(event: "connection", callback: (socket: Socket) => void): Listener; + off(event: 'connection', callback: (socket: Socket) => void): Listener; } /** @@ -2282,13 +2295,13 @@ declare global { end(data?: string): Socket; write(data: string): boolean; - on(event: "close", callback: (hadError: boolean) => void): Socket; - on(event: "error", callback: (hadError: boolean) => void): Socket; - on(event: "data", callback: (data: string) => void): Socket; + on(event: 'close', callback: (hadError: boolean) => void): Socket; + on(event: 'error', callback: (hadError: boolean) => void): Socket; + on(event: 'data', callback: (data: string) => void): Socket; - off(event: "close", callback: (hadError: boolean) => void): Socket; - off(event: "error", callback: (hadError: boolean) => void): Socket; - off(event: "data", callback: (data: string) => void): Socket; + off(event: 'close', callback: (hadError: boolean) => void): Socket; + off(event: 'error', callback: (hadError: boolean) => void): Socket; + off(event: 'data', callback: (data: string) => void): Socket; } interface TitleSequence { @@ -2380,64 +2393,64 @@ declare global { } type TitleSequenceCommandType = - "load" | - "loadsc" | - "location" | - "rotate" | - "zoom" | - "speed" | - "follow" | - "wait" | - "restart" | - "end"; + 'load' | + 'loadsc' | + 'location' | + 'rotate' | + 'zoom' | + 'speed' | + 'follow' | + 'wait' | + 'restart' | + 'end'; interface LoadTitleSequenceCommand { - type: "load"; + type: 'load'; index: number; } interface LocationTitleSequenceCommand { - type: "location"; + type: 'location'; x: number; y: number; } interface RotateTitleSequenceCommand { - type: "rotate"; + type: 'rotate'; rotations: number; } interface ZoomTitleSequenceCommand { - type: "zoom"; + type: 'zoom'; zoom: number; } interface FollowTitleSequenceCommand { - type: "follow"; + type: 'follow'; id: number | null; } interface SpeedTitleSequenceCommand { - type: "speed"; + type: 'speed'; speed: number; } interface WaitTitleSequenceCommand { - type: "wait"; + type: 'wait'; duration: number; } interface LoadScenarioTitleSequenceCommand { - type: "loadsc"; + type: 'loadsc'; scenario: string; } interface RestartTitleSequenceCommand { - type: "restart"; + type: 'restart'; } interface EndTitleSequenceCommand { - type: "end"; + type: 'end'; } type TitleSequenceCommand = diff --git a/src/ride-length.ts b/src/ride-length.ts index 55b9c23..5e49927 100644 --- a/src/ride-length.ts +++ b/src/ride-length.ts @@ -1,25 +1,31 @@ import { RideMeasurements } from "./ride-measurements" import { Measurements, RideMeasurementsWindow } from "./ride-measurements-window" +// TODO: // Doesn't support rides with multiple stations -// G calculation is off -// Ghost trains need to be enabled +// Show imperial measurements +// Add Reset Button registerPlugin({ - name: "Ride Info", - version: "1.0", + name: "Live Ride Measurements", + version: "1.0.0", authors: ["Felix Janus"], licence: "MIT", - type: "local", + type: "remote", + minApiVersion: 1, main: () => { - ui.registerMenuItem("Ride length", () => { + if (!ui) { + return + } + + ui.registerMenuItem("Live Ride Measurements", () => { openRideMeasurementsWindow() }) - console.clear() - ui.closeAllWindows() - openRideMeasurementsWindow() + // console.clear() + // ui.closeAllWindows() + // openRideMeasurementsWindow() } }) @@ -30,15 +36,29 @@ function openRideMeasurementsWindow() { const tickHook = context.subscribe("interval.tick", () => { - if (rideMeasurements.selectedRide == null) + if (rideMeasurements.selectedRide == null) { + rideMeasurementsWindow.hideValues() + rideMeasurementsWindow.hideHint() return + } - const car = rideMeasurements.currentFrontCar - if (car == null) + const cars = rideMeasurements.getRideCars + if (cars == null || cars.length == 0) { + rideMeasurementsWindow.hideValues() + rideMeasurementsWindow.showHint("Please enable ghost trains.") return + } + rideMeasurementsWindow.showValues() + rideMeasurementsWindow.hideHint() rideMeasurements.update() + if (rideMeasurements.selectedRide != null) { + rideMeasurementsWindow.setValue(Measurements.excitment, (rideMeasurements.selectedRide.excitement / 100).toFixed(2).toString()) + rideMeasurementsWindow.setValue(Measurements.intensity, (rideMeasurements.selectedRide.intensity / 100).toFixed(2).toString()) + rideMeasurementsWindow.setValue(Measurements.nausea, (rideMeasurements.selectedRide.nausea / 100).toFixed(2).toString()) + } + rideMeasurementsWindow.setValue(Measurements.maxSpeed, mphToKmph((rideMeasurements.maxSpeed.value * 9) >> 18) + " km/h") rideMeasurementsWindow.setValue(Measurements.rideLength, (rideMeasurements.maxLength.value >> 16) + " m") rideMeasurementsWindow.setValue(Measurements.positiveGs, (rideMeasurements.maxVerticalPosG.value / 100).toFixed(2) + " g") @@ -49,7 +69,10 @@ function openRideMeasurementsWindow() { rideMeasurementsWindow.setValue(Measurements.rideTime, (rideMeasurements.time.value) + " secs") }) - rideMeasurementsWindow.open(tickHook.dispose, (index) => { + rideMeasurementsWindow.open(() => { + rideMeasurements.selectRide(null); + tickHook.dispose(); + }, (index) => { rideMeasurements.selectRide(index - 1) }); rideMeasurementsWindow.dropdownContent = rideNames diff --git a/src/ride-measurements-window.ts b/src/ride-measurements-window.ts index a84d537..73b7e10 100644 --- a/src/ride-measurements-window.ts +++ b/src/ride-measurements-window.ts @@ -88,13 +88,27 @@ export class RideMeasurementsWindow { return this.uiWindow?.findWidget("ride_selection") as DropdownWidget; } - getLabelWidget(type: Measurements): LabelWidget { - return this.uiWindow?.findWidget(type.toString()) as LabelWidget; + getLabelWidget(type: Measurements): LabelWidget | undefined { + for (const widget of this.uiWindow?.widgets ?? []) { + if (widget.name == type.toString()) { + return widget as LabelWidget + } + } + } + + getValueLabelWidget(type: Measurements): LabelWidget | undefined { + for (const widget of this.uiWindow?.widgets ?? []) { + if (widget.name == type.toString() + "-value") { + return widget as LabelWidget + } + } } setValue(type: Measurements, text: string): void { - const label = this.uiWindow?.findWidget(type.toString() + "-value") as LabelWidget; - label.text = text + const label = this.uiWindow?.findWidget(type.toString() + "-value"); + if (label) { + label.text = text + } } set dropdownContent(content: string[]) { @@ -107,7 +121,7 @@ export class RideMeasurementsWindow { classification: "my.window", width: windowWidth, height: windowHeight, - title: "Ride length preview", + title: "Ride Measurements Preview", onClose: onClose, widgets: [ { @@ -119,14 +133,24 @@ export class RideMeasurementsWindow { type: "dropdown", items: this.dropdownHeadline, selectedIndex: 0, - onChange: onSelectRide + onChange: onSelectRide, + }, + { + name: "hint_label", + type: "label", + width: 145, + height: 20, + x: 25, + y: 60, + text: "", + isVisible: false }, - this.label(Measurements.excitment), - this.value(Measurements.excitment), - this.label(Measurements.intensity), - this.value(Measurements.intensity), - this.label(Measurements.nausea), - this.value(Measurements.nausea), + this.label(Measurements.excitment, true), + this.value(Measurements.excitment, true), + this.label(Measurements.intensity, true), + this.value(Measurements.intensity, true), + this.label(Measurements.nausea, true), + this.value(Measurements.nausea, true), this.label(Measurements.maxSpeed), this.value(Measurements.maxSpeed), this.label(Measurements.averageSpeed), @@ -143,15 +167,62 @@ export class RideMeasurementsWindow { this.value(Measurements.lateralGs), this.label(Measurements.airTime), this.value(Measurements.airTime), - this.label(Measurements.drops), - this.value(Measurements.drops), - this.label(Measurements.highestDrop), - this.value(Measurements.highestDrop), + this.label(Measurements.drops, true), + this.value(Measurements.drops, true), + this.label(Measurements.highestDrop, true), + this.value(Measurements.highestDrop, true), ] }); } - label(type: Measurements): Widget { + hideValues(): void { + for (const measurement in Measurements) { + const label = this.getLabelWidget(Number(measurement)) + const value = this.getValueLabelWidget(Number(measurement)) + if (label) { + label.isVisible = false + } + if (value) { + value.isVisible = false + } + } + } + + showValues(): void { + for (const measurement in Measurements) { + const label = this.getLabelWidget(Number(measurement)) + const value = this.getValueLabelWidget(Number(measurement)) + if (label) { + label.isVisible = true + } + if (value) { + value.isVisible = true + } + } + this.updateWindow() + } + + showHint(text: string): void { + for (const widget of this.uiWindow?.widgets ?? []) { + if (widget.name == "hint_label") { + (widget as LabelWidget).text = text + widget.isVisible = true + return + } + } + this.updateWindow() + } + + hideHint(): void { + for (const widget of this.uiWindow?.widgets ?? []) { + if (widget.name == "hint_label") { + widget.isVisible = false + return + } + } + } + + label(type: Measurements, isDisabled = false): LabelWidget { return { name: type.toString(), type: "label", @@ -159,11 +230,12 @@ export class RideMeasurementsWindow { height: 20, x: 5, y: 50 + 10 * getIndex(type), - text: getName(type) + ":" + text: getName(type) + ":", + isDisabled } } - value(type: Measurements): Widget { + value(type: Measurements, isDisabled = false): LabelWidget { return { name: type.toString() + "-value", type: "label", @@ -171,7 +243,17 @@ export class RideMeasurementsWindow { height: 20, x: 150, y: 50 + 10 * getIndex(type), - text: "-" + text: "-", + isDisabled + } + } + + updateWindow(): void { + // This is a hack because the window doesn't get + // updated when labels are made visible/invisible + if (this.uiWindow) { + this.uiWindow.x += 1 + this.uiWindow.x -= 1 } } } diff --git a/src/ride-measurements.ts b/src/ride-measurements.ts index 597dd13..a3ed0cb 100644 --- a/src/ride-measurements.ts +++ b/src/ride-measurements.ts @@ -19,17 +19,20 @@ export class RideMeasurements { time = new MaxValue(); update(): void { - const car = this.currentFrontCar - if (car == null) + const cars = this.getRideCars + if (cars == null || cars.length == 0) return; - if (car.status == "waiting_to_depart" && car.status != this.lastCarStatus) { - this.newRound() + for (const car of cars) { + if (car.status == "waiting_to_depart" && car.status != this.lastCarStatus) { + this.newRound() + } + this.lastCarStatus = car.status + + this.updateMeasurementsLength(car); + this.updateMeasurementsGForce(car); } - this.lastCarStatus = car.status - this.updateMeasurementsLength(car); - this.updateMeasurementsGForce(car); } updateMeasurementsLength(car: Car): void { @@ -59,14 +62,18 @@ export class RideMeasurements { this.time.current++ } - const gForces = getGForces( - trackElement.trackType, + const gForces = car.gForces ? { + gForceVert: car.gForces.verticalG, + gForceLateral: car.gForces.lateralG, + } : getGForces( + trackElement.trackType, // incorrect value car.spriteType, car.bankRotation, car.trackProgress, car.velocity ); + let verticalG = gForces.gForceVert + this.previousVerticalG; let lateralG = gForces.gForceLateral + this.previousLateralG; verticalG /= 2; @@ -75,8 +82,7 @@ export class RideMeasurements { this.previousVerticalG = verticalG; this.previousLateralG = lateralG; - - if (verticalG <= 0) { + if ((verticalG & 0xFFFFFFFF) <= 0) { this.totalAirTime.current++ } @@ -93,7 +99,11 @@ export class RideMeasurements { } } - selectRide(index: number): void { + selectRide(index: number | null): void { + if (index == null) { + this.selectedRide = null + return + } this.selectedRide = this.rides[index]; this.reset() } @@ -134,7 +144,7 @@ export class RideMeasurements { return this.rides.map((ride) => ride.name); } - get currentFrontCar(): Car | null { + get getRideCars(): Car[] | null { if (this.selectedRide == null) return null; @@ -143,12 +153,14 @@ export class RideMeasurements { if (vehicleId != 0 && !vehicleId) return null; - const cars = map.getAllEntities("car"); + const cars = map.getAllEntities("car") + .filter((entity) => entity.id == vehicleId) + .map((entity) => entity as Car); if (cars.length == 0) return null; - return cars.filter((car) => car.id == vehicleId)[0] as Car; + return cars } }