@quake2ts/shared 0.0.1
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/dist/browser/index.global.js +2 -0
- package/dist/browser/index.global.js.map +1 -0
- package/dist/cjs/index.cjs +6569 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/esm/index.js +6200 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/audio/constants.d.ts +24 -0
- package/dist/types/audio/constants.d.ts.map +1 -0
- package/dist/types/bsp/collision.d.ts +201 -0
- package/dist/types/bsp/collision.d.ts.map +1 -0
- package/dist/types/bsp/contents.d.ts +72 -0
- package/dist/types/bsp/contents.d.ts.map +1 -0
- package/dist/types/bsp/spatial.d.ts +13 -0
- package/dist/types/bsp/spatial.d.ts.map +1 -0
- package/dist/types/index.d.ts +38 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/inventory-helpers.d.ts +19 -0
- package/dist/types/inventory-helpers.d.ts.map +1 -0
- package/dist/types/io/binaryStream.d.ts +38 -0
- package/dist/types/io/binaryStream.d.ts.map +1 -0
- package/dist/types/io/binaryWriter.d.ts +26 -0
- package/dist/types/io/binaryWriter.d.ts.map +1 -0
- package/dist/types/io/index.d.ts +4 -0
- package/dist/types/io/index.d.ts.map +1 -0
- package/dist/types/io/messageBuilder.d.ts +21 -0
- package/dist/types/io/messageBuilder.d.ts.map +1 -0
- package/dist/types/items/ammo.d.ts +40 -0
- package/dist/types/items/ammo.d.ts.map +1 -0
- package/dist/types/items/index.d.ts +8 -0
- package/dist/types/items/index.d.ts.map +1 -0
- package/dist/types/items/powerups.d.ts +31 -0
- package/dist/types/items/powerups.d.ts.map +1 -0
- package/dist/types/items/weaponInfo.d.ts +5 -0
- package/dist/types/items/weaponInfo.d.ts.map +1 -0
- package/dist/types/items/weapons.d.ts +27 -0
- package/dist/types/items/weapons.d.ts.map +1 -0
- package/dist/types/math/angles.d.ts +19 -0
- package/dist/types/math/angles.d.ts.map +1 -0
- package/dist/types/math/anorms.d.ts +2 -0
- package/dist/types/math/anorms.d.ts.map +1 -0
- package/dist/types/math/color.d.ts +12 -0
- package/dist/types/math/color.d.ts.map +1 -0
- package/dist/types/math/mat4.d.ts +7 -0
- package/dist/types/math/mat4.d.ts.map +1 -0
- package/dist/types/math/random.d.ts +60 -0
- package/dist/types/math/random.d.ts.map +1 -0
- package/dist/types/math/vec3.d.ts +79 -0
- package/dist/types/math/vec3.d.ts.map +1 -0
- package/dist/types/net/driver.d.ts +10 -0
- package/dist/types/net/driver.d.ts.map +1 -0
- package/dist/types/net/index.d.ts +3 -0
- package/dist/types/net/index.d.ts.map +1 -0
- package/dist/types/net/netchan.d.ts +85 -0
- package/dist/types/net/netchan.d.ts.map +1 -0
- package/dist/types/pmove/apply.d.ts +5 -0
- package/dist/types/pmove/apply.d.ts.map +1 -0
- package/dist/types/pmove/categorize.d.ts +36 -0
- package/dist/types/pmove/categorize.d.ts.map +1 -0
- package/dist/types/pmove/config.d.ts +5 -0
- package/dist/types/pmove/config.d.ts.map +1 -0
- package/dist/types/pmove/constants.d.ts +76 -0
- package/dist/types/pmove/constants.d.ts.map +1 -0
- package/dist/types/pmove/currents.d.ts +58 -0
- package/dist/types/pmove/currents.d.ts.map +1 -0
- package/dist/types/pmove/dimensions.d.ts +14 -0
- package/dist/types/pmove/dimensions.d.ts.map +1 -0
- package/dist/types/pmove/duck.d.ts +39 -0
- package/dist/types/pmove/duck.d.ts.map +1 -0
- package/dist/types/pmove/fly.d.ts +34 -0
- package/dist/types/pmove/fly.d.ts.map +1 -0
- package/dist/types/pmove/index.d.ts +18 -0
- package/dist/types/pmove/index.d.ts.map +1 -0
- package/dist/types/pmove/jump.d.ts +28 -0
- package/dist/types/pmove/jump.d.ts.map +1 -0
- package/dist/types/pmove/move.d.ts +78 -0
- package/dist/types/pmove/move.d.ts.map +1 -0
- package/dist/types/pmove/pmove.d.ts +40 -0
- package/dist/types/pmove/pmove.d.ts.map +1 -0
- package/dist/types/pmove/slide.d.ts +63 -0
- package/dist/types/pmove/slide.d.ts.map +1 -0
- package/dist/types/pmove/snap.d.ts +40 -0
- package/dist/types/pmove/snap.d.ts.map +1 -0
- package/dist/types/pmove/special.d.ts +39 -0
- package/dist/types/pmove/special.d.ts.map +1 -0
- package/dist/types/pmove/stuck.d.ts +21 -0
- package/dist/types/pmove/stuck.d.ts.map +1 -0
- package/dist/types/pmove/types.d.ts +72 -0
- package/dist/types/pmove/types.d.ts.map +1 -0
- package/dist/types/pmove/view.d.ts +19 -0
- package/dist/types/pmove/view.d.ts.map +1 -0
- package/dist/types/pmove/water.d.ts +21 -0
- package/dist/types/pmove/water.d.ts.map +1 -0
- package/dist/types/protocol/bitpack.d.ts +17 -0
- package/dist/types/protocol/bitpack.d.ts.map +1 -0
- package/dist/types/protocol/configstrings.d.ts +73 -0
- package/dist/types/protocol/configstrings.d.ts.map +1 -0
- package/dist/types/protocol/constants.d.ts +36 -0
- package/dist/types/protocol/constants.d.ts.map +1 -0
- package/dist/types/protocol/contracts.d.ts +17 -0
- package/dist/types/protocol/contracts.d.ts.map +1 -0
- package/dist/types/protocol/crc.d.ts +5 -0
- package/dist/types/protocol/crc.d.ts.map +1 -0
- package/dist/types/protocol/cvar.d.ts +15 -0
- package/dist/types/protocol/cvar.d.ts.map +1 -0
- package/dist/types/protocol/effects.d.ts +33 -0
- package/dist/types/protocol/effects.d.ts.map +1 -0
- package/dist/types/protocol/entity.d.ts +46 -0
- package/dist/types/protocol/entity.d.ts.map +1 -0
- package/dist/types/protocol/entityEvent.d.ts +13 -0
- package/dist/types/protocol/entityEvent.d.ts.map +1 -0
- package/dist/types/protocol/entityState.d.ts +26 -0
- package/dist/types/protocol/entityState.d.ts.map +1 -0
- package/dist/types/protocol/index.d.ts +19 -0
- package/dist/types/protocol/index.d.ts.map +1 -0
- package/dist/types/protocol/layout.d.ts +9 -0
- package/dist/types/protocol/layout.d.ts.map +1 -0
- package/dist/types/protocol/ops.d.ts +44 -0
- package/dist/types/protocol/ops.d.ts.map +1 -0
- package/dist/types/protocol/player-state.d.ts +40 -0
- package/dist/types/protocol/player-state.d.ts.map +1 -0
- package/dist/types/protocol/player.d.ts +28 -0
- package/dist/types/protocol/player.d.ts.map +1 -0
- package/dist/types/protocol/renderFx.d.ts +23 -0
- package/dist/types/protocol/renderFx.d.ts.map +1 -0
- package/dist/types/protocol/stats.d.ts +61 -0
- package/dist/types/protocol/stats.d.ts.map +1 -0
- package/dist/types/protocol/tempEntity.d.ts +67 -0
- package/dist/types/protocol/tempEntity.d.ts.map +1 -0
- package/dist/types/protocol/usercmd.d.ts +33 -0
- package/dist/types/protocol/usercmd.d.ts.map +1 -0
- package/dist/types/protocol/writeUserCmd.d.ts +4 -0
- package/dist/types/protocol/writeUserCmd.d.ts.map +1 -0
- package/dist/types/replay/index.d.ts +3 -0
- package/dist/types/replay/index.d.ts.map +1 -0
- package/dist/types/replay/io.d.ts +7 -0
- package/dist/types/replay/io.d.ts.map +1 -0
- package/dist/types/replay/schema.d.ts +41 -0
- package/dist/types/replay/schema.d.ts.map +1 -0
- package/dist/types/testing.d.ts +6 -0
- package/dist/types/testing.d.ts.map +1 -0
- package/package.json +43 -0
- package/src/audio/constants.ts +35 -0
- package/src/bsp/collision.ts +1075 -0
- package/src/bsp/contents.ts +108 -0
- package/src/bsp/spatial.ts +116 -0
- package/src/index.ts +37 -0
- package/src/inventory-helpers.ts +81 -0
- package/src/io/binaryStream.ts +159 -0
- package/src/io/binaryWriter.ts +146 -0
- package/src/io/index.ts +3 -0
- package/src/io/messageBuilder.ts +117 -0
- package/src/items/ammo.ts +47 -0
- package/src/items/index.ts +8 -0
- package/src/items/powerups.ts +32 -0
- package/src/items/weaponInfo.ts +45 -0
- package/src/items/weapons.ts +28 -0
- package/src/math/angles.ts +135 -0
- package/src/math/anorms.ts +165 -0
- package/src/math/color.ts +42 -0
- package/src/math/mat4.ts +58 -0
- package/src/math/random.ts +182 -0
- package/src/math/vec3.ts +379 -0
- package/src/net/driver.ts +9 -0
- package/src/net/index.ts +2 -0
- package/src/net/netchan.ts +451 -0
- package/src/pmove/apply.ts +151 -0
- package/src/pmove/categorize.ts +162 -0
- package/src/pmove/config.ts +5 -0
- package/src/pmove/constants.ts +94 -0
- package/src/pmove/currents.ts +287 -0
- package/src/pmove/dimensions.ts +40 -0
- package/src/pmove/duck.ts +154 -0
- package/src/pmove/fly.ts +197 -0
- package/src/pmove/index.ts +18 -0
- package/src/pmove/jump.ts +92 -0
- package/src/pmove/move.ts +527 -0
- package/src/pmove/pmove.ts +446 -0
- package/src/pmove/slide.ts +267 -0
- package/src/pmove/snap.ts +89 -0
- package/src/pmove/special.ts +207 -0
- package/src/pmove/stuck.ts +258 -0
- package/src/pmove/types.ts +82 -0
- package/src/pmove/view.ts +57 -0
- package/src/pmove/water.ts +56 -0
- package/src/protocol/bitpack.ts +139 -0
- package/src/protocol/configstrings.ts +104 -0
- package/src/protocol/constants.ts +40 -0
- package/src/protocol/contracts.ts +149 -0
- package/src/protocol/crc.ts +32 -0
- package/src/protocol/cvar.ts +15 -0
- package/src/protocol/effects.ts +33 -0
- package/src/protocol/entity.ts +304 -0
- package/src/protocol/entityEvent.ts +14 -0
- package/src/protocol/entityState.ts +28 -0
- package/src/protocol/index.ts +19 -0
- package/src/protocol/layout.ts +9 -0
- package/src/protocol/ops.ts +49 -0
- package/src/protocol/player-state.ts +51 -0
- package/src/protocol/player.ts +165 -0
- package/src/protocol/renderFx.ts +22 -0
- package/src/protocol/stats.ts +161 -0
- package/src/protocol/tempEntity.ts +69 -0
- package/src/protocol/usercmd.ts +63 -0
- package/src/protocol/writeUserCmd.ts +30 -0
- package/src/replay/index.ts +2 -0
- package/src/replay/io.ts +37 -0
- package/src/replay/schema.ts +42 -0
- package/src/testing.ts +200 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { BinaryWriter } from '../io/index.js';
|
|
2
|
+
import { EntityState } from './entityState.js';
|
|
3
|
+
|
|
4
|
+
// Constants matching packages/engine/src/demo/parser.ts
|
|
5
|
+
export const U_ORIGIN1 = (1 << 0);
|
|
6
|
+
export const U_ORIGIN2 = (1 << 1);
|
|
7
|
+
export const U_ANGLE2 = (1 << 2);
|
|
8
|
+
export const U_ANGLE3 = (1 << 3);
|
|
9
|
+
export const U_FRAME8 = (1 << 4);
|
|
10
|
+
export const U_EVENT = (1 << 5);
|
|
11
|
+
export const U_REMOVE = (1 << 6);
|
|
12
|
+
export const U_MOREBITS1 = (1 << 7);
|
|
13
|
+
|
|
14
|
+
export const U_NUMBER16 = (1 << 8);
|
|
15
|
+
export const U_ORIGIN3 = (1 << 9);
|
|
16
|
+
export const U_ANGLE1 = (1 << 10);
|
|
17
|
+
export const U_MODEL = (1 << 11);
|
|
18
|
+
export const U_RENDERFX8 = (1 << 12);
|
|
19
|
+
export const U_ALPHA = (1 << 13); // Rerelease: Alpha
|
|
20
|
+
export const U_EFFECTS8 = (1 << 14);
|
|
21
|
+
export const U_MOREBITS2 = (1 << 15);
|
|
22
|
+
|
|
23
|
+
export const U_SKIN8 = (1 << 16);
|
|
24
|
+
export const U_FRAME16 = (1 << 17);
|
|
25
|
+
export const U_RENDERFX16 = (1 << 18);
|
|
26
|
+
export const U_EFFECTS16 = (1 << 19);
|
|
27
|
+
export const U_MODEL2 = (1 << 20); // Rerelease
|
|
28
|
+
export const U_MODEL3 = (1 << 21); // Rerelease
|
|
29
|
+
export const U_MODEL4 = (1 << 22); // Rerelease
|
|
30
|
+
export const U_MOREBITS3 = (1 << 23);
|
|
31
|
+
|
|
32
|
+
export const U_OLDORIGIN = (1 << 24);
|
|
33
|
+
export const U_SKIN16 = (1 << 25);
|
|
34
|
+
export const U_SOUND = (1 << 26);
|
|
35
|
+
export const U_SOLID = (1 << 27);
|
|
36
|
+
export const U_SCALE = (1 << 28); // Rerelease
|
|
37
|
+
export const U_INSTANCE_BITS = (1 << 29); // Rerelease
|
|
38
|
+
export const U_LOOP_VOLUME = (1 << 30); // Rerelease
|
|
39
|
+
export const U_MOREBITS4 = 0x80000000 | 0; // Bit 31 (sign bit)
|
|
40
|
+
|
|
41
|
+
// Rerelease Extension Bits (Byte 5 - High Bits)
|
|
42
|
+
export const U_LOOP_ATTENUATION_HIGH = (1 << 0);
|
|
43
|
+
export const U_OWNER_HIGH = (1 << 1);
|
|
44
|
+
export const U_OLD_FRAME_HIGH = (1 << 2);
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// A null state for new entities, used as a baseline for comparison.
|
|
48
|
+
const NULL_STATE: EntityState = {
|
|
49
|
+
number: 0,
|
|
50
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
51
|
+
angles: { x: 0, y: 0, z: 0 },
|
|
52
|
+
modelIndex: 0,
|
|
53
|
+
frame: 0,
|
|
54
|
+
skinNum: 0,
|
|
55
|
+
effects: 0,
|
|
56
|
+
renderfx: 0,
|
|
57
|
+
solid: 0,
|
|
58
|
+
sound: 0,
|
|
59
|
+
event: 0,
|
|
60
|
+
alpha: 0,
|
|
61
|
+
scale: 0,
|
|
62
|
+
instanceBits: 0,
|
|
63
|
+
loopVolume: 0,
|
|
64
|
+
loopAttenuation: 0,
|
|
65
|
+
owner: 0,
|
|
66
|
+
oldFrame: 0,
|
|
67
|
+
modelIndex2: 0,
|
|
68
|
+
modelIndex3: 0,
|
|
69
|
+
modelIndex4: 0
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Writes the remove bit for an entity.
|
|
74
|
+
*/
|
|
75
|
+
export function writeRemoveEntity(
|
|
76
|
+
number: number,
|
|
77
|
+
writer: BinaryWriter
|
|
78
|
+
): void {
|
|
79
|
+
let bits = U_REMOVE;
|
|
80
|
+
|
|
81
|
+
if (number >= 256) {
|
|
82
|
+
bits |= U_NUMBER16;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Determine needed bytes for header (U_NUMBER16 is in bits 8-15)
|
|
86
|
+
if (bits & 0xFF00) {
|
|
87
|
+
bits |= U_MOREBITS1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Write Header
|
|
91
|
+
writer.writeByte(bits & 0xFF);
|
|
92
|
+
if (bits & U_MOREBITS1) {
|
|
93
|
+
writer.writeByte((bits >> 8) & 0xFF);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Write Number
|
|
97
|
+
if (bits & U_NUMBER16) {
|
|
98
|
+
writer.writeShort(number);
|
|
99
|
+
} else {
|
|
100
|
+
writer.writeByte(number);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Writes the delta between two entity states to a binary writer.
|
|
106
|
+
*/
|
|
107
|
+
export function writeDeltaEntity(
|
|
108
|
+
from: EntityState,
|
|
109
|
+
to: EntityState,
|
|
110
|
+
writer: BinaryWriter,
|
|
111
|
+
force: boolean,
|
|
112
|
+
newEntity: boolean
|
|
113
|
+
): void {
|
|
114
|
+
let bits = 0;
|
|
115
|
+
let bitsHigh = 0;
|
|
116
|
+
|
|
117
|
+
// If this is a new entity, use a null baseline
|
|
118
|
+
if (newEntity) {
|
|
119
|
+
from = NULL_STATE;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// --- Compare fields and build the bitmask ---
|
|
123
|
+
if (to.modelIndex !== from.modelIndex || force) {
|
|
124
|
+
bits |= U_MODEL;
|
|
125
|
+
}
|
|
126
|
+
if (to.modelIndex2 !== from.modelIndex2 || force) {
|
|
127
|
+
bits |= U_MODEL2;
|
|
128
|
+
}
|
|
129
|
+
if (to.modelIndex3 !== from.modelIndex3 || force) {
|
|
130
|
+
bits |= U_MODEL3;
|
|
131
|
+
}
|
|
132
|
+
if (to.modelIndex4 !== from.modelIndex4 || force) {
|
|
133
|
+
bits |= U_MODEL4;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (to.origin.x !== from.origin.x || force) {
|
|
137
|
+
bits |= U_ORIGIN1;
|
|
138
|
+
}
|
|
139
|
+
if (to.origin.y !== from.origin.y || force) {
|
|
140
|
+
bits |= U_ORIGIN2;
|
|
141
|
+
}
|
|
142
|
+
if (to.origin.z !== from.origin.z || force) {
|
|
143
|
+
bits |= U_ORIGIN3;
|
|
144
|
+
}
|
|
145
|
+
if (to.angles.x !== from.angles.x || force) {
|
|
146
|
+
bits |= U_ANGLE1;
|
|
147
|
+
}
|
|
148
|
+
if (to.angles.y !== from.angles.y || force) {
|
|
149
|
+
bits |= U_ANGLE2;
|
|
150
|
+
}
|
|
151
|
+
if (to.angles.z !== from.angles.z || force) {
|
|
152
|
+
bits |= U_ANGLE3;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (to.frame !== from.frame || force) {
|
|
156
|
+
if (to.frame >= 256) bits |= U_FRAME16;
|
|
157
|
+
else bits |= U_FRAME8;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (to.skinNum !== from.skinNum || force) {
|
|
161
|
+
if (to.skinNum >= 256) bits |= U_SKIN16;
|
|
162
|
+
else bits |= U_SKIN8;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (to.effects !== from.effects || force) {
|
|
166
|
+
if (to.effects >= 256) bits |= U_EFFECTS16;
|
|
167
|
+
else bits |= U_EFFECTS8;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (to.renderfx !== from.renderfx || force) {
|
|
171
|
+
if (to.renderfx >= 256) bits |= U_RENDERFX16;
|
|
172
|
+
else bits |= U_RENDERFX8;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (to.solid !== from.solid || force) {
|
|
176
|
+
bits |= U_SOLID;
|
|
177
|
+
}
|
|
178
|
+
if (to.sound !== from.sound || force) {
|
|
179
|
+
bits |= U_SOUND;
|
|
180
|
+
}
|
|
181
|
+
if (to.event !== from.event || force) {
|
|
182
|
+
bits |= U_EVENT;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Rerelease Fields
|
|
186
|
+
if ((to.alpha !== from.alpha || force) && to.alpha !== undefined) {
|
|
187
|
+
bits |= U_ALPHA;
|
|
188
|
+
}
|
|
189
|
+
if ((to.scale !== from.scale || force) && to.scale !== undefined) {
|
|
190
|
+
bits |= U_SCALE;
|
|
191
|
+
}
|
|
192
|
+
if ((to.instanceBits !== from.instanceBits || force) && to.instanceBits !== undefined) {
|
|
193
|
+
bits |= U_INSTANCE_BITS;
|
|
194
|
+
}
|
|
195
|
+
if ((to.loopVolume !== from.loopVolume || force) && to.loopVolume !== undefined) {
|
|
196
|
+
bits |= U_LOOP_VOLUME;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// High Bits Fields
|
|
200
|
+
if ((to.loopAttenuation !== from.loopAttenuation || force) && to.loopAttenuation !== undefined) {
|
|
201
|
+
bitsHigh |= U_LOOP_ATTENUATION_HIGH;
|
|
202
|
+
}
|
|
203
|
+
if ((to.owner !== from.owner || force) && to.owner !== undefined) {
|
|
204
|
+
bitsHigh |= U_OWNER_HIGH;
|
|
205
|
+
}
|
|
206
|
+
if ((to.oldFrame !== from.oldFrame || force) && to.oldFrame !== undefined) {
|
|
207
|
+
bitsHigh |= U_OLD_FRAME_HIGH;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
// Handle entity number
|
|
212
|
+
if (to.number >= 256) {
|
|
213
|
+
bits |= U_NUMBER16;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Determine needed bytes for header
|
|
217
|
+
|
|
218
|
+
// If we have high bits, we set U_MOREBITS4 on the 4th byte
|
|
219
|
+
if (bitsHigh > 0) {
|
|
220
|
+
bits |= U_MOREBITS4;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Now calculate cascading flags
|
|
224
|
+
if (bits & 0xFF000000) { // e.g. U_MOREBITS4 (bit 31) is here
|
|
225
|
+
bits |= U_MOREBITS3;
|
|
226
|
+
}
|
|
227
|
+
if (bits & 0xFFFF0000) { // e.g. U_MOREBITS3 (bit 23) is here
|
|
228
|
+
bits |= U_MOREBITS2;
|
|
229
|
+
}
|
|
230
|
+
if (bits & 0xFFFFFF00) { // e.g. U_MOREBITS2 (bit 15) is here
|
|
231
|
+
bits |= U_MOREBITS1;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Write Header
|
|
235
|
+
writer.writeByte(bits & 0xFF);
|
|
236
|
+
|
|
237
|
+
if (bits & U_MOREBITS1) {
|
|
238
|
+
writer.writeByte((bits >> 8) & 0xFF);
|
|
239
|
+
}
|
|
240
|
+
if (bits & U_MOREBITS2) {
|
|
241
|
+
writer.writeByte((bits >> 16) & 0xFF);
|
|
242
|
+
}
|
|
243
|
+
if (bits & U_MOREBITS3) {
|
|
244
|
+
writer.writeByte((bits >> 24) & 0xFF);
|
|
245
|
+
}
|
|
246
|
+
if (bits & U_MOREBITS4) {
|
|
247
|
+
writer.writeByte(bitsHigh & 0xFF);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Write Number
|
|
251
|
+
if (bits & U_NUMBER16) {
|
|
252
|
+
writer.writeShort(to.number);
|
|
253
|
+
} else {
|
|
254
|
+
writer.writeByte(to.number);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Write Fields in Order (matching NetworkMessageParser.parseDelta)
|
|
258
|
+
if (bits & U_MODEL) writer.writeByte(to.modelIndex);
|
|
259
|
+
if (bits & U_MODEL2) writer.writeByte(to.modelIndex2 ?? 0);
|
|
260
|
+
if (bits & U_MODEL3) writer.writeByte(to.modelIndex3 ?? 0);
|
|
261
|
+
if (bits & U_MODEL4) writer.writeByte(to.modelIndex4 ?? 0);
|
|
262
|
+
|
|
263
|
+
if (bits & U_FRAME8) writer.writeByte(to.frame);
|
|
264
|
+
if (bits & U_FRAME16) writer.writeShort(to.frame);
|
|
265
|
+
|
|
266
|
+
if (bits & U_SKIN8) writer.writeByte(to.skinNum);
|
|
267
|
+
if (bits & U_SKIN16) writer.writeShort(to.skinNum);
|
|
268
|
+
|
|
269
|
+
if (bits & U_EFFECTS8) writer.writeByte(to.effects);
|
|
270
|
+
if (bits & U_EFFECTS16) writer.writeShort(to.effects);
|
|
271
|
+
|
|
272
|
+
if (bits & U_RENDERFX8) writer.writeByte(to.renderfx);
|
|
273
|
+
if (bits & U_RENDERFX16) writer.writeShort(to.renderfx);
|
|
274
|
+
|
|
275
|
+
if (bits & U_ORIGIN1) writer.writeCoord(to.origin.x);
|
|
276
|
+
if (bits & U_ORIGIN2) writer.writeCoord(to.origin.y);
|
|
277
|
+
if (bits & U_ORIGIN3) writer.writeCoord(to.origin.z);
|
|
278
|
+
|
|
279
|
+
if (bits & U_ANGLE1) writer.writeAngle(to.angles.x);
|
|
280
|
+
if (bits & U_ANGLE2) writer.writeAngle(to.angles.y);
|
|
281
|
+
if (bits & U_ANGLE3) writer.writeAngle(to.angles.z);
|
|
282
|
+
|
|
283
|
+
if (bits & U_OLDORIGIN) {
|
|
284
|
+
// Not implemented in EntityState usually, skip or zero
|
|
285
|
+
// writer.writePos(to.old_origin);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (bits & U_SOUND) writer.writeByte(to.sound ?? 0);
|
|
289
|
+
|
|
290
|
+
if (bits & U_EVENT) writer.writeByte(to.event ?? 0);
|
|
291
|
+
|
|
292
|
+
if (bits & U_SOLID) writer.writeShort(to.solid);
|
|
293
|
+
|
|
294
|
+
// Rerelease Fields Writing
|
|
295
|
+
if (bits & U_ALPHA) writer.writeByte(Math.floor((to.alpha ?? 0) * 255));
|
|
296
|
+
if (bits & U_SCALE) writer.writeFloat(to.scale ?? 0);
|
|
297
|
+
if (bits & U_INSTANCE_BITS) writer.writeLong(to.instanceBits ?? 0);
|
|
298
|
+
if (bits & U_LOOP_VOLUME) writer.writeByte(Math.floor((to.loopVolume ?? 0) * 255));
|
|
299
|
+
|
|
300
|
+
// High bits fields
|
|
301
|
+
if (bitsHigh & U_LOOP_ATTENUATION_HIGH) writer.writeByte(Math.floor((to.loopAttenuation ?? 0) * 255));
|
|
302
|
+
if (bitsHigh & U_OWNER_HIGH) writer.writeShort(to.owner ?? 0);
|
|
303
|
+
if (bitsHigh & U_OLD_FRAME_HIGH) writer.writeShort(to.oldFrame ?? 0);
|
|
304
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Vec3 } from '../math/vec3.js';
|
|
2
|
+
|
|
3
|
+
export interface EntityState {
|
|
4
|
+
readonly number: number;
|
|
5
|
+
readonly origin: Vec3;
|
|
6
|
+
readonly angles: Vec3;
|
|
7
|
+
readonly oldOrigin?: Vec3; // [Paril-KEX] for lerping
|
|
8
|
+
readonly modelIndex: number;
|
|
9
|
+
readonly modelIndex2?: number; // [Paril-KEX]
|
|
10
|
+
readonly modelIndex3?: number; // [Paril-KEX]
|
|
11
|
+
readonly modelIndex4?: number; // [Paril-KEX]
|
|
12
|
+
readonly frame: number;
|
|
13
|
+
readonly skinNum: number;
|
|
14
|
+
readonly effects: number;
|
|
15
|
+
readonly renderfx: number;
|
|
16
|
+
readonly solid: number;
|
|
17
|
+
readonly sound?: number;
|
|
18
|
+
readonly event?: number;
|
|
19
|
+
|
|
20
|
+
// Rerelease fields
|
|
21
|
+
readonly alpha?: number;
|
|
22
|
+
readonly scale?: number;
|
|
23
|
+
readonly instanceBits?: number;
|
|
24
|
+
readonly loopVolume?: number;
|
|
25
|
+
readonly loopAttenuation?: number;
|
|
26
|
+
readonly owner?: number;
|
|
27
|
+
readonly oldFrame?: number;
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
export * from './configstrings.js';
|
|
3
|
+
export * from './contracts.js';
|
|
4
|
+
export * from './cvar.js';
|
|
5
|
+
export * from './usercmd.js';
|
|
6
|
+
export * from './player-state.js';
|
|
7
|
+
export * from './ops.js';
|
|
8
|
+
export * from './tempEntity.js';
|
|
9
|
+
export * from './constants.js';
|
|
10
|
+
export * from './layout.js';
|
|
11
|
+
export * from './stats.js';
|
|
12
|
+
export * from './writeUserCmd.js';
|
|
13
|
+
export * from './renderFx.js';
|
|
14
|
+
export * from './crc.js';
|
|
15
|
+
export * from './effects.js';
|
|
16
|
+
export * from './entityEvent.js';
|
|
17
|
+
export * from './entity.js';
|
|
18
|
+
export * from './entityState.js';
|
|
19
|
+
export * from './player.js';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
export enum ServerCommand {
|
|
3
|
+
bad = 0,
|
|
4
|
+
|
|
5
|
+
// these ops are known to the game dll
|
|
6
|
+
muzzleflash = 1,
|
|
7
|
+
muzzleflash2 = 2,
|
|
8
|
+
temp_entity = 3,
|
|
9
|
+
layout = 4,
|
|
10
|
+
inventory = 5,
|
|
11
|
+
|
|
12
|
+
// the rest are private to the client and server
|
|
13
|
+
nop = 6,
|
|
14
|
+
disconnect = 7,
|
|
15
|
+
reconnect = 8,
|
|
16
|
+
sound = 9, // <see code>
|
|
17
|
+
print = 10, // [byte] id [string] null terminated string
|
|
18
|
+
stufftext = 11, // [string] stuffed into client's console buffer, should be \n terminated
|
|
19
|
+
serverdata = 12, // [long] protocol ...
|
|
20
|
+
configstring = 13, // [short] [string]
|
|
21
|
+
spawnbaseline = 14,
|
|
22
|
+
centerprint = 15, // [string] to put in center of the screen
|
|
23
|
+
download = 16, // [short] size [size bytes]
|
|
24
|
+
playerinfo = 17, // variable
|
|
25
|
+
packetentities = 18, // [...]
|
|
26
|
+
deltapacketentities = 19, // [...]
|
|
27
|
+
frame = 20,
|
|
28
|
+
splitclient = 21,
|
|
29
|
+
configblast = 22,
|
|
30
|
+
spawnbaselineblast = 23,
|
|
31
|
+
level_restart = 24,
|
|
32
|
+
damage = 25,
|
|
33
|
+
locprint = 26,
|
|
34
|
+
fog = 27,
|
|
35
|
+
waitingforplayers = 28,
|
|
36
|
+
bot_chat = 29,
|
|
37
|
+
poi = 30,
|
|
38
|
+
help_path = 31,
|
|
39
|
+
muzzleflash3 = 32,
|
|
40
|
+
achievement = 33
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export enum ClientCommand {
|
|
44
|
+
bad = 0,
|
|
45
|
+
nop = 1,
|
|
46
|
+
move = 2, // [[usercmd_t]
|
|
47
|
+
userinfo = 3, // [[userinfo string]
|
|
48
|
+
stringcmd = 4 // [string] message
|
|
49
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
import { Vec3 } from '../math/vec3.js';
|
|
3
|
+
|
|
4
|
+
import { WaterLevel } from '../pmove/constants.js';
|
|
5
|
+
|
|
6
|
+
export interface DamageIndicator {
|
|
7
|
+
direction: Vec3;
|
|
8
|
+
strength: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PlayerState {
|
|
12
|
+
origin: Vec3;
|
|
13
|
+
velocity: Vec3;
|
|
14
|
+
viewAngles: Vec3;
|
|
15
|
+
onGround: boolean;
|
|
16
|
+
waterLevel: WaterLevel;
|
|
17
|
+
watertype: number; // Added for correct fluid effects
|
|
18
|
+
mins: Vec3;
|
|
19
|
+
maxs: Vec3;
|
|
20
|
+
damageAlpha: number;
|
|
21
|
+
damageIndicators: DamageIndicator[];
|
|
22
|
+
blend: [number, number, number, number]; // r, g, b, a
|
|
23
|
+
pickupIcon?: string; // Name of the icon to draw (e.g. 'w_railgun')
|
|
24
|
+
centerPrint?: string;
|
|
25
|
+
notify?: string;
|
|
26
|
+
|
|
27
|
+
// Stats array (STAT_HEALTH, STAT_AMMO, etc.)
|
|
28
|
+
stats: number[];
|
|
29
|
+
|
|
30
|
+
// View effects
|
|
31
|
+
kick_angles: Vec3;
|
|
32
|
+
kick_origin: Vec3;
|
|
33
|
+
gunoffset: Vec3;
|
|
34
|
+
gunangles: Vec3;
|
|
35
|
+
gunindex: number;
|
|
36
|
+
|
|
37
|
+
// New fields for Q2 network compatibility
|
|
38
|
+
pm_type: number;
|
|
39
|
+
pm_time: number;
|
|
40
|
+
pm_flags: number;
|
|
41
|
+
gun_frame: number;
|
|
42
|
+
rdflags: number;
|
|
43
|
+
fov: number;
|
|
44
|
+
renderfx: number;
|
|
45
|
+
|
|
46
|
+
// Rerelease fields
|
|
47
|
+
gunskin?: number;
|
|
48
|
+
gunrate?: number;
|
|
49
|
+
damage_blend?: [number, number, number, number];
|
|
50
|
+
team_id?: number;
|
|
51
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { BinaryWriter, Vec3 } from '../index.js';
|
|
2
|
+
|
|
3
|
+
export interface ProtocolPlayerState {
|
|
4
|
+
pm_type: number;
|
|
5
|
+
origin: Vec3;
|
|
6
|
+
velocity: Vec3;
|
|
7
|
+
pm_time: number;
|
|
8
|
+
pm_flags: number;
|
|
9
|
+
gravity: number;
|
|
10
|
+
delta_angles: Vec3;
|
|
11
|
+
viewoffset: Vec3;
|
|
12
|
+
viewangles: Vec3;
|
|
13
|
+
kick_angles: Vec3;
|
|
14
|
+
gun_index: number;
|
|
15
|
+
gun_frame: number;
|
|
16
|
+
gun_offset: Vec3;
|
|
17
|
+
gun_angles: Vec3;
|
|
18
|
+
blend: number[]; // [r,g,b,a]
|
|
19
|
+
fov: number;
|
|
20
|
+
rdflags: number;
|
|
21
|
+
stats: number[];
|
|
22
|
+
watertype: number;
|
|
23
|
+
|
|
24
|
+
// Optional / Extension fields if needed
|
|
25
|
+
gunskin?: number;
|
|
26
|
+
gunrate?: number;
|
|
27
|
+
damage_blend?: number[];
|
|
28
|
+
team_id?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Bitflags matching demo/parser.ts
|
|
32
|
+
const PS_M_TYPE = (1 << 0);
|
|
33
|
+
const PS_M_ORIGIN = (1 << 1);
|
|
34
|
+
const PS_M_VELOCITY = (1 << 2);
|
|
35
|
+
const PS_M_TIME = (1 << 3);
|
|
36
|
+
const PS_M_FLAGS = (1 << 4);
|
|
37
|
+
const PS_M_GRAVITY = (1 << 5);
|
|
38
|
+
const PS_M_DELTA_ANGLES = (1 << 6);
|
|
39
|
+
const PS_VIEWOFFSET = (1 << 7);
|
|
40
|
+
const PS_VIEWANGLES = (1 << 8);
|
|
41
|
+
const PS_KICKANGLES = (1 << 9);
|
|
42
|
+
const PS_BLEND = (1 << 10);
|
|
43
|
+
const PS_FOV = (1 << 11);
|
|
44
|
+
const PS_WEAPONINDEX = (1 << 12);
|
|
45
|
+
const PS_WEAPONFRAME = (1 << 13);
|
|
46
|
+
const PS_RDFLAGS = (1 << 14);
|
|
47
|
+
const PS_WATERTYPE = (1 << 15);
|
|
48
|
+
|
|
49
|
+
export function writePlayerState(writer: BinaryWriter, ps: ProtocolPlayerState): void {
|
|
50
|
+
// Determine mask
|
|
51
|
+
let mask = 0;
|
|
52
|
+
|
|
53
|
+
if (ps.pm_type !== 0) mask |= PS_M_TYPE;
|
|
54
|
+
if (ps.origin.x !== 0 || ps.origin.y !== 0 || ps.origin.z !== 0) mask |= PS_M_ORIGIN;
|
|
55
|
+
if (ps.velocity.x !== 0 || ps.velocity.y !== 0 || ps.velocity.z !== 0) mask |= PS_M_VELOCITY;
|
|
56
|
+
if (ps.pm_time !== 0) mask |= PS_M_TIME;
|
|
57
|
+
if (ps.pm_flags !== 0) mask |= PS_M_FLAGS;
|
|
58
|
+
if (ps.gravity !== 0) mask |= PS_M_GRAVITY;
|
|
59
|
+
if (ps.delta_angles.x !== 0 || ps.delta_angles.y !== 0 || ps.delta_angles.z !== 0) mask |= PS_M_DELTA_ANGLES;
|
|
60
|
+
if (ps.viewoffset.x !== 0 || ps.viewoffset.y !== 0 || ps.viewoffset.z !== 0) mask |= PS_VIEWOFFSET;
|
|
61
|
+
if (ps.viewangles.x !== 0 || ps.viewangles.y !== 0 || ps.viewangles.z !== 0) mask |= PS_VIEWANGLES;
|
|
62
|
+
if (ps.kick_angles.x !== 0 || ps.kick_angles.y !== 0 || ps.kick_angles.z !== 0) mask |= PS_KICKANGLES;
|
|
63
|
+
if (ps.gun_index !== 0) mask |= PS_WEAPONINDEX;
|
|
64
|
+
|
|
65
|
+
// Weapon frame includes offset/angles
|
|
66
|
+
if (ps.gun_frame !== 0 ||
|
|
67
|
+
ps.gun_offset.x !== 0 || ps.gun_offset.y !== 0 || ps.gun_offset.z !== 0 ||
|
|
68
|
+
ps.gun_angles.x !== 0 || ps.gun_angles.y !== 0 || ps.gun_angles.z !== 0) {
|
|
69
|
+
mask |= PS_WEAPONFRAME;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (ps.blend && (ps.blend[0] !== 0 || ps.blend[1] !== 0 || ps.blend[2] !== 0 || ps.blend[3] !== 0)) {
|
|
73
|
+
mask |= PS_BLEND;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (ps.fov !== 0) mask |= PS_FOV;
|
|
77
|
+
if (ps.rdflags !== 0) mask |= PS_RDFLAGS;
|
|
78
|
+
if (ps.watertype !== 0) mask |= PS_WATERTYPE;
|
|
79
|
+
|
|
80
|
+
// Stats mask calculation
|
|
81
|
+
let statsMask = 0;
|
|
82
|
+
// Only support first 32 stats for now
|
|
83
|
+
for (let i = 0; i < 32; i++) {
|
|
84
|
+
if (ps.stats[i] && ps.stats[i] !== 0) {
|
|
85
|
+
statsMask |= (1 << i);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Write header
|
|
90
|
+
writer.writeShort(mask);
|
|
91
|
+
|
|
92
|
+
// Write fields
|
|
93
|
+
if (mask & PS_M_TYPE) writer.writeByte(ps.pm_type);
|
|
94
|
+
|
|
95
|
+
if (mask & PS_M_ORIGIN) {
|
|
96
|
+
writer.writeShort(Math.round(ps.origin.x * 8));
|
|
97
|
+
writer.writeShort(Math.round(ps.origin.y * 8));
|
|
98
|
+
writer.writeShort(Math.round(ps.origin.z * 8));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (mask & PS_M_VELOCITY) {
|
|
102
|
+
writer.writeShort(Math.round(ps.velocity.x * 8));
|
|
103
|
+
writer.writeShort(Math.round(ps.velocity.y * 8));
|
|
104
|
+
writer.writeShort(Math.round(ps.velocity.z * 8));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (mask & PS_M_TIME) writer.writeByte(ps.pm_time);
|
|
108
|
+
if (mask & PS_M_FLAGS) writer.writeByte(ps.pm_flags);
|
|
109
|
+
if (mask & PS_M_GRAVITY) writer.writeShort(ps.gravity);
|
|
110
|
+
|
|
111
|
+
if (mask & PS_M_DELTA_ANGLES) {
|
|
112
|
+
writer.writeShort(Math.round(ps.delta_angles.x * (32768 / 180)));
|
|
113
|
+
writer.writeShort(Math.round(ps.delta_angles.y * (32768 / 180)));
|
|
114
|
+
writer.writeShort(Math.round(ps.delta_angles.z * (32768 / 180)));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (mask & PS_VIEWOFFSET) {
|
|
118
|
+
writer.writeChar(Math.round(ps.viewoffset.x * 4));
|
|
119
|
+
writer.writeChar(Math.round(ps.viewoffset.y * 4));
|
|
120
|
+
writer.writeChar(Math.round(ps.viewoffset.z * 4));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (mask & PS_VIEWANGLES) {
|
|
124
|
+
writer.writeAngle16(ps.viewangles.x);
|
|
125
|
+
writer.writeAngle16(ps.viewangles.y);
|
|
126
|
+
writer.writeAngle16(ps.viewangles.z);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (mask & PS_KICKANGLES) {
|
|
130
|
+
writer.writeChar(Math.round(ps.kick_angles.x * 4));
|
|
131
|
+
writer.writeChar(Math.round(ps.kick_angles.y * 4));
|
|
132
|
+
writer.writeChar(Math.round(ps.kick_angles.z * 4));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (mask & PS_WEAPONINDEX) writer.writeByte(ps.gun_index);
|
|
136
|
+
|
|
137
|
+
if (mask & PS_WEAPONFRAME) {
|
|
138
|
+
writer.writeByte(ps.gun_frame);
|
|
139
|
+
writer.writeChar(Math.round(ps.gun_offset.x * 4));
|
|
140
|
+
writer.writeChar(Math.round(ps.gun_offset.y * 4));
|
|
141
|
+
writer.writeChar(Math.round(ps.gun_offset.z * 4));
|
|
142
|
+
writer.writeChar(Math.round(ps.gun_angles.x * 4));
|
|
143
|
+
writer.writeChar(Math.round(ps.gun_angles.y * 4));
|
|
144
|
+
writer.writeChar(Math.round(ps.gun_angles.z * 4));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (mask & PS_BLEND) {
|
|
148
|
+
writer.writeByte(Math.round(ps.blend[0]));
|
|
149
|
+
writer.writeByte(Math.round(ps.blend[1]));
|
|
150
|
+
writer.writeByte(Math.round(ps.blend[2]));
|
|
151
|
+
writer.writeByte(Math.round(ps.blend[3]));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (mask & PS_FOV) writer.writeByte(ps.fov);
|
|
155
|
+
if (mask & PS_RDFLAGS) writer.writeByte(ps.rdflags);
|
|
156
|
+
if (mask & PS_WATERTYPE) writer.writeByte(ps.watertype);
|
|
157
|
+
|
|
158
|
+
// Write Stats
|
|
159
|
+
writer.writeLong(statsMask);
|
|
160
|
+
for (let i = 0; i < 32; i++) {
|
|
161
|
+
if (statsMask & (1 << i)) {
|
|
162
|
+
writer.writeShort(ps.stats[i]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export enum RenderFx {
|
|
2
|
+
MinLight = 1,
|
|
3
|
+
ViewerModel = 2,
|
|
4
|
+
WeaponModel = 4,
|
|
5
|
+
FullBright = 8,
|
|
6
|
+
DepthHack = 16,
|
|
7
|
+
Translucent = 32,
|
|
8
|
+
FrameLerp = 64,
|
|
9
|
+
Beam = 128,
|
|
10
|
+
CustomLight = 256,
|
|
11
|
+
Glow = 512,
|
|
12
|
+
ShellRed = 1024,
|
|
13
|
+
ShellGreen = 2048,
|
|
14
|
+
ShellBlue = 4096,
|
|
15
|
+
IrVisible = 32768,
|
|
16
|
+
ShellDouble = 65536,
|
|
17
|
+
ShellHalfDam = 131072,
|
|
18
|
+
MinLightPlus = 262144,
|
|
19
|
+
ExtraLight = 524288,
|
|
20
|
+
BeamLightning = 1048576,
|
|
21
|
+
Flashlight = 2097152, // 1 << 21
|
|
22
|
+
}
|