Skip to content

Commit

Permalink
feat(fs): fly mode
Browse files Browse the repository at this point in the history
  • Loading branch information
dockfries committed Jun 30, 2024
1 parent 31c53ac commit 0410faf
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 3 deletions.
7 changes: 4 additions & 3 deletions packages/core/src/enums/keys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum KeysEnum {
NONE = 0,
ACTION = 1,
CROUCH = 2,
FIRE = 4,
Expand All @@ -9,7 +10,7 @@ export enum KeysEnum {
HANDBRAKE = 128,
LOOK_LEFT = 256,
SUBMISSION = 512,
LOOK_BEHIND = 512,
LOOK_BEHIND = SUBMISSION,
WALK = 1024,
ANALOG_UP = 2048,
ANALOG_DOWN = 4096,
Expand All @@ -19,7 +20,7 @@ export enum KeysEnum {
NO = 131072,
CTRL_BACK = 262144,
KEY_UP = -128,
KEY_DOWN = 128,
KEY_DOWN = HANDBRAKE,
KEY_LEFT = -128,
KEY_RIGHT = 128,
KEY_RIGHT = HANDBRAKE,
}
3 changes: 3 additions & 0 deletions packages/filterscript/src/scripts/fly_mode/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Players Move Speed
export const MOVE_SPEED = 100.0;
export const ACCEL_RATE = 0.03;
17 changes: 17 additions & 0 deletions packages/filterscript/src/scripts/fly_mode/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Players Mode
export enum CameraMode {
NONE = 0,
FLY = 1,
}
// Key state definitions
export enum Move {
NONE = 0,
FORWARD = 1,
BACK = 2,
LEFT = 3,
RIGHT = 4,
FORWARD_LEFT = 5,
FORWARD_RIGHT = 6,
BACK_LEFT = 7,
BACK_RIGHT = 8,
}
287 changes: 287 additions & 0 deletions packages/filterscript/src/scripts/fly_mode/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
//-------------------------------------------------
//
// This is an example of using the AttachCameraToObject function
// to create a no-clip flying camera.
//
// h02 2012
//
// SA-MP 0.3e and above
//
//-------------------------------------------------

import type { IFilterScript } from "@infernus/core";
import { KeysEnum } from "@infernus/core";
import { DynamicObject, Player, PlayerEvent } from "@infernus/core";
import type { NoClipOptions } from "./interfaces";
import { CameraMode, Move } from "./enums";
import { ACCEL_RATE, MOVE_SPEED } from "./constants";

const noClipData = new Map<Player, NoClipOptions>();

function resetPlayerClip(player: Player) {
noClipData.set(player, {
cameraMode: CameraMode.NONE,
lrOld: KeysEnum.NONE,
udOld: KeysEnum.NONE,
mode: 0,
lastMove: 0,
accelMul: 0.0,
});
}

function getMoveDirectionFromKeys(ud: KeysEnum, lr: KeysEnum) {
let direction = 0;

if (lr < 0) {
if (ud < 0)
direction = Move.FORWARD_LEFT; // Up & Left key pressed
else if (ud > 0)
direction = Move.BACK_LEFT; // Back & Left key pressed
else direction = Move.LEFT; // Left key pressed
} else if (lr > 0) {
// Right pressed
if (ud < 0)
direction = Move.FORWARD_RIGHT; // Up & Right key pressed
else if (ud > 0)
direction = Move.BACK_RIGHT; // Back & Right key pressed
else direction = Move.RIGHT; // Right key pressed
} else if (ud < 0)
direction = Move.FORWARD; // Up key pressed
else if (ud > 0) direction = Move.BACK; // Down key pressed

return direction;
}

function moveCamera(player: Player) {
const { x: cpX, y: cpY, z: cpZ } = player.getCameraPos(); // Cameras position in space
const { x: fvX, y: fvY, z: fvZ } = player.getCameraFrontVector(); // Where the camera is looking at

const clip = noClipData.get(player)!;

// Increases the acceleration multiplier the longer the key is held
if (clip.accelMul <= 1) clip.accelMul += ACCEL_RATE;

// Determine the speed to move the camera based on the acceleration multiplier
const speed = MOVE_SPEED * clip.accelMul;

// Calculate the cameras next position based on their current position and the direction their camera is facing
const { x, y, z } = getNextCameraPosition(
clip.mode,
[cpX, cpY, cpZ],
[fvX, fvY, fvZ],
);
clip.flyObject?.move(x, y, z, speed);

// Store the last time the camera was moved as now
clip.lastMove = Date.now();

noClipData.set(player, clip);
}

function getNextCameraPosition(
move_mode: Move,
cp: [number, number, number],
fv: [number, number, number],
) {
// Calculate the cameras next position based on their current position and the direction their camera is facing
const offsetX = fv[0] * 6000.0;
const offsetY = fv[1] * 6000.0;
const offsetZ = fv[2] * 6000.0;
let x = 0,
y = 0,
z = 0;
switch (move_mode) {
case Move.FORWARD:
{
x = cp[0] + offsetX;
y = cp[1] + offsetY;
z = cp[2] + offsetZ;
}
break;
case Move.BACK:
{
x = cp[0] - offsetX;
y = cp[1] - offsetY;
z = cp[2] - offsetZ;
}
break;
case Move.LEFT:
{
x = cp[0] - offsetY;
y = cp[1] + offsetX;
z = cp[2];
}
break;
case Move.RIGHT:
{
x = cp[0] + offsetY;
y = cp[1] - offsetX;
z = cp[2];
}
break;
case Move.BACK_LEFT:
{
x = cp[0] + (-offsetX - offsetY);
y = cp[1] + (-offsetY + offsetX);
z = cp[2] - offsetZ;
}
break;
case Move.BACK_RIGHT:
{
x = cp[0] + (-offsetX + offsetY);
y = cp[1] + (-offsetY - offsetX);
z = cp[2] - offsetZ;
}
break;
case Move.FORWARD_LEFT:
{
x = cp[0] + (offsetX - offsetY);
y = cp[1] + (offsetY + offsetX);
z = cp[2] + offsetZ;
}
break;
case Move.FORWARD_RIGHT:
{
x = cp[0] + (offsetX + offsetY);
y = cp[1] + (offsetY - offsetX);
z = cp[2] + offsetZ;
}
break;
}
return { x, y, z };
}

function cancelFlyMode(player: Player, isConnected = true) {
const clip = noClipData.get(player);
if (clip && clip.cameraMode === CameraMode.FLY) {
clip.flyObject!.destroy();

if (isConnected) {
clip.cameraMode = CameraMode.NONE;

player.endObjectEditing();
player.toggleSpectating(false);

noClipData.set(player, clip);
} else {
noClipData.delete(player);
}
}
}

function flyMode(player: Player) {
// Create an invisible object for the players camera to be attached to
const { x, y, z } = player.getPos()!;

const clip = noClipData.get(player)!;

clip.flyObject = new DynamicObject({
modelId: 19300,
x,
y,
z,
playerId: player.id,
rx: 0,
ry: 0,
rz: 0,
});

clip.flyObject.create();

// Place the player in spectating mode so objects will be streamed based on camera location
player.toggleSpectating(true);
// Attach the players camera to the created object

clip.flyObject.attachCamera(player);
clip.cameraMode = CameraMode.FLY;

noClipData.set(player, clip);
}

export const FlyMode: IFilterScript = {
name: "fly_mode",
offs: [],
load() {
const onDisconnect = PlayerEvent.onDisconnect(({ player, next }) => {
cancelFlyMode(player, false);
return next();
});

const flyCommand = PlayerEvent.onCommandText(
"flymode",
({ player, next }) => {
// Place the player in and out of edit mode
const clip = noClipData.get(player);

if (!clip) {
resetPlayerClip(player);
flyMode(player);
return next();
}
if (clip.cameraMode === CameraMode.NONE) {
flyMode(player);
} else {
cancelFlyMode(player);
}
return next();
},
);

const onUpdate = PlayerEvent.onUpdate(({ player, next }) => {
const clip = noClipData.get(player);
if (!clip) return next();

if (clip.cameraMode === CameraMode.FLY) {
const { upDown: ud, leftRight: lr } = player.getKeys();

if (clip.mode && Date.now() - clip.lastMove > 100) {
// If the last move was > 100ms ago, process moving the object the players camera is attached to
moveCamera(player);
}

// Is the players current key state different than their last keystate?
if (clip.udOld !== ud || clip.lrOld !== lr) {
if (
(clip.udOld || clip.lrOld) &&
ud === KeysEnum.NONE &&
lr === KeysEnum.NONE
) {
// All keys have been released, stop the object the camera is attached to and reset the acceleration multiplier
clip.flyObject!.stop();
clip.mode = 0;
clip.accelMul = 0.0;
} else {
// Indicates a let key has been pressed

// Get the direction the player wants to move as indicated by the keys
clip.mode = getMoveDirectionFromKeys(ud, lr);

// Process moving the object the players camera is attached to
moveCamera(player);
}
}
clip.udOld = ud;
clip.lrOld = lr; // Store current keys pressed for comparison next update
noClipData.set(player, clip);
return false;
}
return next();
});

this.offs.push(onDisconnect, flyCommand, onUpdate);
},
unload() {
this.offs.forEach((off) => off());
this.offs = [];
// If any players are still in edit mode, boot them out before the filterscript unloads
Player.getInstances().forEach((p) => {
if (
!noClipData.has(p) ||
noClipData.get(p)!.cameraMode === CameraMode.FLY
)
return;
cancelFlyMode(p);
});
noClipData.clear();
},
};
13 changes: 13 additions & 0 deletions packages/filterscript/src/scripts/fly_mode/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { DynamicObject, KeysEnum } from "@infernus/core";
import type { CameraMode } from "../enums";

// Enumeration for storing data about the player
export interface NoClipOptions {
cameraMode: CameraMode;
flyObject?: DynamicObject;
mode: number;
lrOld: KeysEnum;
udOld: KeysEnum;
lastMove: number;
accelMul: number;
}
1 change: 1 addition & 0 deletions packages/filterscript/src/scripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from "./anti_flood";
export * from "./cargo_ship";
export * from "./dillimore_gas";
export * from "./ferris_wheel";
export * from "./fly_mode";

0 comments on commit 0410faf

Please sign in to comment.