updated
This commit is contained in:
parent
4e0acf9e7c
commit
02fc0aa4ee
109
server.js
109
server.js
@ -101,6 +101,81 @@ function parseVec3(str) {
|
|||||||
return { x, y, z };
|
return { x, y, z };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pickVec3Any(pos) {
|
||||||
|
if (!pos) return { x: 0, y: 0, z: 0 };
|
||||||
|
if (Array.isArray(pos)) return { x: +pos[0]||0, y: +pos[1]||0, z: +pos[2]||0 };
|
||||||
|
if (typeof pos === 'string') return parseVec3(pos);
|
||||||
|
return { x: +pos.x||0, y: +pos.y||0, z: +pos.z||0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const NADE_DEFAULTS = {
|
||||||
|
smoke: { radius: 150, lifetimeMs: 18_000 },
|
||||||
|
molotov:{ radius: 120, lifetimeMs: 7_000 },
|
||||||
|
he: { radius: 40, lifetimeMs: 300 },
|
||||||
|
flash: { radius: 36, lifetimeMs: 300 },
|
||||||
|
decoy: { radius: 80, lifetimeMs: 15_000 },
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizeGrenadesFromGSI(raw, now) {
|
||||||
|
if (!raw || typeof raw !== 'object') return [];
|
||||||
|
|
||||||
|
// Dr. Weissbrot Format: buckets (smokes, infernos, hegrenades, flashes, decoys, ...),
|
||||||
|
// evtl. auch *_projectile – beides unterstützen.
|
||||||
|
const mapKind = (k) => {
|
||||||
|
const s = k.toLowerCase();
|
||||||
|
if (s.includes('smoke')) return 'smoke';
|
||||||
|
if (s.includes('inferno') || s.includes('molotov') || s.includes('incendiary')) return 'molotov';
|
||||||
|
if (s.includes('flash')) return 'flash';
|
||||||
|
if (s.includes('decoy')) return 'decoy';
|
||||||
|
if (s.includes('he')) return 'he';
|
||||||
|
return 'unknown';
|
||||||
|
};
|
||||||
|
|
||||||
|
const pushList = (acc, kind, list) => {
|
||||||
|
if (!list) return;
|
||||||
|
const arr = Array.isArray(list) ? list : Object.values(list);
|
||||||
|
let i = 0;
|
||||||
|
for (const g of arr) {
|
||||||
|
const pos = pickVec3Any(g?.position || g?.pos || g?.location || g);
|
||||||
|
const id =
|
||||||
|
String(g?.id ?? g?.entityid ?? g?.entindex ??
|
||||||
|
`${kind}#${Math.round(pos.x)}:${Math.round(pos.y)}:${i++}`);
|
||||||
|
|
||||||
|
const ownerTeam = (g?.owner?.team || g?.team || '').toString().toUpperCase();
|
||||||
|
const team = ownerTeam === 'T' || ownerTeam === 'CT' ? ownerTeam : null;
|
||||||
|
|
||||||
|
const def = NADE_DEFAULTS[kind] || { radius: 60, lifetimeMs: 2_000 };
|
||||||
|
const bornAt =
|
||||||
|
Number.isFinite(+g?.lifetime) ? (now - Math.max(0, +g.lifetime * 1000))
|
||||||
|
: Number.isFinite(+g?.spawn_time) ? +g.spawn_time
|
||||||
|
: now;
|
||||||
|
|
||||||
|
// Für aktive Effekte (z.B. smoke/inferno) gibt es teils direkte "expire"-Infos.
|
||||||
|
const radius = Number.isFinite(+g?.radius) ? +g.radius : def.radius;
|
||||||
|
const expiresAt = Number.isFinite(+g?.expiresAt)
|
||||||
|
? +g.expiresAt
|
||||||
|
: bornAt + def.lifetimeMs;
|
||||||
|
|
||||||
|
acc.push({
|
||||||
|
id,
|
||||||
|
kind,
|
||||||
|
x: pos.x, y: pos.y, z: pos.z,
|
||||||
|
radius,
|
||||||
|
expiresAt,
|
||||||
|
team,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const out = [];
|
||||||
|
for (const [k, v] of Object.entries(raw)) {
|
||||||
|
// z.B. "smokes", "smokegrenade_projectile", "inferno", "hegrenade_projectile" ...
|
||||||
|
const kind = mapKind(k);
|
||||||
|
pushList(out, kind, v);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
function forwardToYawPitch(fwd) {
|
function forwardToYawPitch(fwd) {
|
||||||
const yaw = Math.atan2(fwd.y || 0, fwd.x || 0) * 180 / Math.PI;
|
const yaw = Math.atan2(fwd.y || 0, fwd.x || 0) * 180 / Math.PI;
|
||||||
const z = Math.max(-1, Math.min(1, fwd.z || 0));
|
const z = Math.max(-1, Math.min(1, fwd.z || 0));
|
||||||
@ -141,13 +216,19 @@ function normalizeBombFromGSI(body) {
|
|||||||
: { x: Number(b?.x) || 0, y: Number(b?.y) || 0, z: Number(b?.z) || 0 };
|
: { x: Number(b?.x) || 0, y: Number(b?.y) || 0, z: Number(b?.z) || 0 };
|
||||||
|
|
||||||
let raw = String(b?.state || b?.status || "").toLowerCase();
|
let raw = String(b?.state || b?.status || "").toLowerCase();
|
||||||
let status = "unknown"; // 'planted'|'dropped'|'carried'|'unknown'
|
let status = "unknown";
|
||||||
if (raw.includes("plant")) status = "planted";
|
|
||||||
else if (raw.includes("drop")) status = "dropped";
|
if (raw.includes("planted")) status = "planted";
|
||||||
else if (raw.includes("carry")) status = "carried";
|
else if (raw.includes("defusing")) status = "defusing";
|
||||||
|
else if (raw.includes("defused")) status = "defused";
|
||||||
|
else if (raw.includes("dropped")) status = "dropped";
|
||||||
|
else if (raw.includes("carried")) status = "carried";
|
||||||
|
// "planting" bewusst NICHT als planted behandeln
|
||||||
|
|
||||||
// bool-Fallbacks
|
// bool-Fallbacks
|
||||||
if (b?.planted === true) status = "planted";
|
if (b?.planted === true) status = "planted";
|
||||||
|
if (b?.defusing === true) status = "defusing";
|
||||||
|
if (b?.defused === true) status = "defused";
|
||||||
if (b?.dropped === true) status = "dropped";
|
if (b?.dropped === true) status = "dropped";
|
||||||
if (b?.carried === true) status = "carried";
|
if (b?.carried === true) status = "carried";
|
||||||
|
|
||||||
@ -155,6 +236,7 @@ function normalizeBombFromGSI(body) {
|
|||||||
return { x: pos.x, y: pos.y, z: pos.z, status };
|
return { x: pos.x, y: pos.y, z: pos.z, status };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function sameBomb(a, b) {
|
function sameBomb(a, b) {
|
||||||
if (!a && !b) return true;
|
if (!a && !b) return true;
|
||||||
if (!a || !b) return false;
|
if (!a || !b) return false;
|
||||||
@ -253,17 +335,29 @@ app.post(GSI_PATH, (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4) Grenades roh (du kannst bei Bedarf normalisieren)
|
// 4) Grenades roh (du kannst bei Bedarf normalisieren)
|
||||||
const grenades = body?.grenades || {};
|
const grenades = normalizeGrenadesFromGSI(body?.grenades || {}, Date.now());
|
||||||
|
|
||||||
// 5) Bombe normalisieren + Events bei Statuswechsel
|
// 5) Bombe normalisieren + Events bei Statuswechsel
|
||||||
const bomb = normalizeBombFromGSI(body);
|
const bomb = normalizeBombFromGSI(body);
|
||||||
if (!sameBomb(bomb, lastBomb)) {
|
if (!sameBomb(bomb, lastBomb)) {
|
||||||
if (bomb) {
|
if (bomb) {
|
||||||
if (bomb.status === "planted" && (!lastBomb || lastBomb.status !== "planted")) {
|
const prev = lastBomb;
|
||||||
|
// echte Plant-Transition
|
||||||
|
if (bomb.status === "planted" && (!prev || prev.status !== "planted")) {
|
||||||
broadcast({ type: "bomb_planted", bomb });
|
broadcast({ type: "bomb_planted", bomb });
|
||||||
|
// Defuse begonnen
|
||||||
|
} else if (bomb.status === "defusing" && (!prev || prev.status !== "defusing")) {
|
||||||
|
broadcast({ type: "bomb_begindefuse", bomb });
|
||||||
|
// Defuse abgebrochen (zurück zu planted)
|
||||||
|
} else if (prev && prev.status === "defusing" && bomb.status === "planted") {
|
||||||
|
broadcast({ type: "bomb_abortdefuse", bomb });
|
||||||
|
// Erfolgreich entschärft
|
||||||
|
} else if (bomb.status === "defused" && (!prev || prev.status !== "defused")) {
|
||||||
|
broadcast({ type: "bomb_defused", bomb });
|
||||||
|
// Drop / Pickup
|
||||||
} else if (bomb.status === "dropped") {
|
} else if (bomb.status === "dropped") {
|
||||||
broadcast({ type: "bomb_dropped", bomb });
|
broadcast({ type: "bomb_dropped", bomb });
|
||||||
} else if (bomb.status === "carried" && lastBomb && lastBomb.status === "dropped") {
|
} else if (bomb.status === "carried" && prev && prev.status === "dropped") {
|
||||||
broadcast({ type: "bomb_pickup", bomb });
|
broadcast({ type: "bomb_pickup", bomb });
|
||||||
} else {
|
} else {
|
||||||
broadcast({ type: "bomb", bomb });
|
broadcast({ type: "bomb", bomb });
|
||||||
@ -274,6 +368,7 @@ app.post(GSI_PATH, (req, res) => {
|
|||||||
lastBomb = bomb || null;
|
lastBomb = bomb || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 6) Tick mit kompletter Momentaufnahme (inkl. bomb)
|
// 6) Tick mit kompletter Momentaufnahme (inkl. bomb)
|
||||||
broadcast({
|
broadcast({
|
||||||
type: "tick",
|
type: "tick",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user