@sagl/server 0.0.0-stage → 0.1.0-next.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -2
- package/dist/actions.d.ts +46 -0
- package/dist/actions.js +80 -0
- package/dist/actions.js.map +1 -0
- package/dist/animations.d.ts +32 -0
- package/dist/animations.js +95 -0
- package/dist/animations.js.map +1 -0
- package/dist/audio.d.ts +180 -0
- package/dist/audio.js +353 -0
- package/dist/audio.js.map +1 -0
- package/dist/audio_effects.d.ts +219 -0
- package/dist/audio_effects.js +84 -0
- package/dist/audio_effects.js.map +1 -0
- package/dist/camera.d.ts +188 -0
- package/dist/camera.js +247 -0
- package/dist/camera.js.map +1 -0
- package/dist/claims.d.ts +27 -0
- package/dist/claims.js +48 -0
- package/dist/claims.js.map +1 -0
- package/dist/commands.d.ts +135 -0
- package/dist/commands.js +373 -0
- package/dist/commands.js.map +1 -0
- package/dist/console.d.ts +21 -0
- package/dist/console.js +100 -0
- package/dist/console.js.map +1 -0
- package/dist/constants.d.ts +654 -0
- package/dist/constants.js +657 -0
- package/dist/constants.js.map +1 -0
- package/dist/cutscene.d.ts +47 -0
- package/dist/cutscene.js +190 -0
- package/dist/cutscene.js.map +1 -0
- package/dist/draw.d.ts +106 -0
- package/dist/draw.js +132 -0
- package/dist/draw.js.map +1 -0
- package/dist/effects.d.ts +91 -0
- package/dist/effects.js +116 -0
- package/dist/effects.js.map +1 -0
- package/dist/gangzones.d.ts +47 -0
- package/dist/gangzones.js +122 -0
- package/dist/gangzones.js.map +1 -0
- package/dist/geo.d.ts +11 -0
- package/dist/geo.js +25 -0
- package/dist/geo.js.map +1 -0
- package/dist/hud.d.ts +159 -0
- package/dist/hud.js +247 -0
- package/dist/hud.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction.d.ts +81 -0
- package/dist/interaction.js +143 -0
- package/dist/interaction.js.map +1 -0
- package/dist/junctions.d.ts +1 -0
- package/dist/junctions.js +330 -0
- package/dist/junctions.js.map +1 -0
- package/dist/map.d.ts +55 -0
- package/dist/map.js +71 -0
- package/dist/map.js.map +1 -0
- package/dist/menu.d.ts +96 -0
- package/dist/menu.js +129 -0
- package/dist/menu.js.map +1 -0
- package/dist/mission.d.ts +482 -0
- package/dist/mission.js +1258 -0
- package/dist/mission.js.map +1 -0
- package/dist/models.d.ts +56 -0
- package/dist/models.js +71 -0
- package/dist/models.js.map +1 -0
- package/dist/modshop.d.ts +229 -0
- package/dist/modshop.js +904 -0
- package/dist/modshop.js.map +1 -0
- package/dist/modshop_data.d.ts +44 -0
- package/dist/modshop_data.js +287 -0
- package/dist/modshop_data.js.map +1 -0
- package/dist/native.d.ts +809 -0
- package/dist/native.js +27 -0
- package/dist/native.js.map +1 -0
- package/dist/objects.d.ts +249 -0
- package/dist/objects.js +455 -0
- package/dist/objects.js.map +1 -0
- package/dist/paint.d.ts +124 -0
- package/dist/paint.js +84 -0
- package/dist/paint.js.map +1 -0
- package/dist/paynspray.d.ts +44 -0
- package/dist/paynspray.js +177 -0
- package/dist/paynspray.js.map +1 -0
- package/dist/ped.d.ts +335 -0
- package/dist/ped.js +536 -0
- package/dist/ped.js.map +1 -0
- package/dist/player.d.ts +685 -0
- package/dist/player.js +1163 -0
- package/dist/player.js.map +1 -0
- package/dist/screeneffects.d.ts +99 -0
- package/dist/screeneffects.js +257 -0
- package/dist/screeneffects.js.map +1 -0
- package/dist/scripts.d.ts +69 -0
- package/dist/scripts.js +125 -0
- package/dist/scripts.js.map +1 -0
- package/dist/server.d.ts +713 -0
- package/dist/server.js +1080 -0
- package/dist/server.js.map +1 -0
- package/dist/shopblips.d.ts +9 -0
- package/dist/shopblips.js +30 -0
- package/dist/shopblips.js.map +1 -0
- package/dist/special.d.ts +33 -0
- package/dist/special.js +26 -0
- package/dist/special.js.map +1 -0
- package/dist/splash.d.ts +42 -0
- package/dist/splash.js +55 -0
- package/dist/splash.js.map +1 -0
- package/dist/text.d.ts +19 -0
- package/dist/text.js +25 -0
- package/dist/text.js.map +1 -0
- package/dist/ui.d.ts +141 -0
- package/dist/ui.js +243 -0
- package/dist/ui.js.map +1 -0
- package/dist/vehicle.d.ts +306 -0
- package/dist/vehicle.js +612 -0
- package/dist/vehicle.js.map +1 -0
- package/dist/vehiclecolors.d.ts +26 -0
- package/dist/vehiclecolors.js +49 -0
- package/dist/vehiclecolors.js.map +1 -0
- package/dist/world.d.ts +222 -0
- package/dist/world.js +227 -0
- package/dist/world.js.map +1 -0
- package/dist/zone.d.ts +89 -0
- package/dist/zone.js +140 -0
- package/dist/zone.js.map +1 -0
- package/package.json +33 -4
package/dist/paint.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Paint finishes (`vehicle.paintFinish`): a car's paint drawn with a shader
|
|
2
|
+
// instead of the game's plain paint. Built in, or your own (an HLSL client
|
|
3
|
+
// file): see docs/server-api/paint.md.
|
|
4
|
+
const SLOTS = { both: 3, primary: 1, secondary: 2, all: 15 };
|
|
5
|
+
const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
6
|
+
const rgb = (c) => [((c >> 16) & 255) / 255, ((c >> 8) & 255) / 255, (c & 255) / 255];
|
|
7
|
+
/** @internal A finish and livery as sent (null: neither, the game's paint). */
|
|
8
|
+
export function encodePaintAndLivery(f, l) {
|
|
9
|
+
if (!f && !l)
|
|
10
|
+
return null;
|
|
11
|
+
const sent = f ? encodePaint(f) : { type: "", shader: "", params: "", values: [], slots: SLOTS[l?.on ?? "both"] ?? 3 };
|
|
12
|
+
if (l) {
|
|
13
|
+
sent.livery = l.texture;
|
|
14
|
+
sent.liveryMapping = l.mapping === "uv" ? 1 : 2;
|
|
15
|
+
sent.liveryScale = Math.max(0.05, l.scale ?? 4);
|
|
16
|
+
sent.liveryOpacity = Math.max(0, Math.min(1, l.opacity ?? 1));
|
|
17
|
+
sent.liveryMultiply = l.blend === "multiply";
|
|
18
|
+
if (!f)
|
|
19
|
+
sent.slots = SLOTS[l.on ?? "both"] ?? 3;
|
|
20
|
+
}
|
|
21
|
+
return sent;
|
|
22
|
+
}
|
|
23
|
+
/** @internal As sent: its type or shader, parameters ("name:size,...") and values (4 each). */
|
|
24
|
+
export function encodePaint(f) {
|
|
25
|
+
let type = "", shader = "";
|
|
26
|
+
let params = [];
|
|
27
|
+
if ("type" in f) {
|
|
28
|
+
switch (f.type) {
|
|
29
|
+
case "metallic":
|
|
30
|
+
type = "metallic";
|
|
31
|
+
params = [
|
|
32
|
+
["flakes", [f.flakes ?? 0.7]],
|
|
33
|
+
["flakeSize", [f.flakeSize ?? 1]],
|
|
34
|
+
["flakeColor", rgb(f.flakeColor ?? 0xffffff)],
|
|
35
|
+
["gloss", [f.gloss ?? 0.85]],
|
|
36
|
+
];
|
|
37
|
+
break;
|
|
38
|
+
case "pearl":
|
|
39
|
+
type = "pearl";
|
|
40
|
+
params = [
|
|
41
|
+
["pearlColor", rgb(f.pearlColor ?? 0xffffff)],
|
|
42
|
+
["flakes", [f.flakes ?? 0.35]],
|
|
43
|
+
["flakeSize", [f.flakeSize ?? 1]],
|
|
44
|
+
["gloss", [f.gloss ?? 0.9]],
|
|
45
|
+
];
|
|
46
|
+
break;
|
|
47
|
+
case "satin":
|
|
48
|
+
case "matte":
|
|
49
|
+
type = "satin";
|
|
50
|
+
params = [["sheen", [f.sheen ?? (f.type === "matte" ? 0.1 : 0.5)]]];
|
|
51
|
+
break;
|
|
52
|
+
case "colorShift":
|
|
53
|
+
type = "colorShift";
|
|
54
|
+
params = [
|
|
55
|
+
["hueShift", [f.hueShift ?? 30]],
|
|
56
|
+
["shift", [f.shift ?? 1]],
|
|
57
|
+
["flakes", [f.flakes ?? 0.35]],
|
|
58
|
+
["flakeSize", [f.flakeSize ?? 1]],
|
|
59
|
+
["gloss", [f.gloss ?? 0.9]],
|
|
60
|
+
];
|
|
61
|
+
break;
|
|
62
|
+
default:
|
|
63
|
+
throw new RangeError(`not a paint finish: ${String(f.type)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
shader = f.shader;
|
|
68
|
+
params = Object.entries(f.params ?? {}).map(([n, v]) => [n, Array.isArray(v) ? v : [v]]);
|
|
69
|
+
if (params.length > 8)
|
|
70
|
+
throw new RangeError(`at most 8 paint parameters (${params.length})`);
|
|
71
|
+
for (const [n, v] of params) {
|
|
72
|
+
if (!NAME.test(n))
|
|
73
|
+
throw new RangeError(`not a parameter name: "${n}"`);
|
|
74
|
+
if (v.length < 1 || v.length > 4)
|
|
75
|
+
throw new RangeError(`parameter ${n}: 1 to 4 numbers`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const values = params.flatMap(([, v]) => [0, 1, 2, 3].map((i) => Number(v[i] ?? 0)));
|
|
79
|
+
const slots = SLOTS[f.on ?? "both"];
|
|
80
|
+
if (slots === undefined)
|
|
81
|
+
throw new RangeError(`not paint slots: ${f.on}`);
|
|
82
|
+
return { type, shader, params: params.map(([n, v]) => `${n}:${v.length}`).join(","), values, slots };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=paint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paint.js","sourceRoot":"","sources":["../src/paint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Player } from "./player.js";
|
|
2
|
+
import type { Server } from "./server.js";
|
|
3
|
+
import type { Vehicle } from "./vehicle.js";
|
|
4
|
+
import { type Garage } from "./zone.js";
|
|
5
|
+
export interface PayNSprayOptions {
|
|
6
|
+
/** What a respray costs. Default 100 (the game's). */
|
|
7
|
+
price?: number;
|
|
8
|
+
/** Their wanted level cleared (on parole, as the game's: the stars flash, and a crime brings them back). Default true. */
|
|
9
|
+
clearWanted?: boolean;
|
|
10
|
+
/** The car repaired. Default true. */
|
|
11
|
+
repair?: boolean;
|
|
12
|
+
/** New colours (never the one it had; a modded car, `vehicle.modded`, keeps its own). Default true. */
|
|
13
|
+
respray?: boolean;
|
|
14
|
+
/** A cloud of dust in the new colour rolling out of the door. Default true. */
|
|
15
|
+
dust?: boolean;
|
|
16
|
+
/** Its blip (the game's spray can) on everyone's radar, near it. Default true. */
|
|
17
|
+
blips?: boolean;
|
|
18
|
+
/** Models turned away as too hot. Default: the game's law enforcement vehicles. */
|
|
19
|
+
hotModels?: number[];
|
|
20
|
+
/**
|
|
21
|
+
* Asked as they drive in, after the game's own checks: return false to
|
|
22
|
+
* turn them away (say why yourself), or a text to turn them away with it
|
|
23
|
+
* (as the game's notices), or a number for the price this time.
|
|
24
|
+
*/
|
|
25
|
+
onEnter?: (player: Player, vehicle: Vehicle, garage: Garage) => boolean | string | number | void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The game's Pay 'n' Sprays (`server.payNSpray`), on by default: drive in,
|
|
29
|
+
* the door shuts, the car is repaired and resprayed and the wanted level
|
|
30
|
+
* cleared, for $100. Turn it off to build your own from `garage.zone`'s
|
|
31
|
+
* events, or change how it works with `configure`. Fires `vehicleRespray`.
|
|
32
|
+
*/
|
|
33
|
+
export declare class PayNSpray {
|
|
34
|
+
#private;
|
|
35
|
+
/** @internal */
|
|
36
|
+
constructor(server: Server, emit: (player: Player, vehicle: Vehicle, colors: [number, number]) => void);
|
|
37
|
+
/** Whether they work (default true). Off, their doors stay as they are: open them as you need. */
|
|
38
|
+
get enabled(): boolean;
|
|
39
|
+
set enabled(on: boolean);
|
|
40
|
+
/** Changes how they work (only the options given). */
|
|
41
|
+
configure(options: PayNSprayOptions): this;
|
|
42
|
+
/** The game's Pay 'n' Spray garages. */
|
|
43
|
+
get garages(): Garage[];
|
|
44
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Pay 'n' Spray, as the game's. Players' games run no garage logic (it was
|
|
2
|
+
// the game's own code, for one player), so the server does it, the way the
|
|
3
|
+
// game did:
|
|
4
|
+
//
|
|
5
|
+
// - Drive in. A police car (or the like) is turned away ("Whoa! I don't
|
|
6
|
+
// touch nothing THAT hot!"), an RC toy too ("I don't do toys!"), and so is
|
|
7
|
+
// anyone without the price ("No more freebies!"), each with the game's
|
|
8
|
+
// sound for it.
|
|
9
|
+
// - Otherwise, once the car is wholly inside, the door shuts, holding them
|
|
10
|
+
// still, and the spray hisses (the game's sound, as long as the game plays
|
|
11
|
+
// it). Then the car is repaired and resprayed (another colour), their
|
|
12
|
+
// wanted level cleared on parole (the old stars flash for 20 s, and a
|
|
13
|
+
// crime brings them back) and the price taken, and the door opens, with
|
|
14
|
+
// the game's notice.
|
|
15
|
+
//
|
|
16
|
+
// Not the game's, an extra: a cloud of dust in the new paint's colour rolls
|
|
17
|
+
// out of the door as it opens.
|
|
18
|
+
import { GameSound } from "./constants.js";
|
|
19
|
+
import { gxt } from "./text.js";
|
|
20
|
+
import { GarageType } from "./zone.js";
|
|
21
|
+
import { ShopBlips } from "./shopblips.js";
|
|
22
|
+
const DOOR_MS = 1500; // the door shutting
|
|
23
|
+
const SPRAY_MS = 1900; // the spray's hiss: as long as the game plays it
|
|
24
|
+
const NOTICE_MS = 4000;
|
|
25
|
+
const DUST_AFTER_MS = 700; // the door part open
|
|
26
|
+
const INSIDE_BY = 2.6; // m from the walls: a car, wholly inside
|
|
27
|
+
// The game's law enforcement vehicles (the cops would know them anywhere),
|
|
28
|
+
// and its RC toys.
|
|
29
|
+
const HOT = [427, 430, 432, 433, 490, 497, 523, 528, 596, 597, 598, 599, 601];
|
|
30
|
+
const TOYS = new Set([441, 464, 465, 501, 564, 594]);
|
|
31
|
+
// Wheel Arch Angels is a Pay 'n' Spray in the map files (the game's script
|
|
32
|
+
// makes it a mod shop).
|
|
33
|
+
const NOT_SPRAYS = new Set(["mds1SFS"]);
|
|
34
|
+
const SPRAY_SPRITE = 63; // RADAR_SPRITE_SPRAY
|
|
35
|
+
/**
|
|
36
|
+
* The game's Pay 'n' Sprays (`server.payNSpray`), on by default: drive in,
|
|
37
|
+
* the door shuts, the car is repaired and resprayed and the wanted level
|
|
38
|
+
* cleared, for $100. Turn it off to build your own from `garage.zone`'s
|
|
39
|
+
* events, or change how it works with `configure`. Fires `vehicleRespray`.
|
|
40
|
+
*/
|
|
41
|
+
export class PayNSpray {
|
|
42
|
+
#server;
|
|
43
|
+
#emit;
|
|
44
|
+
#enabled = true;
|
|
45
|
+
#o = {};
|
|
46
|
+
#busy = new Set();
|
|
47
|
+
// "player:garage": turned away, or just resprayed, until they drive out.
|
|
48
|
+
#untilTheyLeave = new Set();
|
|
49
|
+
// The price agreed as they drove in.
|
|
50
|
+
#price = new Map();
|
|
51
|
+
#blips;
|
|
52
|
+
/** @internal */
|
|
53
|
+
constructor(server, emit) {
|
|
54
|
+
this.#server = server;
|
|
55
|
+
this.#emit = emit;
|
|
56
|
+
server.on("zoneEnter", (p, z) => this.#enter(p, z.garage));
|
|
57
|
+
server.on("zoneLeave", (p, z) => {
|
|
58
|
+
if (z.garage) {
|
|
59
|
+
this.#untilTheyLeave.delete(this.#key(p, z.garage));
|
|
60
|
+
this.#price.delete(this.#key(p, z.garage));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
server.on("zoneMove", (p, z) => this.#move(p, z.garage));
|
|
64
|
+
this.#blips = new ShopBlips(server, SPRAY_SPRITE, () => this.#enabled && (this.#o.blips ?? true) ? this.garages.map((g) => g.center) : []);
|
|
65
|
+
}
|
|
66
|
+
/** Whether they work (default true). Off, their doors stay as they are: open them as you need. */
|
|
67
|
+
get enabled() {
|
|
68
|
+
return this.#enabled;
|
|
69
|
+
}
|
|
70
|
+
set enabled(on) {
|
|
71
|
+
this.#enabled = on;
|
|
72
|
+
this.#blips.refresh();
|
|
73
|
+
}
|
|
74
|
+
/** Changes how they work (only the options given). */
|
|
75
|
+
configure(options) {
|
|
76
|
+
Object.assign(this.#o, options);
|
|
77
|
+
this.#blips.refresh();
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
/** The game's Pay 'n' Spray garages. */
|
|
81
|
+
get garages() {
|
|
82
|
+
return this.#server.garages.filter((g) => this.#isSpray(g));
|
|
83
|
+
}
|
|
84
|
+
#isSpray(g) {
|
|
85
|
+
return g !== null && g.type === GarageType.PayNSpray && !NOT_SPRAYS.has(g.name);
|
|
86
|
+
}
|
|
87
|
+
#key(player, garage) {
|
|
88
|
+
return `${player.id}:${garage.id}`;
|
|
89
|
+
}
|
|
90
|
+
#sprayable(g) {
|
|
91
|
+
return this.#enabled && this.#isSpray(g) && !this.#busy.has(g);
|
|
92
|
+
}
|
|
93
|
+
#enter(player, garage) {
|
|
94
|
+
const vehicle = player.vehicle;
|
|
95
|
+
if (!this.#sprayable(garage) || !vehicle || vehicle.driver !== player)
|
|
96
|
+
return;
|
|
97
|
+
const key = this.#key(player, garage);
|
|
98
|
+
const away = (text, sound) => {
|
|
99
|
+
player.hud.notice(text, NOTICE_MS);
|
|
100
|
+
this.#server.audio.playGame(sound, { to: [player] });
|
|
101
|
+
this.#untilTheyLeave.add(key);
|
|
102
|
+
};
|
|
103
|
+
const hot = (this.#o.hotModels ?? HOT).includes(vehicle.model);
|
|
104
|
+
if (hot || TOYS.has(vehicle.model))
|
|
105
|
+
return away(gxt(TOYS.has(vehicle.model) ? "GA_1B" : "GA_1"), GameSound.CarIsHot);
|
|
106
|
+
let price = this.#o.price ?? 100;
|
|
107
|
+
const said = this.#o.onEnter?.(player, vehicle, garage);
|
|
108
|
+
if (said === false)
|
|
109
|
+
return void this.#untilTheyLeave.add(key);
|
|
110
|
+
if (typeof said === "string")
|
|
111
|
+
return away(said, GameSound.CarIsHot);
|
|
112
|
+
if (typeof said === "number")
|
|
113
|
+
price = Math.max(0, said);
|
|
114
|
+
this.#price.set(key, price);
|
|
115
|
+
if (player.money < price) {
|
|
116
|
+
away(price === 100 ? gxt("GA_3") : `No more freebies~n~$${price} to respray!`, GameSound.CarNoCash);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
#move(player, garage) {
|
|
120
|
+
const vehicle = player.vehicle;
|
|
121
|
+
if (!this.#sprayable(garage) || !vehicle || vehicle.driver !== player)
|
|
122
|
+
return;
|
|
123
|
+
const key = this.#key(player, garage);
|
|
124
|
+
if (this.#untilTheyLeave.has(key))
|
|
125
|
+
return;
|
|
126
|
+
const price = this.#price.get(key) ?? this.#o.price ?? 100;
|
|
127
|
+
if (!garage.contains(vehicle.position, INSIDE_BY) || player.money < price)
|
|
128
|
+
return;
|
|
129
|
+
this.#busy.add(garage);
|
|
130
|
+
garage.close();
|
|
131
|
+
player.frozen = true;
|
|
132
|
+
// Shut: the spray.
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
if (player.connected)
|
|
135
|
+
this.#server.audio.playGame(GameSound.CarRespray, { to: [player] });
|
|
136
|
+
}, DOOR_MS);
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
let colors = vehicle.exists ? vehicle.colors : [0, 0];
|
|
139
|
+
if (vehicle.exists) {
|
|
140
|
+
// A modified car (a mod shop's) keeps its paint: only repaired.
|
|
141
|
+
if ((this.#o.respray ?? true) && !vehicle.modded) {
|
|
142
|
+
// Another colour: never the one it had.
|
|
143
|
+
const [old] = vehicle.colors;
|
|
144
|
+
let paint = old;
|
|
145
|
+
while (paint === old)
|
|
146
|
+
paint = Math.floor(Math.random() * 127);
|
|
147
|
+
colors = [paint, Math.floor(Math.random() * 127)];
|
|
148
|
+
vehicle.colors = colors;
|
|
149
|
+
}
|
|
150
|
+
if (this.#o.repair ?? true)
|
|
151
|
+
vehicle.repair();
|
|
152
|
+
vehicle.dirt = 0; // fresh paint: clean
|
|
153
|
+
if (this.#o.dust ?? true) {
|
|
154
|
+
// The dust: a cloud in the new colour, rolling out of the door as it opens.
|
|
155
|
+
const paint = colors[0];
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
if (vehicle.exists)
|
|
158
|
+
this.#server.effects.dust(vehicle, { carColor: paint, count: 30, spread: 2.5, size: 2, rise: 0.3 });
|
|
159
|
+
}, DUST_AFTER_MS);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (player.connected) {
|
|
163
|
+
player.giveMoney(-price);
|
|
164
|
+
if (this.#o.clearWanted ?? true)
|
|
165
|
+
player.clearWantedLevel({ parole: true });
|
|
166
|
+
player.frozen = false;
|
|
167
|
+
player.hud.notice(price === 100 ? gxt("GA_2") : `New engine and paint job: $${price}~n~Cops won't recognize you!`, NOTICE_MS);
|
|
168
|
+
if (vehicle.exists)
|
|
169
|
+
this.#emit(player, vehicle, colors);
|
|
170
|
+
}
|
|
171
|
+
garage.open();
|
|
172
|
+
this.#busy.delete(garage);
|
|
173
|
+
this.#untilTheyLeave.add(key);
|
|
174
|
+
}, DOOR_MS + SPRAY_MS);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=paynspray.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paynspray.js","sourceRoot":"","sources":["../src/paynspray.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,2EAA2E;AAC3E,YAAY;AACZ,EAAE;AACF,wEAAwE;AACxE,6EAA6E;AAC7E,yEAAyE;AACzE,kBAAkB;AAClB,2EAA2E;AAC3E,6EAA6E;AAC7E,wEAAwE;AACxE,wEAAwE;AACxE,0EAA0E;AAC1E,uBAAuB;AACvB,EAAE;AACF,4EAA4E;AAC5E,+BAA+B;AAE/B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,OAAO,EAAE,UAAU,EAAe,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAyB3C,MAAM,OAAO,GAAG,IAAI,CAAC,CAAM,oBAAoB;AAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAK,iDAAiD;AAC5E,MAAM,SAAS,GAAG,IAAI,CAAC;AACvB,MAAM,aAAa,GAAG,GAAG,CAAC,CAAC,qBAAqB;AAChD,MAAM,SAAS,GAAG,GAAG,CAAC,CAAK,yCAAyC;AAEpE,2EAA2E;AAC3E,mBAAmB;AACnB,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC9E,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAErD,2EAA2E;AAC3E,wBAAwB;AACxB,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AACxC,MAAM,YAAY,GAAG,EAAE,CAAC,CAAE,qBAAqB;AAE/C;;;;;GAKG;AACH,MAAM,OAAO,SAAS;IACpB,OAAO,CAAS;IAChB,KAAK,CAAuE;IAC5E,QAAQ,GAAG,IAAI,CAAC;IAChB,EAAE,GAAqB,EAAE,CAAC;IAC1B,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAC1B,yEAAyE;IACzE,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,qCAAqC;IACrC,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACnC,MAAM,CAAY;IAElB,gBAAgB;IAChB,YAAY,MAAc,EAAE,IAA0E;QACpG,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC9B,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBACb,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;gBACpD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,CACrD,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,kGAAkG;IAClG,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,CAAC,EAAW;QACrB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,sDAAsD;IACtD,SAAS,CAAC,OAAyB;QACjC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wCAAwC;IACxC,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,QAAQ,CAAC,CAAgB;QACvB,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClF,CAAC;IAED,IAAI,CAAC,MAAsB,EAAE,MAAc;QACzC,OAAO,GAAG,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;IACrC,CAAC;IAED,UAAU,CAAC,CAAgB;QACzB,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,CAAC,MAAc,EAAE,MAAqB;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO;QAC9E,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE;YAC3C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACnC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACrD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC,CAAC;QACF,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC/D,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;QACrH,IAAI,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACxD,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,KAAK,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9D,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;QACpE,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,uBAAuB,KAAK,cAAc,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;QACtG,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAc,EAAE,MAAqB;QACzC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO;QAC9E,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,KAAK;YAAE,OAAO;QAClF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvB,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,mBAAmB;QACnB,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,MAAM,CAAC,SAAS;gBAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC,EAAE,OAAO,CAAC,CAAC;QACZ,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,MAAM,GAAqB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACxE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,gEAAgE;gBAChE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBACjD,wCAAwC;oBACxC,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;oBAC7B,IAAI,KAAK,GAAG,GAAG,CAAC;oBAChB,OAAO,KAAK,KAAK,GAAG;wBAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;oBAC9D,MAAM,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;oBAClD,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;gBAC1B,CAAC;gBACD,IAAI,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,IAAI;oBAAE,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC7C,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAE,qBAAqB;gBACxC,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;oBACzB,4EAA4E;oBAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;oBACxB,UAAU,CAAC,GAAG,EAAE;wBACd,IAAI,OAAO,CAAC,MAAM;4BAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;oBAC1H,CAAC,EAAE,aAAa,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC;YACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrB,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC;gBACzB,IAAI,IAAI,CAAC,EAAE,CAAC,WAAW,IAAI,IAAI;oBAAE,MAAM,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3E,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;gBACtB,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,8BAA8B,KAAK,8BAA8B,EAAE,SAAS,CAAC,CAAC;gBAC9H,IAAI,OAAO,CAAC,MAAM;oBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1D,CAAC;YACD,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC1B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC;IACzB,CAAC;CACF"}
|
package/dist/ped.d.ts
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { type AudioEffects } from "./audio_effects.js";
|
|
2
|
+
import type { NativePedStep, NativeServer } from "./native.js";
|
|
3
|
+
import type { Player, Vector3 } from "./player.js";
|
|
4
|
+
import type { Vehicle } from "./vehicle.js";
|
|
5
|
+
import type { Hit } from "./server.js";
|
|
6
|
+
import type { SpeakOptions, Speech } from "./audio.js";
|
|
7
|
+
/** @internal */
|
|
8
|
+
export declare function setPedSpeaker(fn: (file: string, options: SpeakOptions) => Speech): void;
|
|
9
|
+
/** Who a ped's order is about: a player or another ped. */
|
|
10
|
+
export type PedTarget = Player | Ped;
|
|
11
|
+
/** How fast it goes. */
|
|
12
|
+
export type Pace = "walk" | "run" | "sprint";
|
|
13
|
+
/**
|
|
14
|
+
* One step of an order (`ped.sequence()`): a game task its owner's game
|
|
15
|
+
* runs with the ped's own AI. Exactly one of the keys below.
|
|
16
|
+
*/
|
|
17
|
+
export type PedStep = {
|
|
18
|
+
walkTo: Vector3;
|
|
19
|
+
pace?: Pace;
|
|
20
|
+
straight?: boolean;
|
|
21
|
+
radius?: number;
|
|
22
|
+
} | {
|
|
23
|
+
follow: PedTarget;
|
|
24
|
+
distance?: number;
|
|
25
|
+
ms?: number;
|
|
26
|
+
} | {
|
|
27
|
+
fleeFrom: PedTarget;
|
|
28
|
+
distance?: number;
|
|
29
|
+
ms?: number;
|
|
30
|
+
} | {
|
|
31
|
+
face: PedTarget | Vector3;
|
|
32
|
+
} | {
|
|
33
|
+
lookAt: PedTarget;
|
|
34
|
+
ms: number;
|
|
35
|
+
} | {
|
|
36
|
+
standStill: number | true;
|
|
37
|
+
} | {
|
|
38
|
+
handsUp: number | true;
|
|
39
|
+
} | {
|
|
40
|
+
cower: number | true;
|
|
41
|
+
} | {
|
|
42
|
+
attack: PedTarget;
|
|
43
|
+
} | {
|
|
44
|
+
wander: true;
|
|
45
|
+
} | {
|
|
46
|
+
playAnim: [block: string, name: string];
|
|
47
|
+
loop?: boolean;
|
|
48
|
+
ms?: number;
|
|
49
|
+
} | {
|
|
50
|
+
enterVehicle: Vehicle;
|
|
51
|
+
seat?: number;
|
|
52
|
+
} | {
|
|
53
|
+
leaveVehicle: true;
|
|
54
|
+
} | {
|
|
55
|
+
driveTo: Vector3;
|
|
56
|
+
speed?: number;
|
|
57
|
+
hurry?: boolean;
|
|
58
|
+
radius?: number;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* How an order ended. `"cancelled"`: replaced by another order, or
|
|
62
|
+
* `ped.stop()` (`reason` says which). `"failed"`: `reason` says why
|
|
63
|
+
* (`"died"`, `"removed"`, `"unreachable"`, `"target lost"`, `"animation"`,
|
|
64
|
+
* `"in a vehicle"`, `"refused"`).
|
|
65
|
+
*/
|
|
66
|
+
export interface OrderResult {
|
|
67
|
+
status: "done" | "failed" | "cancelled";
|
|
68
|
+
reason?: string;
|
|
69
|
+
/** The step it ended at (the last, if done). */
|
|
70
|
+
step: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* How a ped behaves beyond its orders: mission characters' settings
|
|
74
|
+
* (`createPed(..., { mission })`, `ped.set()`).
|
|
75
|
+
*/
|
|
76
|
+
export interface PedSettings {
|
|
77
|
+
/**
|
|
78
|
+
* `"normal"`: as the game's peds of its kind (flees gunfire, ...).
|
|
79
|
+
* `"none"`: ignores everything. `"self-defence"`: ignores everything but
|
|
80
|
+
* fights whoever hits it (unless busy with an order and `keepTask`).
|
|
81
|
+
*/
|
|
82
|
+
reactions?: "normal" | "none" | "self-defence";
|
|
83
|
+
/** Being hurt or bumped doesn't stop its order. */
|
|
84
|
+
keepTask?: boolean;
|
|
85
|
+
/** Takes no damage at all (all of `proofs`). */
|
|
86
|
+
invincible?: boolean;
|
|
87
|
+
/** Takes no damage of these kinds (enforced by the server too). */
|
|
88
|
+
proofs?: {
|
|
89
|
+
bullet?: boolean;
|
|
90
|
+
fire?: boolean;
|
|
91
|
+
explosion?: boolean;
|
|
92
|
+
collision?: boolean;
|
|
93
|
+
melee?: boolean;
|
|
94
|
+
};
|
|
95
|
+
/** False: players can't drag it out of a vehicle. Default true. */
|
|
96
|
+
draggable?: boolean;
|
|
97
|
+
/** Aim, 1-100; left out: its own. */
|
|
98
|
+
accuracy?: number;
|
|
99
|
+
/** Attacks these players on sight (when not busy with an order). */
|
|
100
|
+
hostileTo?: readonly Player[];
|
|
101
|
+
/** How near "on sight" is, metres. Default 40. */
|
|
102
|
+
hostileRange?: number;
|
|
103
|
+
}
|
|
104
|
+
/** A ped's own events: `ped.on(...)`. */
|
|
105
|
+
export interface PedEvents {
|
|
106
|
+
/** An order's step finished; the next starts. */
|
|
107
|
+
stepDone: (step: number, orderId: number) => void;
|
|
108
|
+
/** An order finished. */
|
|
109
|
+
orderDone: (orderId: number) => void;
|
|
110
|
+
/** An order failed. */
|
|
111
|
+
orderFailed: (reason: string, step: number, orderId: number) => void;
|
|
112
|
+
}
|
|
113
|
+
/** @internal A step, as the native server takes it. */
|
|
114
|
+
export declare function encodeStep(step: PedStep): NativePedStep;
|
|
115
|
+
/** @internal What a `say()` sends, and how long it lasts. */
|
|
116
|
+
export declare function speechOf(native: NativeServer, line: {
|
|
117
|
+
subtitle?: string;
|
|
118
|
+
dialogue?: string;
|
|
119
|
+
table?: string;
|
|
120
|
+
ms?: number;
|
|
121
|
+
}): {
|
|
122
|
+
subtitle: string;
|
|
123
|
+
dialogue: string;
|
|
124
|
+
ms: number;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* A ped (NPC) created with `server.createPed()`. The nearest player's game
|
|
128
|
+
* simulates it, running its own AI as for any ped in single-player (it
|
|
129
|
+
* flees gunfire, falls over when hit, dies), and everyone sees the same ped.
|
|
130
|
+
* The server checks what that game reports.
|
|
131
|
+
*/
|
|
132
|
+
export declare class Ped {
|
|
133
|
+
#private;
|
|
134
|
+
readonly id: number;
|
|
135
|
+
/** Ped model (skin). */
|
|
136
|
+
readonly model: number;
|
|
137
|
+
/** The story character it is ("ryder"), or null: then `model` is 0. */
|
|
138
|
+
readonly character: string | null;
|
|
139
|
+
/** Free-form per-ped storage for gamemode data. */
|
|
140
|
+
readonly data: Record<string, unknown>;
|
|
141
|
+
/** @internal Created by Server. */
|
|
142
|
+
constructor(nativeServer: NativeServer, id: number, player: (id: number) => Player | undefined, vehicle: (id: number) => Vehicle | undefined, destroy: (ped: Ped) => void);
|
|
143
|
+
/** False once destroyed. */
|
|
144
|
+
get exists(): boolean;
|
|
145
|
+
/** @internal */
|
|
146
|
+
markDestroyed(): void;
|
|
147
|
+
/** Walks (or runs, sprints) to `position`, along the paths (or `straight`). Done within `radius` (1 m). */
|
|
148
|
+
walkTo(position: Vector3, options?: {
|
|
149
|
+
pace?: Pace;
|
|
150
|
+
straight?: boolean;
|
|
151
|
+
radius?: number;
|
|
152
|
+
}): Promise<OrderResult>;
|
|
153
|
+
/** Keeps within `distance` (2 m) of `target`, for `ms` (until the next order if left out). */
|
|
154
|
+
follow(target: PedTarget, options?: {
|
|
155
|
+
distance?: number;
|
|
156
|
+
ms?: number;
|
|
157
|
+
}): Promise<OrderResult>;
|
|
158
|
+
/** Runs from `target` until `distance` (50 m) away, or `ms` is up. */
|
|
159
|
+
fleeFrom(target: PedTarget, options?: {
|
|
160
|
+
distance?: number;
|
|
161
|
+
ms?: number;
|
|
162
|
+
}): Promise<OrderResult>;
|
|
163
|
+
/** Turns to face a player, a ped or a position. */
|
|
164
|
+
face(target: PedTarget | Vector3): Promise<OrderResult>;
|
|
165
|
+
/** Looks at `target` for `ms`. */
|
|
166
|
+
lookAt(target: PedTarget, ms: number): Promise<OrderResult>;
|
|
167
|
+
/** Stands still for `ms` (until the next order if left out). */
|
|
168
|
+
standStill(ms?: number): Promise<OrderResult>;
|
|
169
|
+
/** Puts its hands up for `ms` (until the next order if left out). */
|
|
170
|
+
handsUp(ms?: number): Promise<OrderResult>;
|
|
171
|
+
/** Cowers for `ms` (until the next order if left out). */
|
|
172
|
+
cower(ms?: number): Promise<OrderResult>;
|
|
173
|
+
/** Attacks `target` with what it holds, until one of them is dead. */
|
|
174
|
+
attack(target: PedTarget): Promise<OrderResult>;
|
|
175
|
+
/** Wanders around (until the next order). */
|
|
176
|
+
wander(): Promise<OrderResult>;
|
|
177
|
+
/**
|
|
178
|
+
* Walks to `vehicle` and gets in: its driver's seat (`seat` 0, the
|
|
179
|
+
* default) or a passenger's (1-8). Done once it's sitting there.
|
|
180
|
+
*/
|
|
181
|
+
enterVehicle(vehicle: Vehicle, seat?: number): Promise<OrderResult>;
|
|
182
|
+
/** Gets out of the vehicle it's in (done at once if it's on foot). */
|
|
183
|
+
leaveVehicle(): Promise<OrderResult>;
|
|
184
|
+
/**
|
|
185
|
+
* Drives the vehicle it's driving to `position` along the roads, at
|
|
186
|
+
* `speed` m/s (default 12), by the rules (stopping for traffic and
|
|
187
|
+
* lights) or, with `hurry`, weaving round traffic and through lights.
|
|
188
|
+
* Done within `radius` (8 m).
|
|
189
|
+
*/
|
|
190
|
+
driveTo(position: Vector3, options?: {
|
|
191
|
+
speed?: number;
|
|
192
|
+
hurry?: boolean;
|
|
193
|
+
radius?: number;
|
|
194
|
+
}): Promise<OrderResult>;
|
|
195
|
+
/** Plays one of the game's animations (`block`, `name`), once or `loop`ing, for `ms`. */
|
|
196
|
+
playAnim(block: string, name: string, options?: {
|
|
197
|
+
loop?: boolean;
|
|
198
|
+
ms?: number;
|
|
199
|
+
}): Promise<OrderResult>;
|
|
200
|
+
/**
|
|
201
|
+
* Several steps, run one after another by its owner's game with no pause
|
|
202
|
+
* between them (up to 16). Resolves when the last is done, or one fails.
|
|
203
|
+
*/
|
|
204
|
+
sequence(steps: PedStep[]): Promise<OrderResult>;
|
|
205
|
+
/** Stops what it's been ordered to do: back to standing (or wandering). */
|
|
206
|
+
stop(): void;
|
|
207
|
+
/**
|
|
208
|
+
* Who sees it: `"world"` (the default), everyone its virtual world shows
|
|
209
|
+
* it to (its world, and worlds viewing it); or only these players (while
|
|
210
|
+
* they're in its world or one viewing it). Players who can't see it can't
|
|
211
|
+
* hit it, nor it them. Players who leave drop off the list.
|
|
212
|
+
*/
|
|
213
|
+
get visibleTo(): readonly Player[] | "world";
|
|
214
|
+
set visibleTo(players: readonly Player[] | "world");
|
|
215
|
+
/**
|
|
216
|
+
* The player whose game always runs it, at any distance, while they can
|
|
217
|
+
* see it (a mission's player, so a chase or a follow plays out on their
|
|
218
|
+
* screen); null, the nearest player's (the default). If they can't have
|
|
219
|
+
* it (left, another world), the nearest takes over meanwhile.
|
|
220
|
+
*/
|
|
221
|
+
get controller(): Player | null;
|
|
222
|
+
set controller(player: Player | null);
|
|
223
|
+
/** Its settings (see `PedSettings`). */
|
|
224
|
+
get settings(): Readonly<PedSettings>;
|
|
225
|
+
/**
|
|
226
|
+
* Changes its settings; fields left out keep their value. For mission
|
|
227
|
+
* characters: `{ reactions: "self-defence", keepTask: true }` is what
|
|
228
|
+
* `createPed`'s `mission` option starts from.
|
|
229
|
+
*/
|
|
230
|
+
set(settings: PedSettings): this;
|
|
231
|
+
/** @internal Set at creation (the server has them already). */
|
|
232
|
+
initVisibility(visibleTo: readonly Player[] | "world", controller: Player | null): void;
|
|
233
|
+
/** @internal A player left: off its hostile and visibility lists (the server drops them too). */
|
|
234
|
+
forgetPlayer(player: Player): void;
|
|
235
|
+
/**
|
|
236
|
+
* Says something: one of the game's voice lines in its own voice
|
|
237
|
+
* (`context`, a `VoiceContext`), and/or a `subtitle` shown to the players
|
|
238
|
+
* who hear it (those who have subtitles on in their pause menu). Or a line
|
|
239
|
+
* of the game's mission dialogue (`dialogue`, its GXT key such as
|
|
240
|
+
* "RYD2_AA", from mission table `table`): its recording, from this ped,
|
|
241
|
+
* with its own text as the subtitle. Heard by `to`, or everyone within
|
|
242
|
+
* 40 m who sees it. Resolves when it's done (after `ms`, the recording,
|
|
243
|
+
* or about as long as reading the subtitle takes). `effects` puts the
|
|
244
|
+
* recording through audio effects (`"phone"`...; then its lips don't move).
|
|
245
|
+
*/
|
|
246
|
+
say(line: {
|
|
247
|
+
context?: number;
|
|
248
|
+
subtitle?: string;
|
|
249
|
+
dialogue?: string;
|
|
250
|
+
table?: string;
|
|
251
|
+
ms?: number;
|
|
252
|
+
to?: readonly Player[];
|
|
253
|
+
effects?: AudioEffects;
|
|
254
|
+
/** A recording of your own instead (a client file): from this ped, `subtitle` in step with it. */
|
|
255
|
+
file?: string;
|
|
256
|
+
/** With `file`: from inside this vehicle, muffled through the glass. */
|
|
257
|
+
inside?: Vehicle;
|
|
258
|
+
}): Promise<void>;
|
|
259
|
+
/**
|
|
260
|
+
* What happens to it when hurt, for this ped alone (as
|
|
261
|
+
* `server.on("pedDamage")` for all): return `false` to refuse it, or a
|
|
262
|
+
* number for the damage instead. Called before the server's listeners.
|
|
263
|
+
* **Returns** a function that removes it.
|
|
264
|
+
*
|
|
265
|
+
* ```ts
|
|
266
|
+
* witness.onDamage((by, hit) => (hit.weapon <= 15 ? 0 : undefined)); // fists and bats don't hurt him
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
onDamage(fn: (attacker: Player | Ped, hit: Hit) => boolean | number | void): () => void;
|
|
270
|
+
/** @internal Hurt: to its own listeners first. */
|
|
271
|
+
damagedBy(attacker: Player | Ped, hit: Hit): boolean | number | undefined;
|
|
272
|
+
/**
|
|
273
|
+
* A player threatening it: aiming a gun at it within `range` m (default
|
|
274
|
+
* 8), or (unless `melee: false`) hitting it with fists or a melee weapon.
|
|
275
|
+
* For a ped that gives up, as in a holdup or an arrest. Called each time
|
|
276
|
+
* it happens (aiming: at most once a second per player). **Returns** a
|
|
277
|
+
* function that removes it.
|
|
278
|
+
*
|
|
279
|
+
* ```ts
|
|
280
|
+
* clerk.onThreatened((player) => { void clerk.handsUp(5000); });
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
onThreatened(fn: (player: Player, how: "aim" | "melee") => void, options?: {
|
|
284
|
+
range?: number;
|
|
285
|
+
melee?: boolean;
|
|
286
|
+
}): () => void;
|
|
287
|
+
/** @internal Whether anything listens for threats (the server checks aims only then). */
|
|
288
|
+
get watchesThreats(): boolean;
|
|
289
|
+
/** @internal Checks whether `player` is aiming at it (the server, a few times a second). */
|
|
290
|
+
checkAim(player: Player): void;
|
|
291
|
+
/** @internal */
|
|
292
|
+
threatenedBy(player: Player, how: "aim" | "melee"): void;
|
|
293
|
+
/** Whether it's carrying out an order. */
|
|
294
|
+
get busy(): boolean;
|
|
295
|
+
on<E extends keyof PedEvents>(event: E, listener: PedEvents[E]): this;
|
|
296
|
+
off<E extends keyof PedEvents>(event: E, listener: PedEvents[E]): this;
|
|
297
|
+
/** @internal The server's word on an order. */
|
|
298
|
+
handleOrder(orderId: number, step: number, state: "stepDone" | "done" | "failed", reason: string): void;
|
|
299
|
+
/** Where it is, as its owner last reported. */
|
|
300
|
+
get position(): Vector3;
|
|
301
|
+
/** Facing, degrees. */
|
|
302
|
+
get heading(): number;
|
|
303
|
+
get health(): number;
|
|
304
|
+
get armour(): number;
|
|
305
|
+
/** The weapon it holds (`Weapon`), 0 unarmed. */
|
|
306
|
+
get weapon(): number;
|
|
307
|
+
get dead(): boolean;
|
|
308
|
+
get world(): number;
|
|
309
|
+
/** The player whose game simulates it, or null (nobody near). */
|
|
310
|
+
get owner(): Player | null;
|
|
311
|
+
/** Part of the ambient population (the server removes it when nobody is near). */
|
|
312
|
+
get ambient(): boolean;
|
|
313
|
+
/** The vehicle it's in, or null on foot. */
|
|
314
|
+
get vehicle(): Vehicle | null;
|
|
315
|
+
/** Its seat in `vehicle` (0 driver, 1-8 passenger), or null on foot. */
|
|
316
|
+
get seat(): number | null;
|
|
317
|
+
/**
|
|
318
|
+
* Puts it in a vehicle seat (0 driver, the default) for everyone. A driver
|
|
319
|
+
* whose behaviour is "wander" drives around; it still reacts as any
|
|
320
|
+
* driver would (it gets out and runs, fights back...). Throws if the seat
|
|
321
|
+
* is taken or the vehicle is in another world.
|
|
322
|
+
*/
|
|
323
|
+
putInVehicle(vehicle: Vehicle, seat?: number): void;
|
|
324
|
+
/** Takes it out of its vehicle (beside it). */
|
|
325
|
+
removeFromVehicle(): void;
|
|
326
|
+
/**
|
|
327
|
+
* Hands it back to the game, like `MARK_CHAR_AS_NO_LONGER_NEEDED`: its
|
|
328
|
+
* order stops, everyone sees it, and the server removes it as it does the
|
|
329
|
+
* ambient population, once nobody is near (a body after it has lain a
|
|
330
|
+
* while). The Ped object stays usable until then.
|
|
331
|
+
*/
|
|
332
|
+
release(): void;
|
|
333
|
+
/** Removes it for everyone. */
|
|
334
|
+
destroy(): void;
|
|
335
|
+
}
|