quake2ts 0.0.467 → 0.0.469

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.
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  InputBindings: () => InputBindings,
30
30
  InputCommandBuffer: () => InputCommandBuffer,
31
31
  InputController: () => InputController,
32
+ ScoreboardManager: () => ScoreboardManager,
32
33
  ViewEffects: () => import_cgame6.ViewEffects,
33
34
  createClient: () => createClient,
34
35
  createDefaultBindings: () => createDefaultBindings,
@@ -14388,6 +14389,123 @@ var updateBlend = (state, ps, dt, damageIntensity = 0) => {
14388
14389
  return [0, 0, 0, 0];
14389
14390
  };
14390
14391
 
14392
+ // src/scoreboard.ts
14393
+ var ScoreboardManager = class {
14394
+ constructor(configStrings) {
14395
+ this.localPlayerId = -1;
14396
+ this.listeners = [];
14397
+ // Cache players to support stateful updates (e.g. scores)
14398
+ this.players = /* @__PURE__ */ new Map();
14399
+ this.configStrings = configStrings;
14400
+ this.refreshAll();
14401
+ }
14402
+ setLocalPlayerId(id) {
14403
+ this.localPlayerId = id;
14404
+ }
14405
+ addListener(listener) {
14406
+ this.listeners.push(listener);
14407
+ }
14408
+ removeListener(listener) {
14409
+ const index = this.listeners.indexOf(listener);
14410
+ if (index !== -1) {
14411
+ this.listeners.splice(index, 1);
14412
+ }
14413
+ }
14414
+ notifyUpdate() {
14415
+ const data = this.getScoreboard();
14416
+ for (const listener of this.listeners) {
14417
+ listener(data);
14418
+ }
14419
+ }
14420
+ refreshAll() {
14421
+ this.players.clear();
14422
+ for (let i = 0; i < MAX_CLIENTS2; i++) {
14423
+ const csIndex = ConfigStringIndex2.PlayerSkins + i;
14424
+ const str3 = this.configStrings.get(csIndex);
14425
+ if (str3 && str3.length > 0) {
14426
+ this.parseConfigString(csIndex, str3);
14427
+ }
14428
+ }
14429
+ }
14430
+ parseConfigString(index, str3) {
14431
+ if (index < ConfigStringIndex2.PlayerSkins || index >= ConfigStringIndex2.PlayerSkins + MAX_CLIENTS2) {
14432
+ return;
14433
+ }
14434
+ const id = index - ConfigStringIndex2.PlayerSkins;
14435
+ if (!str3 || str3.length === 0) {
14436
+ if (this.players.has(id)) {
14437
+ this.players.delete(id);
14438
+ }
14439
+ return;
14440
+ }
14441
+ const entry = this.parsePlayerConfigString(id, str3);
14442
+ if (entry) {
14443
+ const existing = this.players.get(id);
14444
+ if (existing) {
14445
+ entry.frags = existing.frags;
14446
+ entry.deaths = existing.deaths;
14447
+ entry.ping = existing.ping;
14448
+ }
14449
+ this.players.set(id, entry);
14450
+ }
14451
+ }
14452
+ getScoreboard() {
14453
+ const players = Array.from(this.players.values());
14454
+ players.sort((a, b) => b.frags - a.frags);
14455
+ const worldModel = this.configStrings.getModelName(1);
14456
+ let mapName = "unknown";
14457
+ if (worldModel) {
14458
+ const match = worldModel.match(/maps\/(.*)\.bsp/i);
14459
+ if (match) {
14460
+ mapName = match[1];
14461
+ } else {
14462
+ mapName = worldModel;
14463
+ }
14464
+ }
14465
+ return {
14466
+ players,
14467
+ mapName
14468
+ };
14469
+ }
14470
+ parsePlayerConfigString(id, str3) {
14471
+ const parts = str3.split("\\");
14472
+ const info = {};
14473
+ let startIndex = 0;
14474
+ if (parts.length > 0 && parts[0] === "") {
14475
+ startIndex = 1;
14476
+ }
14477
+ for (let i = startIndex; i < parts.length; i += 2) {
14478
+ if (i + 1 < parts.length) {
14479
+ const key = parts[i].toLowerCase();
14480
+ const value = parts[i + 1];
14481
+ info[key] = value;
14482
+ }
14483
+ }
14484
+ if (!info.name) {
14485
+ return null;
14486
+ }
14487
+ return {
14488
+ id,
14489
+ name: info.name,
14490
+ frags: 0,
14491
+ deaths: 0,
14492
+ ping: 0,
14493
+ skin: info.skin,
14494
+ model: info.model
14495
+ };
14496
+ }
14497
+ // Method to update scores explicitly
14498
+ updateScore(id, frags, deaths, ping) {
14499
+ const player = this.players.get(id);
14500
+ if (player) {
14501
+ player.frags = frags;
14502
+ player.deaths = deaths;
14503
+ player.ping = ping;
14504
+ this.notifyUpdate();
14505
+ }
14506
+ }
14507
+ };
14508
+
14391
14509
  // src/input/bindings.ts
14392
14510
  var DEFAULT_BINDINGS = [
14393
14511
  { code: "KeyW", command: "+forward" },
@@ -14915,6 +15033,7 @@ var GameSession = class {
14915
15033
  this.currentMapName = "";
14916
15034
  this.options = options;
14917
15035
  this.engine = options.engine;
15036
+ this.inputController = new InputController();
14918
15037
  }
14919
15038
  startNewGame(mapName, skill = 1) {
14920
15039
  if (this.host) {
@@ -15005,9 +15124,16 @@ var GameSession = class {
15005
15124
  this.host = new EngineHost(this.game, clientProxy);
15006
15125
  const clientImports = {
15007
15126
  engine: this.engine,
15008
- host: this.host
15127
+ host: this.host,
15128
+ inputController: this.inputController
15009
15129
  };
15010
15130
  this.client = createClient(clientImports);
15131
+ if (this._onInputCommand) {
15132
+ this.client.onInputCommand = this._onInputCommand;
15133
+ }
15134
+ if (this._onHudUpdate) {
15135
+ this.client.onHudUpdate = this._onHudUpdate;
15136
+ }
15011
15137
  this.game.spawnWorld();
15012
15138
  this.host.start();
15013
15139
  if (this.engine.cmd) {
@@ -15101,9 +15227,16 @@ var GameSession = class {
15101
15227
  this.host = new EngineHost(this.game, clientProxy);
15102
15228
  const clientImports = {
15103
15229
  engine: this.engine,
15104
- host: this.host
15230
+ host: this.host,
15231
+ inputController: this.inputController
15105
15232
  };
15106
15233
  this.client = createClient(clientImports);
15234
+ if (this._onInputCommand) {
15235
+ this.client.onInputCommand = this._onInputCommand;
15236
+ }
15237
+ if (this._onHudUpdate) {
15238
+ this.client.onHudUpdate = this._onHudUpdate;
15239
+ }
15107
15240
  if (this.engine.cmd) {
15108
15241
  this.engine.cmd.executeText(`map ${mapName}`);
15109
15242
  } else if (this.host.commands) {
@@ -15134,6 +15267,43 @@ var GameSession = class {
15134
15267
  getHost() {
15135
15268
  return this.host;
15136
15269
  }
15270
+ bindInputSource(source) {
15271
+ this.inputController.bindInputSource(source);
15272
+ }
15273
+ setKeyBinding(action, keys) {
15274
+ this.inputController.setKeyBinding(action, keys);
15275
+ }
15276
+ getDefaultBindings() {
15277
+ return this.inputController.getDefaultBindings();
15278
+ }
15279
+ set onInputCommand(handler) {
15280
+ this._onInputCommand = handler;
15281
+ if (this.client) {
15282
+ this.client.onInputCommand = handler;
15283
+ }
15284
+ }
15285
+ get onInputCommand() {
15286
+ return this._onInputCommand;
15287
+ }
15288
+ // Section 4.2: HUD and UI Integration
15289
+ getHudData() {
15290
+ return this.client?.getHudData() ?? null;
15291
+ }
15292
+ getStatusBar() {
15293
+ return this.client?.getStatusBar() ?? null;
15294
+ }
15295
+ getCrosshairInfo() {
15296
+ return this.client?.getCrosshairInfo() ?? null;
15297
+ }
15298
+ set onHudUpdate(handler) {
15299
+ this._onHudUpdate = handler;
15300
+ if (this.client) {
15301
+ this.client.onHudUpdate = handler;
15302
+ }
15303
+ }
15304
+ get onHudUpdate() {
15305
+ return this._onHudUpdate;
15306
+ }
15137
15307
  // Section 4.1.3: Game State Queries
15138
15308
  getPlayerState() {
15139
15309
  if (this.client && this.client.lastRendered) {
@@ -15236,6 +15406,12 @@ function createClient(imports) {
15236
15406
  freeCameraAngles: { x: 0, y: 0, z: 0 },
15237
15407
  followEntityId: -1
15238
15408
  };
15409
+ const inputController = imports.inputController ?? new InputController();
15410
+ inputController.onInputCommand = (cmd) => {
15411
+ if (clientExports.onInputCommand) {
15412
+ clientExports.onInputCommand(cmd);
15413
+ }
15414
+ };
15239
15415
  const inputState = {
15240
15416
  forward: false,
15241
15417
  backward: false,
@@ -15252,6 +15428,7 @@ function createClient(imports) {
15252
15428
  let pauseMenuFactory;
15253
15429
  let optionsFactory;
15254
15430
  const configStrings = new ClientConfigStrings();
15431
+ const scoreboardManager = new ScoreboardManager(configStrings);
15255
15432
  const blendState = createBlendState();
15256
15433
  let currentBlend = [0, 0, 0, 0];
15257
15434
  let pendingDamage = 0;
@@ -15306,6 +15483,10 @@ function createClient(imports) {
15306
15483
  onConfigString: (index, str3) => {
15307
15484
  configStrings.set(index, str3);
15308
15485
  cg.ParseConfigString(index, str3);
15486
+ if (index >= ConfigStringIndex2.PlayerSkins && index < ConfigStringIndex2.PlayerSkins + MAX_CLIENTS2) {
15487
+ scoreboardManager.parseConfigString(index, str3);
15488
+ scoreboardManager.notifyUpdate();
15489
+ }
15309
15490
  },
15310
15491
  onServerData: (protocol, tickRate) => {
15311
15492
  if (tickRate && tickRate > 0) {
@@ -15541,6 +15722,15 @@ function createClient(imports) {
15541
15722
  menuSystem.pushMenu(pauseMenuFactory.createPauseMenu());
15542
15723
  }
15543
15724
  },
15725
+ bindInputSource(source) {
15726
+ inputController.bindInputSource(source);
15727
+ },
15728
+ setKeyBinding(action, keys) {
15729
+ inputController.setKeyBinding(action, keys);
15730
+ },
15731
+ getDefaultBindings() {
15732
+ return inputController.getDefaultBindings();
15733
+ },
15544
15734
  createMainMenu(options, storage, saveCallback, loadCallback, deleteCallback) {
15545
15735
  const saveLoadFactory = new SaveLoadMenuFactory(menuSystem, storage, saveCallback, loadCallback, deleteCallback);
15546
15736
  let optsFactory = optionsFactory;
@@ -15708,7 +15898,10 @@ function createClient(imports) {
15708
15898
  } else {
15709
15899
  currentBlend = [0, 0, 0, 0];
15710
15900
  }
15711
- const command = {};
15901
+ const command = inputController.buildCommand(dtMs, now, demoHandler.latestServerFrame);
15902
+ if (!isDemoPlaying && !menuSystem.isActive()) {
15903
+ clientExports.predict(command);
15904
+ }
15712
15905
  const timeSeconds = sample.nowMs / 1e3;
15713
15906
  dlightManager.update(timeSeconds, dtMs / 1e3);
15714
15907
  const dlights = [...dlightManager.getActiveLights()];
@@ -16009,8 +16202,17 @@ function createClient(imports) {
16009
16202
  },
16010
16203
  setDemoFollowEntity(entityId) {
16011
16204
  demoCameraState.followEntityId = entityId;
16205
+ },
16206
+ // Scoreboard API
16207
+ getScoreboard() {
16208
+ return scoreboardManager.getScoreboard();
16012
16209
  }
16013
16210
  };
16211
+ scoreboardManager.addListener((data) => {
16212
+ if (clientExports.onScoreboardUpdate) {
16213
+ clientExports.onScoreboardUpdate(data);
16214
+ }
16215
+ });
16014
16216
  return clientExports;
16015
16217
  }
16016
16218
  // Annotate the CommonJS export names for ESM import in node:
@@ -16024,6 +16226,7 @@ function createClient(imports) {
16024
16226
  InputBindings,
16025
16227
  InputCommandBuffer,
16026
16228
  InputController,
16229
+ ScoreboardManager,
16027
16230
  ViewEffects,
16028
16231
  createClient,
16029
16232
  createDefaultBindings,