Skip to content

Commit

Permalink
Allow dragger tools to snap vehicles to tracks
Browse files Browse the repository at this point in the history
  • Loading branch information
guysv committed Dec 7, 2024
1 parent 2df06ce commit 1005e46
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 24 deletions.
108 changes: 85 additions & 23 deletions src/services/vehicleDragger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { getCarById, RideVehicle } from "../objects/rideVehicle";
import { getTileElement } from "../utilities/map";
import { floor } from "../utilities/math";
import { cancelCurrentTool, cancelTools } from "../utilities/tools";
import { isUndefined } from "../utilities/type";
import { isNull, isUndefined } from "../utilities/type";
import { register } from "./actions";
import { invoke, refreshVehicle } from "./events";
import { getDistanceFromProgress } from "./spacingEditor";
import { equalCoordsXYZ } from "../utilities/coords";


const execute = register<DragVehicleArgs>("rve-drag-car", updateVehicleDrag);
Expand All @@ -19,7 +21,12 @@ export const dragToolId = "rve-drag-vehicle";
/**
* Enable or disable a tool to drag the vehicle to a new location.
*/
export function toggleVehicleDragger(isPressed: boolean, storeVehicle: Store<[RideVehicle, number] | null>, storeX: Store<number>, storeY: Store<number>, storeZ: Store<number>, onCancel: () => void): void
export function toggleVehicleDragger(isPressed: boolean,
storeVehicle: Store<[RideVehicle, number] | null>,
storeXYZ: Store<CoordsXYZ>,
storeTrackLocation: Store<CarTrackLocation | null>,
storeTrackProgress: Store<number>,
onCancel: () => void): void
{
const rideVehicle = storeVehicle.get();
if (!isPressed || !rideVehicle)
Expand All @@ -28,15 +35,19 @@ export function toggleVehicleDragger(isPressed: boolean, storeVehicle: Store<[Ri
return;
}

const originalXYZ = storeXYZ.get();
const multiplayer = isMultiplayer();
const originalPosition =
{
revert: true,
x: storeX.get(),
y: storeY.get(),
z: storeZ.get()
x: originalXYZ.x,
y: originalXYZ.y,
z: originalXYZ.z,
};
const originalTrackPosition = storeTrackLocation.get();
const originalTrackProgress = storeTrackProgress.get();
let lastPosition: CoordsXYZ = originalPosition;
let lastTrackPosition: CarTrackLocation | null = originalTrackPosition;

ui.activateTool({
id: dragToolId,
Expand All @@ -48,25 +59,42 @@ export function toggleVehicleDragger(isPressed: boolean, storeVehicle: Store<[Ri
// Limit updates to 8 fps to avoid bringing down multiplayer servers
return;
}
const position = getPositionFromTool(args, rideVehicle[0]._type());
if (position && (position.x !== lastPosition.x || position.y !== lastPosition.y || position.z !== lastPosition.z))
const [tilePosition, trackPosition] = getPositionFromTool(args, rideVehicle[0]._type(), rideVehicle[0]._id);
if (tilePosition && trackPosition && !equalCoordsXYZ(trackPosition, lastTrackPosition) ||
!trackPosition && tilePosition && !equalCoordsXYZ(tilePosition, lastPosition))
{
const position = {
tilePosition,
trackPosition,
trackProgress: null,
};
updateCarPosition(rideVehicle, position, DragState.Dragging);
ui.tileSelection.tiles = [{ x: alignWithMap(position.x), y : alignWithMap(position.y) }];
lastPosition = position;
ui.tileSelection.tiles = [{ x: alignWithMap(tilePosition.x), y : alignWithMap(tilePosition.y) }];
lastPosition = tilePosition;
lastTrackPosition = trackPosition;
}
},
onDown: () =>
{
originalPosition.revert = false;
updateCarPosition(rideVehicle, lastPosition, DragState.Complete);
const position = {
tilePosition: lastPosition,
trackPosition: lastTrackPosition,
trackProgress: null,
};
updateCarPosition(rideVehicle, position, DragState.Complete);
cancelCurrentTool();
},
onFinish: () =>
{
if (originalPosition.revert)
{
updateCarPosition(rideVehicle, originalPosition, DragState.Cancel);
const position = {
tilePosition: originalPosition,
trackPosition: originalTrackPosition,
trackProgress: originalTrackProgress,
};
updateCarPosition(rideVehicle, position, DragState.Cancel);
}
ui.tileSelection.tiles = [];
onCancel();
Expand All @@ -84,25 +112,33 @@ const enum DragState
Cancel
}

interface DragPosition
{
tilePosition: CoordsXYZ,
trackPosition: CarTrackLocation | null,
trackProgress: number | null,
}

/**
* Arguments of a currently dragged vehicle.
*/
interface DragVehicleArgs
{
target: number;
position: CoordsXYZ;
position: DragPosition;
state: DragState;
}

/**
* Get a possible position to drag the vehicle to.
*/
function getPositionFromTool(args: ToolEventArgs, vehicleType: RideObjectVehicle | null): CoordsXYZ | null
function getPositionFromTool(args: ToolEventArgs, vehicleType: RideObjectVehicle | null, currentId: number): [CoordsXYZ | null, CarTrackLocation | null]
{
const { entityId, mapCoords, tileElementIndex } = args;
let x: number | undefined, y: number | undefined, z: number | undefined;
let trackLocation: CarTrackLocation | null = null;

if (!isUndefined(entityId))
if (!isUndefined(entityId) && entityId !== currentId)
{
const entity = map.getEntity(entityId);
x = entity.x;
Expand All @@ -117,7 +153,7 @@ function getPositionFromTool(args: ToolEventArgs, vehicleType: RideObjectVehicle
const type = element.type;
const tabHeight = (vehicleType) ? vehicleType.tabHeight : 0;

z = (type === "footpath" || type === "banner" || type === "wall")
z = (type === "footpath" || type === "banner" || type === "wall" || type === "track")
? element.baseZ : element.clearanceZ;

// Custom heights for surface elements
Expand All @@ -134,21 +170,34 @@ function getPositionFromTool(args: ToolEventArgs, vehicleType: RideObjectVehicle
}
}

if (type === "track") {
const iterator = map.getTrackIterator({x, y}, tileElementIndex);
if (!isNull(iterator)) {
trackLocation = {
x: iterator.position.x-16, // back to block center
y: iterator.position.y-16,
z: iterator.position.z,
direction: element.direction,
trackType: element.trackType
};
}
}

// Increase height for certain vehicles like inverted ones based on the height in the tab icon
// 29 for actual inverted, inverted tabheight for other negatives, 0 for big cars
z += (tabHeight < -10) ? 29 : (tabHeight < 0) ? -tabHeight : 0;
}
else
{
return null;
return [null, null];
}
return { x, y, z };
return [{ x, y, z }, trackLocation];
}

/**
* Trigger the game action to inform all clients of the new location.
*/
function updateCarPosition(vehicle: [RideVehicle, number], position: CoordsXYZ, state: DragState): void
function updateCarPosition(vehicle: [RideVehicle, number], position: DragPosition, state: DragState): void
{
execute({ target: vehicle[0]._id, position, state });
}
Expand All @@ -165,10 +214,23 @@ function updateVehicleDrag(args: DragVehicleArgs): void
return;
}

const position = args.position;
car.x = position.x;
car.y = position.y;
car.z = position.z;
if (isNull(args.position.trackPosition)) {
const position = args.position.tilePosition;
car.x = position.x;
car.y = position.y;
car.z = position.z;
} else {
car.trackLocation = args.position.trackPosition;
if (!isNull(args.position.trackProgress)) {
car.travelBy(getDistanceFromProgress(car, args.position.trackProgress));
} else {
// internally, travelBy calls MoveTo so the car will render on the holding
// track
// HACK: seems to make the vehicle update render properly
car.travelBy(getDistanceFromProgress(car, 1));
car.travelBy(getDistanceFromProgress(car, -1));
}
}

invoke(refreshVehicle, id);
}
Expand All @@ -179,4 +241,4 @@ function updateVehicleDrag(args: DragVehicleArgs): void
function alignWithMap(coordinate: number): number
{
return floor(coordinate / 32) * 32;
}
}
2 changes: 1 addition & 1 deletion src/ui/mainWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const mainWindow = window({
image: 5174, // SPR_PICKUP_BTN
isPressed: twoway(model._isDragging),
disabled: model._isPositionDisabled,
onChange: pressed => toggleVehicleDragger(pressed, model._selectedVehicle, model._x, model._y, model._z, () => model._isDragging.set(false))
onChange: pressed => toggleVehicleDragger(pressed, model._selectedVehicle, model._xyz, model._trackLocation, model._trackProgress, () => model._isDragging.set(false))
}),
toggle({
width: buttonSize, height: buttonSize,
Expand Down
12 changes: 12 additions & 0 deletions src/utilities/coords.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { isNull } from "./type";

export function equalCoordsXYZ(a: CoordsXYZ, b: CoordsXYZ | null): boolean
{
if (isNull(b)) {
return false;
}

return a.x === b.x
&& a.y === b.y
&& a.z === b.z;
}
3 changes: 3 additions & 0 deletions src/viewmodels/vehicleViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ export class VehicleViewModel
readonly _poweredAcceleration = store<number>(0);
readonly _poweredMaxSpeed = store<number>(0);
readonly _trackProgress = store<number>(0);
readonly _trackLocation = store<CarTrackLocation | null>(null);
readonly _spacing = store<number | null>(0);
readonly _x = store<number>(0);
readonly _y = store<number>(0);
readonly _z = store<number>(0);
readonly _spin = store<number>(0);
readonly _xyz = compute(this._x, this._y, this._z, (x, y, z) => { return {x, y, z}; });

readonly _primaryColour = store<Colour>(0);
readonly _secondaryColour = store<Colour>(0);
Expand Down Expand Up @@ -276,6 +278,7 @@ export class VehicleViewModel
this._variant.set(car.vehicleObject);
this._mass.set(car.mass);
this._trackProgress.set(car.trackProgress);
this._trackLocation.set(car.trackLocation);
this._x.set(car.x);
this._y.set(car.y);
this._z.set(car.z);
Expand Down

0 comments on commit 1005e46

Please sign in to comment.