@sagl/server 0.0.0-stage → 0.1.0-next.51

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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -2
  3. package/dist/actions.d.ts +46 -0
  4. package/dist/actions.js +80 -0
  5. package/dist/actions.js.map +1 -0
  6. package/dist/animations.d.ts +32 -0
  7. package/dist/animations.js +95 -0
  8. package/dist/animations.js.map +1 -0
  9. package/dist/audio.d.ts +180 -0
  10. package/dist/audio.js +353 -0
  11. package/dist/audio.js.map +1 -0
  12. package/dist/audio_effects.d.ts +219 -0
  13. package/dist/audio_effects.js +84 -0
  14. package/dist/audio_effects.js.map +1 -0
  15. package/dist/camera.d.ts +188 -0
  16. package/dist/camera.js +247 -0
  17. package/dist/camera.js.map +1 -0
  18. package/dist/claims.d.ts +27 -0
  19. package/dist/claims.js +48 -0
  20. package/dist/claims.js.map +1 -0
  21. package/dist/commands.d.ts +135 -0
  22. package/dist/commands.js +373 -0
  23. package/dist/commands.js.map +1 -0
  24. package/dist/console.d.ts +21 -0
  25. package/dist/console.js +100 -0
  26. package/dist/console.js.map +1 -0
  27. package/dist/constants.d.ts +654 -0
  28. package/dist/constants.js +657 -0
  29. package/dist/constants.js.map +1 -0
  30. package/dist/cutscene.d.ts +47 -0
  31. package/dist/cutscene.js +190 -0
  32. package/dist/cutscene.js.map +1 -0
  33. package/dist/draw.d.ts +106 -0
  34. package/dist/draw.js +132 -0
  35. package/dist/draw.js.map +1 -0
  36. package/dist/effects.d.ts +91 -0
  37. package/dist/effects.js +116 -0
  38. package/dist/effects.js.map +1 -0
  39. package/dist/gangzones.d.ts +47 -0
  40. package/dist/gangzones.js +122 -0
  41. package/dist/gangzones.js.map +1 -0
  42. package/dist/geo.d.ts +11 -0
  43. package/dist/geo.js +25 -0
  44. package/dist/geo.js.map +1 -0
  45. package/dist/hud.d.ts +159 -0
  46. package/dist/hud.js +247 -0
  47. package/dist/hud.js.map +1 -0
  48. package/dist/index.d.ts +40 -0
  49. package/dist/index.js +43 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/interaction.d.ts +81 -0
  52. package/dist/interaction.js +143 -0
  53. package/dist/interaction.js.map +1 -0
  54. package/dist/junctions.d.ts +1 -0
  55. package/dist/junctions.js +330 -0
  56. package/dist/junctions.js.map +1 -0
  57. package/dist/map.d.ts +55 -0
  58. package/dist/map.js +71 -0
  59. package/dist/map.js.map +1 -0
  60. package/dist/menu.d.ts +96 -0
  61. package/dist/menu.js +129 -0
  62. package/dist/menu.js.map +1 -0
  63. package/dist/mission.d.ts +482 -0
  64. package/dist/mission.js +1258 -0
  65. package/dist/mission.js.map +1 -0
  66. package/dist/models.d.ts +56 -0
  67. package/dist/models.js +71 -0
  68. package/dist/models.js.map +1 -0
  69. package/dist/modshop.d.ts +229 -0
  70. package/dist/modshop.js +904 -0
  71. package/dist/modshop.js.map +1 -0
  72. package/dist/modshop_data.d.ts +44 -0
  73. package/dist/modshop_data.js +287 -0
  74. package/dist/modshop_data.js.map +1 -0
  75. package/dist/native.d.ts +809 -0
  76. package/dist/native.js +27 -0
  77. package/dist/native.js.map +1 -0
  78. package/dist/objects.d.ts +249 -0
  79. package/dist/objects.js +455 -0
  80. package/dist/objects.js.map +1 -0
  81. package/dist/paint.d.ts +124 -0
  82. package/dist/paint.js +84 -0
  83. package/dist/paint.js.map +1 -0
  84. package/dist/paynspray.d.ts +44 -0
  85. package/dist/paynspray.js +177 -0
  86. package/dist/paynspray.js.map +1 -0
  87. package/dist/ped.d.ts +335 -0
  88. package/dist/ped.js +536 -0
  89. package/dist/ped.js.map +1 -0
  90. package/dist/player.d.ts +685 -0
  91. package/dist/player.js +1163 -0
  92. package/dist/player.js.map +1 -0
  93. package/dist/screeneffects.d.ts +99 -0
  94. package/dist/screeneffects.js +257 -0
  95. package/dist/screeneffects.js.map +1 -0
  96. package/dist/scripts.d.ts +69 -0
  97. package/dist/scripts.js +125 -0
  98. package/dist/scripts.js.map +1 -0
  99. package/dist/server.d.ts +713 -0
  100. package/dist/server.js +1080 -0
  101. package/dist/server.js.map +1 -0
  102. package/dist/shopblips.d.ts +9 -0
  103. package/dist/shopblips.js +30 -0
  104. package/dist/shopblips.js.map +1 -0
  105. package/dist/special.d.ts +33 -0
  106. package/dist/special.js +26 -0
  107. package/dist/special.js.map +1 -0
  108. package/dist/splash.d.ts +42 -0
  109. package/dist/splash.js +55 -0
  110. package/dist/splash.js.map +1 -0
  111. package/dist/text.d.ts +19 -0
  112. package/dist/text.js +25 -0
  113. package/dist/text.js.map +1 -0
  114. package/dist/ui.d.ts +141 -0
  115. package/dist/ui.js +243 -0
  116. package/dist/ui.js.map +1 -0
  117. package/dist/vehicle.d.ts +306 -0
  118. package/dist/vehicle.js +612 -0
  119. package/dist/vehicle.js.map +1 -0
  120. package/dist/vehiclecolors.d.ts +26 -0
  121. package/dist/vehiclecolors.js +49 -0
  122. package/dist/vehiclecolors.js.map +1 -0
  123. package/dist/world.d.ts +222 -0
  124. package/dist/world.js +227 -0
  125. package/dist/world.js.map +1 -0
  126. package/dist/zone.d.ts +89 -0
  127. package/dist/zone.js +140 -0
  128. package/dist/zone.js.map +1 -0
  129. 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":"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"}
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
+ }