Add Catenary DC electrification layer, CommunityMapLocation fixes, and CI/CD
Deploy & Release / build-and-deploy (push) Successful in 1m3s
Deploy & Release / build-and-deploy (push) Successful in 1m3s
This commit is contained in:
11 files changed
+660
-20
No files matched your search
+197
-5
@@ -88,11 +88,30 @@ export let view = {
|
||||
}
|
||||
}
|
||||
|
||||
export let layers = {};
|
||||
export let layers = {
|
||||
catenary: true,
|
||||
catenaryWires: true,
|
||||
catenarySubstations: true,
|
||||
catenaryNeutrals: true,
|
||||
catenaryInterruptions: true,
|
||||
};
|
||||
|
||||
export let catenaryData = null;
|
||||
export let catenaryPolylines = [];
|
||||
export let catenarySubstations = [];
|
||||
export let catenaryNeutrals = [];
|
||||
export let catenaryInterruptions = [];
|
||||
export let catenarySigns = [];
|
||||
export let catenaryColorMode = 'Voltage';
|
||||
|
||||
export function reset(){
|
||||
railTracks = [];
|
||||
markers = [];
|
||||
catenaryPolylines = [];
|
||||
catenarySubstations = [];
|
||||
catenaryNeutrals = [];
|
||||
catenaryInterruptions = [];
|
||||
catenarySigns = [];
|
||||
}
|
||||
|
||||
export function sortTracks(){
|
||||
@@ -572,6 +591,56 @@ export function testTooltip(cursorX, cursorY){
|
||||
}
|
||||
if(finalTooltip != '') return finalTooltip;
|
||||
|
||||
if(layers.catenary && layers.catenarySubstations){
|
||||
for(const sub of catenarySubstations){
|
||||
let radius = 24 / view.scale;
|
||||
if(Math.pow(sub.x - cursorX, 2) + Math.pow(sub.z - cursorY, 2) <= radius * radius){
|
||||
return sub.tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(layers.catenary && layers.catenaryNeutrals){
|
||||
for(const n of catenaryNeutrals){
|
||||
let radius = 35 / view.scale;
|
||||
if(Math.pow(n.center.x - cursorX, 2) + Math.pow(n.center.z - cursorY, 2) <= radius * radius){
|
||||
return n.tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(layers.catenary && layers.catenaryInterruptions){
|
||||
for(const inter of catenaryInterruptions){
|
||||
let radius = 24 / view.scale;
|
||||
if(Math.pow(inter.center.x - cursorX, 2) + Math.pow(inter.center.z - cursorY, 2) <= radius * radius){
|
||||
return inter.tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(layers.catenary && layers.catenaryWires){
|
||||
let bestWireDistSq = Infinity;
|
||||
let wireTooltip = '';
|
||||
let wireBoundsPadding = 25 / view.scale;
|
||||
for(const poly of catenaryPolylines){
|
||||
let b = poly.bounds;
|
||||
if(cursorX >= b[0] - wireBoundsPadding && cursorX <= b[2] + wireBoundsPadding && cursorY >= b[1] - wireBoundsPadding && cursorY <= b[3] + wireBoundsPadding){
|
||||
let pts = poly.points;
|
||||
for(let i = 0; i < pts.length - 1; i++){
|
||||
let d2 = distToSegmentSquared(cursorX, cursorY, pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1]);
|
||||
if(d2 < bestWireDistSq){
|
||||
bestWireDistSq = d2;
|
||||
wireTooltip = poly.tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let maxWireDist = Math.min(Math.max(1.2, 12 / view.scale), 80);
|
||||
if(bestWireDistSq <= maxWireDist * maxWireDist){
|
||||
return wireTooltip;
|
||||
}
|
||||
}
|
||||
|
||||
let bestDistance = Infinity;
|
||||
let test = {};
|
||||
let boundsPadding = 40/view.scale;
|
||||
@@ -684,16 +753,139 @@ export function setTrackColorMode(mode){
|
||||
view.dirty = true;
|
||||
}
|
||||
|
||||
export function distToSegmentSquared(px, pz, x1, z1, x2, z2) {
|
||||
let dx = x2 - x1;
|
||||
let dz = z2 - z1;
|
||||
let l2 = dx * dx + dz * dz;
|
||||
if (l2 === 0) {
|
||||
let ex = px - x1;
|
||||
let ez = pz - z1;
|
||||
return ex * ex + ez * ez;
|
||||
}
|
||||
let t = ((px - x1) * dx + (pz - z1) * dz) / l2;
|
||||
t = Math.max(0, Math.min(1, t));
|
||||
let nx = x1 + t * dx;
|
||||
let nz = z1 + t * dz;
|
||||
let rx = px - nx;
|
||||
let rz = pz - nz;
|
||||
return rx * rx + rz * rz;
|
||||
}
|
||||
|
||||
export function addCatenaryData(data){
|
||||
catenaryData = data;
|
||||
catenaryPolylines = data.polylines || [];
|
||||
catenarySubstations = data.substations || [];
|
||||
catenaryNeutrals = data.neutral_sections || [];
|
||||
catenaryInterruptions = data.interruptions || [];
|
||||
catenarySigns = data.signs || [];
|
||||
|
||||
for(const sub of catenarySubstations){
|
||||
sub.tooltip = `<h1>⚡ Тяговая подстанция ${sub.id}</h1>` +
|
||||
`Номинальное напряжение: ${sub.nominal_voltage} В DC\n` +
|
||||
`Питающее напряжение: ${sub.supply_voltage} В DC\n` +
|
||||
`Максимальная мощность: ${sub.maximum_load} кВт\n` +
|
||||
`Инвертор рекуперации: ${sub.has_inverter ? 'Есть (возврат энергии в сеть)' : 'Нет'}`;
|
||||
}
|
||||
|
||||
for(const n of catenaryNeutrals){
|
||||
n.tooltip = `<h1>⛔ Токораздел (Нейтральная вставка): ${n.id}</h1>` +
|
||||
`Напряжение: 0 В (Обесточенный участок)\n` +
|
||||
`Длина вставки: ${n.total_length} м\n` +
|
||||
`Проводов: ${n.wire_count}\n` +
|
||||
`Знаки: ${(n.signs || []).map(s => s.type).join(', ')}\n` +
|
||||
`Внимание: Отключить тягу перед прохождением!`;
|
||||
}
|
||||
|
||||
for(const inter of catenaryInterruptions){
|
||||
inter.tooltip = `<h1>⤓ Прерывание контактной сети</h1>` +
|
||||
`Знаки: ${(inter.signs || []).join(', ')}\n` +
|
||||
`Внимание: Опустить токоприемники (Drop Pantographs)!`;
|
||||
}
|
||||
|
||||
for(const p of catenaryPolylines){
|
||||
let catDesc = 'Одинарная подвеска (Single 6.0м)';
|
||||
if(p.category === 'dual') catDesc = 'Двойная подвеска (Dual 6.0м, повышенная токоотдача)';
|
||||
else if(p.category === 'quad') catDesc = 'Усиленная 4-проводная (Quad 6.0м, горный перевал)';
|
||||
else if(p.category === 'trolley') catDesc = 'Трамвайная/простая подвеска (Trolley 6.0м, маневровые)';
|
||||
else if(p.category === 'side_rail') catDesc = 'Боковой 3-й контактный рельс (Side Rail 4.5м, шахты)';
|
||||
|
||||
p.tooltip = `<h1>⚡ Контактная сеть: ${p.is_neutral ? 'Токораздел (0 В)' : p.voltage + ' В DC'}</h1>` +
|
||||
`Напряжение: ${p.voltage} В DC\n` +
|
||||
`Тип подвески: ${catDesc}\n` +
|
||||
`Тяговая подстанция: ${p.substation}\n` +
|
||||
`Длина участка: ${p.length} м (${p.wire_count} звеньев)`;
|
||||
}
|
||||
|
||||
setCatenaryColorMode(catenaryColorMode);
|
||||
}
|
||||
|
||||
export function setCatenaryColorMode(mode){
|
||||
catenaryColorMode = mode;
|
||||
for(const p of catenaryPolylines){
|
||||
if(p.is_neutral){
|
||||
p.color = '#e63946';
|
||||
continue;
|
||||
}
|
||||
switch(mode){
|
||||
case 'Substation':
|
||||
p.color = Config.catenarySubstationColors[p.substation] || '#3a86ff';
|
||||
break;
|
||||
case 'Wire Type':
|
||||
p.color = Config.catenaryWireTypeColors[p.category] || '#48cae4';
|
||||
break;
|
||||
case 'Voltage':
|
||||
default:
|
||||
p.color = Config.catenaryVoltageColors[String(p.voltage)] || '#00d2ff';
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateCatenaryLegend();
|
||||
view.dirty = true;
|
||||
}
|
||||
|
||||
export function updateCatenaryLegend(){
|
||||
const elem = document.getElementById('legendCatenaryColors');
|
||||
if(!elem) return;
|
||||
elem.innerHTML = '';
|
||||
switch(catenaryColorMode){
|
||||
case 'Voltage':
|
||||
elem.innerHTML = `
|
||||
<span class="colorKey"><span style="background:${Config.catenaryVoltageColors['1500']}"></span> 1500V DC (Магистраль)</span>
|
||||
<span class="colorKey"><span style="background:${Config.catenaryVoltageColors['1000']}"></span> 1000V DC (Боковой рельс)</span>
|
||||
<span class="colorKey"><span style="background:${Config.catenaryVoltageColors['750']}"></span> 750V DC (Троллей)</span>
|
||||
<span class="colorKey"><span style="background:${Config.catenaryVoltageColors['0']}"></span> 0V (Токораздел)</span>
|
||||
`;
|
||||
break;
|
||||
case 'Substation':
|
||||
let subHtml = '';
|
||||
for(const [subId, col] of Object.entries(Config.catenarySubstationColors)){
|
||||
subHtml += `<span class="colorKey"><span style="background:${col}"></span> ${subId}</span> `;
|
||||
}
|
||||
elem.innerHTML = subHtml;
|
||||
break;
|
||||
case 'Wire Type':
|
||||
elem.innerHTML = `
|
||||
<span class="colorKey"><span style="background:${Config.catenaryWireTypeColors['single']}"></span> Single (6.0м)</span>
|
||||
<span class="colorKey"><span style="background:${Config.catenaryWireTypeColors['dual']}"></span> Dual (6.0м)</span>
|
||||
<span class="colorKey"><span style="background:${Config.catenaryWireTypeColors['quad']}"></span> Quad (Перевал 6.0м)</span>
|
||||
<span class="colorKey"><span style="background:${Config.catenaryWireTypeColors['trolley']}"></span> Trolley (6.0м)</span>
|
||||
<span class="colorKey"><span style="background:${Config.catenaryWireTypeColors['side_rail']}"></span> 3-й рельс (4.5м)</span>
|
||||
`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function setLocationUpdateRate(rate){
|
||||
locationUpdateRate = rate;
|
||||
}
|
||||
|
||||
export function connectPlayerLocation(address, status){
|
||||
if(!address.startsWith('http://')){
|
||||
address.replaceAll(/.*:\/\//g, '');
|
||||
address = 'http://' + address;
|
||||
let cleanAddress = (address || '').trim().replace(/^https?:\/\//i, '').replace(/\/+$/, '');
|
||||
cleanAddress = 'http://' + cleanAddress;
|
||||
if(!cleanAddress.endsWith('/location')){
|
||||
cleanAddress += '/location';
|
||||
}
|
||||
address += '/location';
|
||||
address = cleanAddress;
|
||||
console.log(`Connecting to game at ${address}`);
|
||||
let started = false;
|
||||
const endConnect = () => {
|
||||
|
||||
Reference in new issue
Block a user