diff --git a/.gitignore b/.gitignore
index bf02b011b..09fdf0670 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# IDE
.idea
+.vscode
# Build artifacts
/ocap-webserver
@@ -9,6 +10,7 @@
data.db
data/
maps/
+setting.json
static/data
static/images/maps
diff --git a/README.md b/README.md
index 3bbd75bfc..1926a9b0d 100644
--- a/README.md
+++ b/README.md
@@ -168,16 +168,19 @@ docker run --name ocap-web -d \
This Project is based on [Golang](https://golang.org/dl/)
### Windows
+
```bash
go build -o ocap-webserver.exe ./cmd
```
### Linux
+
```bash
go build -o ocap-webserver ./cmd
```
### Docker
+
```bash
docker build -t ocap-webserver .
```
diff --git a/setting.json b/setting.json
deleted file mode 100644
index b7088097a..000000000
--- a/setting.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "listen": "127.0.0.1:5000",
- "prefixURL": "/aar/",
- "secret": "same-secret",
- "logger": true,
- "customize": {
- "websiteURL": "",
- "websiteLogo": "",
- "disableKillCount": false
- }
-}
diff --git a/setting.json.example b/setting.json.example
new file mode 100644
index 000000000..22875a5ed
--- /dev/null
+++ b/setting.json.example
@@ -0,0 +1,25 @@
+{
+ "listen": "127.0.0.1:5000",
+ "prefixURL": "",
+ "secret": "change-me",
+ "db": "data.db",
+ "markers": "assets/markers",
+ "ammo": "assets/ammo",
+ "maps": "maps",
+ "data": "data",
+ "static": "static",
+ "logger": false,
+ "customize": {
+ "websiteURL": "",
+ "websiteLogo": "",
+ "websiteLogoSize": "32px",
+ "disableKillCount": false
+ },
+ "conversion": {
+ "enabled": false,
+ "interval": "5m",
+ "batchSize": 1,
+ "chunkSize": 300,
+ "storageEngine": "protobuf"
+ }
+}
diff --git a/static/index.html b/static/index.html
index b996016ce..b2c0ba670 100644
--- a/static/index.html
+++ b/static/index.html
@@ -8,12 +8,17 @@
+
+
+
+
+
diff --git a/static/leaflet/L.Control.Basemaps.css b/static/leaflet/L.Control.Basemaps.css
new file mode 100644
index 000000000..69921e1c3
--- /dev/null
+++ b/static/leaflet/L.Control.Basemaps.css
@@ -0,0 +1,28 @@
+.basemaps {
+ padding: 4px;
+}
+.basemaps.closed .basemap {
+ display: none;
+}
+.basemaps.closed .basemap.alt {
+ display: inline-block;
+}
+.basemaps.closed .basemap.alt h4 {
+ display: none;
+}
+
+.basemap {
+ display: inline-block; /* todo: flexbox? */
+ cursor: pointer;
+}
+.basemap.active img {
+ border-color: orange;
+ box-shadow: 2px 2px 4px #000;
+}
+.basemap img {
+ width: 64px;
+ border: 2px solid #FFF;
+ margin: 0 2px;
+ border-radius: 40px;
+ box-shadow: 0 1px 5px rgba(0,0,0,0.65)
+}
\ No newline at end of file
diff --git a/static/leaflet/L.Control.Basemaps.js b/static/leaflet/L.Control.Basemaps.js
new file mode 100644
index 000000000..8851f55dc
--- /dev/null
+++ b/static/leaflet/L.Control.Basemaps.js
@@ -0,0 +1 @@
+L.Control.Basemaps=L.Control.extend({_map:null,includes:L.Evented?L.Evented.prototype:L.Mixin.Event,options:{position:"bottomright",tileX:0,tileY:0,tileZ:0,layers:[]},basemap:null,onAdd:function(e){this._map=e;var t=L.DomUtil.create("div","basemaps leaflet-control closed");return L.DomEvent.disableClickPropagation(t),L.Browser.touch||L.DomEvent.disableScrollPropagation(t),this.options.basemaps.forEach(function(s,o){var a,i="basemap";if(0===o?(this.basemap=s,this._map.addLayer(s),i+=" active"):1===o&&(i+=" alt"),s.options.iconURL)a=s.options.iconURL;else{var l={x:this.options.tileX,y:this.options.tileY};if(a=L.Util.template(s._url,L.extend({s:s._getSubdomain(l),x:l.x,y:s.options.tms?s._globalTileRange.max.y-l.y:l.y,z:this.options.tileZ},s.options)),s instanceof L.TileLayer.WMS){s._map=e;var n=s.options.crs||e.options.crs,r=L.extend({},s.wmsParams),m=parseFloat(r.version);r[m>=1.3?"crs":"srs"]=n.code;var p=L.point(l);p.z=this.options.tileZ;var c=s._tileCoordsToBounds(p),d=n.project(c.getNorthWest()),h=n.project(c.getSouthEast()),v=(m>=1.3&&n===L.CRS.EPSG4326?[h.y,d.x,d.y,h.x]:[d.x,h.y,h.x,d.y]).join(",");a+=L.Util.getParamString(r,a,s.options.uppercase)+(s.options.uppercase?"&BBOX=":"&bbox=")+v}}var b=L.DomUtil.create("div",i,t),u=L.DomUtil.create("img",null,b);u.src=a,s.options&&s.options.label&&(u.title=s.options.label),L.DomEvent.on(b,"click",function(){if(this.options.basemaps.length>2&&L.Browser.mobile&&L.DomUtil.hasClass(t,"closed"))L.DomUtil.removeClass(t,"closed");else if(s!=this.basemap){e.removeLayer(this.basemap),e.addLayer(s),s.bringToBack(),e.fire("baselayerchange",s),this.basemap=s,L.DomUtil.removeClass(t.getElementsByClassName("basemap active")[0],"active"),L.DomUtil.addClass(b,"active");var a=(o+1)%this.options.basemaps.length;L.DomUtil.removeClass(t.getElementsByClassName("basemap alt")[0],"alt"),L.DomUtil.addClass(t.getElementsByClassName("basemap")[a],"alt"),L.DomUtil.addClass(t,"closed")}},this)},this),this.options.basemaps.length>2&&!L.Browser.mobile&&(L.DomEvent.on(t,"mouseenter",function(){L.DomUtil.removeClass(t,"closed")},this),L.DomEvent.on(t,"mouseleave",function(){L.DomUtil.addClass(t,"closed")},this)),this._container=t,this._container}}),L.control.basemaps=function(e){return new L.Control.Basemaps(e)};
\ No newline at end of file
diff --git a/static/leaflet/L.Control.Zoominfo.css b/static/leaflet/L.Control.Zoominfo.css
new file mode 100644
index 000000000..d22339432
--- /dev/null
+++ b/static/leaflet/L.Control.Zoominfo.css
@@ -0,0 +1,124 @@
+/** Slider **/
+.leaflet-control-zoominfo-wrap {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ background-color: #fff;
+ border-bottom: 1px solid #ccc;
+}
+.leaflet-control-zoominfo-body {
+ width: 2px;
+ border: solid #fff;
+ border-width: 0px 9px 0px 9px;
+ background-color: black;
+ margin: 0 auto;
+}
+
+.leaflet-control-zoominfo-body:hover {
+ cursor: pointer;
+}
+
+.leaflet-dragging .leaflet-control-zoominfo,
+.leaflet-dragging .leaflet-control-zoominfo-wrap,
+.leaflet-dragging .leaflet-control-zoominfo-body,
+.leaflet-dragging .leaflet-control-zoominfo a,
+.leaflet-dragging .leaflet-control-zoominfo a.leaflet-control-zoominfo-disabled {
+ cursor: move;
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+}
+
+/** Leaflet Zoom Styles **/
+.leaflet-container .leaflet-control-zoominfo {
+ margin-left: 10px;
+ margin-top: 10px;
+}
+.leaflet-control-zoominfo a {
+ width: 26px;
+ height: 26px;
+ text-align: center;
+ text-decoration: none;
+ color: black;
+ display: block;
+}
+.leaflet-control-zoominfo a:hover {
+ background-color: #f4f4f4;
+}
+.leaflet-control-zoominfo-in {
+ font: bold 18px 'Lucida Console', Monaco, monospace;
+}
+.leaflet-control-zoominfo-in:after{
+ content:"+"
+}
+.leaflet-control-zoominfo-out {
+ font: bold 22px 'Lucida Console', Monaco, monospace;
+}
+.leaflet-control-zoominfo-out:after{
+ content:"−"
+}
+.leaflet-control-zoominfo a.leaflet-control-zoominfo-disabled {
+ cursor: default;
+ color: #bbb;
+}
+
+/* Touch */
+.leaflet-touch .leaflet-control-zoominfo-body {
+ background-position: 10px 0px;
+}
+.leaflet-touch .leaflet-control-zoominfo a {
+ width: 30px;
+ line-height: 30px;
+}
+.leaflet-touch .leaflet-control-zoominfo a:hover {
+ width: 30px;
+ line-height: 30px;
+}
+.leaflet-touch .leaflet-control-zoominfo-in {
+ font-size: 24px;
+ line-height: 29px;
+}
+.leaflet-touch .leaflet-control-zoominfo-out {
+ font-size: 28px;
+ line-height: 30px;
+}
+.leaflet-touch .leaflet-control-zoominfo {
+ box-shadow: none;
+ border: 4px solid rgba(0,0,0,0.3);
+}
+
+.leaflet-control-zoominfo-info {
+ margin-left: -13px;
+ background-color: #fff;
+ border: none;
+ width: 22px;
+ height: 22px;
+ display: block;
+ text-align: center;
+ text-decoration: none;
+ color: black;
+ padding: 4px 4px 0px 3px;
+
+ border-radius: 4px;
+ cursor: default;
+}
+
+.leaflet-control-zoominfoinfo h4 {
+ margin: 0 0 0px 0px;
+ color: #000;
+}
+
+/* Old IE */
+
+.leaflet-oldie .leaflet-control-zoominfo-wrap {
+ width: 26px;
+}
+
+.leaflet-oldie .leaflet-control-zoominfo {
+ border: 1px solid #999;
+}
+
+.leaflet-oldie .leaflet-control-zoominfo-in {
+ *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '+');
+}
+.leaflet-oldie .leaflet-control-zoominfo-out {
+ *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '-');
+}
diff --git a/static/leaflet/L.Control.Zoominfo.js b/static/leaflet/L.Control.Zoominfo.js
new file mode 100644
index 000000000..c4f8b36ce
--- /dev/null
+++ b/static/leaflet/L.Control.Zoominfo.js
@@ -0,0 +1,138 @@
+(function (factory) {
+ // Packaging/modules magic dance
+ var L;
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(['leaflet'], factory);
+ } else if (typeof module !== 'undefined') {
+ // Node/CommonJS
+ L = require('leaflet');
+ module.exports = factory(L);
+ } else {
+ // Browser globals
+ if (typeof window.L === 'undefined') {
+ throw new Error('Leaflet must be loaded first');
+ }
+ factory(window.L);
+ }
+}(function (L) {
+ 'use strict';
+
+ L.Control.Zoominfo = (function () {
+
+ var Zoominfo = L.Control.extend({
+ options: {
+ position: 'topleft',
+ styleNS: 'leaflet-control-zoominfo'
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+ this._ui = this._createUI();
+
+ map.whenReady(this._initInfo, this)
+ .whenReady(this._initEvents, this)
+ .whenReady(this._updateInfoValue, this)
+ .whenReady(this._updateDisabled, this);
+ return this._ui.bar;
+ },
+
+ onRemove: function (map) {
+ map.off('zoomlevelschange', this._updateSize, this)
+ .off('zoomend zoomlevelschange', this._updateInfoValue, this)
+ .off('zoomend zoomlevelschange', this._updateDisabled, this);
+ },
+
+ _createUI: function () {
+ var ui = {},
+ ns = this.options.styleNS;
+
+ ui.bar = L.DomUtil.create('div', ns + ' leaflet-bar');
+ ui.zoomIn = this._createZoomBtn('in', 'top', ui.bar);
+ ui.wrap = L.DomUtil.create('div', ns + '-wrap leaflet-bar-part', ui.bar);
+ ui.zoomOut = this._createZoomBtn('out', 'bottom', ui.bar);
+ ui.body = L.DomUtil.create('div', ns + '-body', ui.wrap);
+ ui.info = L.DomUtil.create('div', ns + '-info');
+
+ L.DomEvent.disableClickPropagation(ui.bar);
+ L.DomEvent.disableClickPropagation(ui.info);
+
+ return ui;
+ },
+ _createZoomBtn: function (zoomDir, end, container) {
+ var classDef = this.options.styleNS + '-' + zoomDir +
+ ' leaflet-bar-part' +
+ ' leaflet-bar-part-' + end,
+ link = L.DomUtil.create('a', classDef, container);
+
+ link.href = '#';
+ link.title = 'Zoom ' + zoomDir;
+
+ L.DomEvent.on(link, 'click', L.DomEvent.preventDefault);
+
+ return link;
+ },
+
+ _initInfo: function () {
+ this._ui.body.appendChild(this._ui.info);
+ },
+ _initEvents: function () {
+ this._map
+ .on('zoomend zoomlevelschange', this._updateInfoValue, this)
+ .on('zoomend zoomlevelschange', this._updateDisabled, this);
+
+ L.DomEvent.on(this._ui.zoomIn, 'click', this._zoomIn, this);
+ L.DomEvent.on(this._ui.zoomOut, 'click', this._zoomOut, this);
+ },
+
+ _zoomIn: function (e) {
+ this._map.zoomIn(e.shiftKey ? 3 : 1);
+ },
+ _zoomOut: function (e) {
+ this._map.zoomOut(e.shiftKey ? 3 : 1);
+ },
+
+ _zoomLevels: function () {
+ var zoomLevels = this._map.getMaxZoom() - this._map.getMinZoom() + 1;
+ return zoomLevels < Infinity ? zoomLevels : 0;
+ },
+ _toZoomLevel: function (value) {
+ return value + this._map.getMinZoom();
+ },
+ _toValue: function (zoomLevel) {
+ return zoomLevel - this._map.getMinZoom();
+ },
+
+ _updateInfoValue: function () {
+ this._ui.info.innerHTML = ''+ this._map.getZoom() + '';
+ },
+ _updateDisabled: function () {
+ var zoomLevel = this._map.getZoom(),
+ className = this.options.styleNS + '-disabled';
+
+ L.DomUtil.removeClass(this._ui.zoomIn, className);
+ L.DomUtil.removeClass(this._ui.zoomOut, className);
+
+ if (zoomLevel === this._map.getMinZoom()) {
+ L.DomUtil.addClass(this._ui.zoomOut, className);
+ }
+ if (zoomLevel === this._map.getMaxZoom()) {
+ L.DomUtil.addClass(this._ui.zoomIn, className);
+ }
+ }
+ });
+
+ return Zoominfo;
+ })();
+
+ L.Map.addInitHook(function () {
+ if (this.options.zoominfoControl) {
+ this.zoominfoControl = new L.Control.Zoominfo();
+ this.addControl(this.zoominfoControl);
+ }
+ });
+
+ L.control.zoominfo = function (options) {
+ return new L.Control.Zoominfo(options);
+ };
+}));
diff --git a/static/scripts/localizable.js b/static/scripts/localizable.js
index f1ee71f66..e2fb389e6 100644
--- a/static/scripts/localizable.js
+++ b/static/scripts/localizable.js
@@ -113,14 +113,14 @@ let _localizable = {
},
"nickname": {
"ru": "Никнеймы игроков и название техники ",
- "en": "Player nicknames and vehicle names",
- "de": "Spieler- und Fahrzeugnamen",
+ "en": "Player, vehicle, and projectile tags",
+ "de": "Spieler-, Fahrzeug und Projektilnamen",
"cs": "Hráčské přezdívky a názvy vozidel",
"it": "Nome giocatori e veicoli"
},
"markers": {
"ru": "Маркеры",
- "en": "Markers",
+ "en": "Marker names",
"de": "Markierungen",
"cs": "Značení",
"it": "Indicatori"
@@ -260,15 +260,15 @@ let _localizable = {
},
"time_mission": {
"ru": "Время миссии",
- "en": "Mission Time Elapsed",
+ "en": "In-Game World Time",
"de": "Verstrichene Missionszeit",
"cs": "Čas mise",
"it": "Orario missione"
},
"time_system": {
"ru": "Системное время",
- "en": "System time",
- "de": "Systemzeit",
+ "en": "Server Time UTC",
+ "de": "Systemzeit (UTC)",
"cs": "Čas systému",
"it": "Orario sistema"
},
@@ -292,6 +292,20 @@ let _localizable = {
"de": " hat den Hack unterbrochen",
"cs": " přerušil hackování",
"it": " ha interrotto l'hackeraggio"
+ },
+ "version-extension": {
+ "ru": "Pасширения bерсия : ",
+ "en": "Extension version: ",
+ "de": "Erweiterungs Version: ",
+ "cs": "Verze rozšíření: ",
+ "it": "Versione estensione: "
+ },
+ "version-addon": {
+ "ru": "Aддона bерсия: ",
+ "en": "Addon version: ",
+ "de": "Addon Version: ",
+ "cs": "Verze addonu: ",
+ "it": "Versione addon: "
}
};
let localizableElement = [];
@@ -316,6 +330,8 @@ function switchLocalizable(lang) {
if (item.length != 0)
localizable(item[0], item[1], item[2], item[3]);
});
+ document.getElementById("versionInfo-extension").innerHTML += this.extensionVersion;
+ document.getElementById("versionInfo-addon").innerHTML += this.addonVersion;
}
function deleteLocalizable(elem) {
var id = elem.dataset.lbId;
diff --git a/static/scripts/ocap.entity.js b/static/scripts/ocap.entity.js
index 22313f41c..f6521d29b 100644
--- a/static/scripts/ocap.entity.js
+++ b/static/scripts/ocap.entity.js
@@ -60,9 +60,9 @@ class Entity {
this._element = el;
}
- getElement() {
- return this._element;
- }
+ // getElement() {
+ // return this._element;
+ // }
getName() {
return this._name;
@@ -77,7 +77,7 @@ class Entity {
autoPan: false,
autoClose: false,
closeButton: false,
- className: this._popupClassName
+ className: this._popupClassName,
});
popup.setContent(content);
return popup;
@@ -88,7 +88,7 @@ class Entity {
icon: this._realIcon,
rotationOrigin: this._markerRotationOrigin
});
- this._marker.addTo(map);
+ this._marker.addTo(entitiesLayerGroup);
}
// TODO: Optimise this. No need to remove marker (and recreate it later).
@@ -97,7 +97,7 @@ class Entity {
removeMarker() {
let marker = this._marker;
if (marker != null) {
- map.removeLayer(marker);
+ entitiesLayerGroup.removeLayer(marker);
this._marker = null;
this.remove();
}
@@ -136,7 +136,10 @@ class Entity {
let popup = this._marker.getPopup();
if (popup != null) {
- popup.getElement().style.opacity = opacity;
+ let element = popup.getElement()
+ if (element) {
+ element.style.opacity = opacity;
+ }
}
}
@@ -146,8 +149,13 @@ class Entity {
if (popup == null) { return }
let element = popup.getElement();
+ if (element == null) { return }
let display = "inherit";
- if (bool || !ui.nicknameEnable) { display = "none" }
+ if (this.constructor.name == 'Unit' && !this.isPlayer) {
+ display = "none"
+ } else {
+ if (bool || !ui.nicknameEnable) {display = "none"};
+ };
if (element.style.display !== display) {
element.style.display = display;
diff --git a/static/scripts/ocap.event.js b/static/scripts/ocap.event.js
index 94faa152d..f8e1b4bf3 100644
--- a/static/scripts/ocap.event.js
+++ b/static/scripts/ocap.event.js
@@ -3,18 +3,18 @@ class GameEvents {
this._events = [];
}
- addEvent(event) {
+ addEvent (event) {
this._events.push(event);
}
- init() {
+ init () {
for (const event of this._events) {
event.init();
}
}
// Return an array of events that occured on the given frame
- getEventsAtFrame(f) {
+ getEventsAtFrame (f) {
var events = [];
this._events.forEach((event) => {
if (event.frameNum == f) {
@@ -25,9 +25,9 @@ class GameEvents {
return events;
}
- getEvents() { return this._events }
+ getEvents () { return this._events }
- getActiveEvents() {
+ getActiveEvents () {
return this._events.filter((event) => event.frameNum <= playbackFrame);
}
}
@@ -44,30 +44,30 @@ class GameEvent {
this.objectID = objectID;
}
- getElement() { return this._element };
+ getElement () { return this._element };
// initialize event once all events are known
- init() {}
+ init () { }
// forces an update for the next frame
- forceUpdate() {
+ forceUpdate () {
this._forceUpdate = true;
}
// check if update() call is needed
- needsUpdate(f, onlyVisible = true) {
+ needsUpdate (f, onlyVisible = true) {
return ((!onlyVisible || f >= this.frameNum) && this.lastFrameNumUpdate !== f) || this._forceUpdate;
}
// update element
- update(f) {
+ update (f) {
this.lastFrameNumUpdate = f;
this._forceUpdate = false;
}
// update time related to frameNum
- updateTime() {}
+ updateTime () { }
// get previous event for the same objectID based given constructor
- getPreviousObjectEvent(type) {
+ getPreviousObjectEvent (type) {
const events = gameEvents.getEvents();
const thisIndex = events.indexOf(this);
if (thisIndex === -1) return null;
@@ -82,7 +82,7 @@ class GameEvent {
}
// move the camera on position
- focusOnPosition(position) {
+ focusOnPosition (position) {
entityToFollow = null;
map.setView(armaToLatLng(position), map.getZoom(), { animate: true });
}
@@ -125,10 +125,10 @@ class HitKilledEvent extends GameEvent {
causedBySpan.className = this.causedBy.getSideClass();
switch (this.type) {
case "killed":
- causedBySpan.textContent = `${this.causedBy.getName()} (${causedBy.killCount - (causedBy.teamKillCount*2)} kills)`;
+ causedBySpan.textContent = `${this.causedBy.getName()} (${causedBy.killCount - (causedBy.teamKillCount * 2)} kills)`;
break;
case "hit":
- causedBySpan.textContent = `${this.causedBy.getName()} (${causedBy.killCount - (causedBy.teamKillCount*2)} kills)`;
+ causedBySpan.textContent = `${this.causedBy.getName()} (${causedBy.killCount - (causedBy.teamKillCount * 2)} kills)`;
break;
}
} else {
@@ -178,7 +178,7 @@ class HitKilledEvent extends GameEvent {
this._element = li;
};
- updateTime() {
+ updateTime () {
this.detailsDiv.textContent = ui.getTimeString(this.frameNum) + " - " + this.distance + "m - " + this.weapon;
}
}
@@ -203,7 +203,7 @@ class ConnectEvent extends GameEvent {
this._element = li;
};
- updateTime() {
+ updateTime () {
this.detailsDiv.textContent = ui.getTimeString(this.frameNum);
}
}
@@ -263,13 +263,13 @@ class CapturedEvent extends GameEvent {
}
};
- update(f) {
+ update (f) {
if (!this.needsUpdate(f, false)) return;
const markerVisible = this.markerIsVisible(f);
if (!this._marker && markerVisible) {
const color = "#" + getPulseMarkerColor(this.unitColor);
- this._marker = L.marker.pulse(armaToLatLng(this.objectPosition), {iconSize: [50,50], color: color, fillColor: 'transparent', iterationCount: 1}).addTo(map);
+ this._marker = L.marker.pulse(armaToLatLng(this.objectPosition), { iconSize: [50, 50], color: color, fillColor: 'transparent', iterationCount: 1 }).addTo(map);
} else if (this._marker && !markerVisible) {
this._marker.remove();
this._marker = null;
@@ -278,13 +278,13 @@ class CapturedEvent extends GameEvent {
super.update(f);
}
- markerIsVisible(f) {
+ markerIsVisible (f) {
if (!this.objectPosition) return false;
return f >= this.frameNum;
}
- updateTime() {
+ updateTime () {
this.detailsDiv.textContent = ui.getTimeString(this.frameNum);
}
}
@@ -341,12 +341,12 @@ class TerminalHackStartEvent extends GameEvent {
}
};
- update(f) {
+ update (f) {
if (!this.needsUpdate(f, false)) return;
const markerVisible = this.markerIsVisible(f);
if (!this._marker && markerVisible) {
- this._marker = L.marker.pulse(armaToLatLng(this.terminalPosition), {iconSize: [50,50], color: 'red', fillColor: 'transparent'}).addTo(map);
+ this._marker = L.marker.pulse(armaToLatLng(this.terminalPosition), { iconSize: [50, 50], color: 'red', fillColor: 'transparent' }).addTo(map);
} else if (this._marker && !markerVisible) {
this._marker.remove();
this._marker = null;
@@ -370,7 +370,7 @@ class TerminalHackStartEvent extends GameEvent {
super.update(f);
}
- markerIsVisible(f) {
+ markerIsVisible (f) {
if (!this.terminalPosition) return false;
if (f < this.frameNum) return false;
if (this._state !== "running") return false;
@@ -379,12 +379,12 @@ class TerminalHackStartEvent extends GameEvent {
return secondsLeft > 0;
}
- updateTime() {
+ updateTime () {
this.detailsDiv.textContent = ui.getTimeString(this.frameNum);
}
- getState() { return this._state; }
- setState(state) {
+ getState () { return this._state; }
+ setState (state) {
this._state = state;
this.forceUpdate();
}
@@ -430,7 +430,7 @@ class TerminalHackUpdateEvent extends GameEvent {
this._element = li;
}
- init() {
+ init () {
this._parent = this.getPreviousObjectEvent(TerminalHackStartEvent);
if (this._parent && this._parent.terminalPosition) {
this._element.classList.add("action");
@@ -440,7 +440,7 @@ class TerminalHackUpdateEvent extends GameEvent {
}
}
- update(f) {
+ update (f) {
if (!this.needsUpdate(f, false)) return;
if (f >= this.frameNum && !this._triggered) {
@@ -459,11 +459,27 @@ class TerminalHackUpdateEvent extends GameEvent {
super.update(f);
}
- updateTime() {
+ updateTime () {
this.detailsDiv.textContent = ui.getTimeString(this.frameNum);
}
}
+// [20, "generalEvent", "Mission has started!"]
+class generalEvent extends GameEvent {
+ constructor(frameNum, type, msg) {
+ super(frameNum, type);
+ this.msg = msg;
+ this._element = null;
+
+ var span = document.createElement("span");
+ span.className = "medium";
+ span.textContent = msg;
+ var li = document.createElement("li");
+ li.appendChild(span)
+ this._element = li;
+ };
+};
+
// [4639, "endMission", ["EAST", "Offar Factory зазахвачена. Победа Сил РФ."]]
class endMissionEvent extends GameEvent {
constructor(frameNum, type, side, msg) {
diff --git a/static/scripts/ocap.group.js b/static/scripts/ocap.group.js
index 8eefa6816..3e49c66c4 100644
--- a/static/scripts/ocap.group.js
+++ b/static/scripts/ocap.group.js
@@ -120,6 +120,23 @@ class Group {
//liGroup.addEventListener("click", function() {console.log(group.getUnits())});
this.setElement(liGroup);
targetList.appendChild(liGroup);
+
+ var elements = [].slice.call(targetList.childNodes)
+ while (targetList.childNodes.length > 0) {
+ targetList.childNodes.forEach(a => {
+ targetList.removeChild(a)
+ })
+ };
+
+ var sortedElements = elements.sort((a, b) => {
+ if (a.innerText < b.innerText) return -1;
+ if (a.innerText > b.innerText) return 1;
+ return 0;
+ });
+
+ sortedElements.forEach(a => {
+ targetList.appendChild(a)
+ });
};
isEmpty() {
diff --git a/static/scripts/ocap.js b/static/scripts/ocap.js
index c401f27cd..d02c1face 100644
--- a/static/scripts/ocap.js
+++ b/static/scripts/ocap.js
@@ -56,8 +56,22 @@ var trim = 0; // Number of pixels that were trimmed when cropping image (used to
var mapMinZoom = null;
var mapMaxNativeZoom = null;
var mapMaxZoom = null; // mapMaxNativeZoom + 3;
+var topoLayer = null;
+var satLayer = null;
+var terrainLayer = null;
+var terrainDarkLayer = null;
+var contourLayer = null;
+var baseLayerControl = null;
+var overlayLayerControl = null;
+var entitiesLayerGroup = L.layerGroup([]);
+var markersLayerGroup = L.layerGroup([]);
+var systemMarkersLayerGroup = L.layerGroup([]);
+var projectileMarkersLayerGroup = L.layerGroup([]);
var map = null;
var mapDiv = null;
+var mapBounds = null;
+var worldObject = null;
+var mapAvailable = false;
var frameCaptureDelay = 1000; // Delay between capture of each frame in-game (ms). Default: 1000
var playbackMultiplier = 10; // Playback speed. 1 = realtime.
var maxPlaybackMultipler = 60; // Max speed user can set playback to
@@ -88,17 +102,21 @@ var followColour = "#FFA81A";
var hitColour = "#FF0000";
var deadColour = "#000000";
-const skipAnimationDistance = 100;
+const skipAnimationDistance = 222; // 800 kph at 1 sec frame delay, cruise for most planes - objects changing a larger distance than this would represent will be temporarily hidden between frames because it's assumed they're teleporting
let requestedFrame;
function getArguments () {
- let args = new Object();
- window.location.search.replace("?", "").split("&").forEach(function (s) {
- let values = s.split("=");
- if (values.length > 1) {
- args[values[0]] = values[1].replace(/%20/g, " ");
- }
- });
+ // let args = new Object();
+ // window.location.search.replace("?", "").split("&").forEach(function (s) {
+ // let values = s.split("=");
+ // if (values.length > 1) {
+ // args[values[0]] = values[1].replace(/%20/g, " ");
+ // }
+ // });
+
+ let args = new URLSearchParams(window.location.search);
+
+
// console.log(args);
return args;
}
@@ -115,85 +133,219 @@ function initOCAP () {
Promise.all([ui.updateCustomize(), ui.setModalOpList()])
.then(() => {
- /*
- window.addEventListener("keypress", function (event) {
- switch (event.charCode) {
- case 32: // Spacebar
- event.preventDefault(); // Prevent space from scrolling page on some browsers
- break;
- };
- });
- */
- if (args.file) {
+ /*
+ window.addEventListener("keypress", function (event) {
+ switch (event.charCode) {
+ case 32: // Spacebar
+ event.preventDefault(); // Prevent space from scrolling page on some browsers
+ break;
+ };
+ });
+ */
+ if (args.get('file')) {
document.addEventListener("mapInited", function (event) {
let args = getArguments();
- if (args.x && args.y && args.zoom) {
- let coords = [parseFloat(args.x), parseFloat(args.y)];
- let zoom = parseFloat(args.zoom);
+ if (args.get('x') && args.get('y') && args.get('zoom')) {
+ let coords = [parseFloat(args.get('x')), parseFloat(args.get('y'))];
+ let zoom = parseFloat(args.get('zoom'));
map.setView(coords, zoom);
- } else {
- map.setView([0, 0], mapMaxNativeZoom);
}
- if (args.frame) {
- ui.setMissionCurTime(parseInt(args.frame));
+ if (args.get('frame')) {
+ ui.setMissionCurTime(parseInt(args.get('frame')));
}
}, false);
- return loadOperationByFilename(args.file);
+
+ document.addEventListener("operationProcessed", function (event) {
+ let bounds = getMapMarkerBounds();
+ map.fitBounds(bounds);
+ });
+
+ return loadOperationByFilename(args.get('file'));
}
})
.catch((error) => {
ui.showHint(error);
});
- if (args.experimental) ui.showExperimental();
+ if (args.get('experimental')) ui.showExperimental();
}
-function getWorldByName (worldName) {
+async function getWorldByName (worldName) {
console.log("Getting world " + worldName);
- let map = {};
+
let defaultMap = {
"name": "NOT FOUND",
+ "displayName": "NOT FOUND",
"worldname": "NOT FOUND",
"worldSize": 16384,
"imageSize": 16384,
"multiplier": 1,
"maxZoom": 6,
"minZoom": 0,
+ "hasTopo": true,
+ "hasTopoRelief": false,
+ "hasTopoDark": false,
+ "hasColorRelief": false,
+ "attribution": "Bohemia Interactive and 3rd Party Developers"
};
- return fetch(`images/maps/${worldName}/map.json`)
- .then((res) => res.json())
- .then((data) => {
- map = data;
- return Object.assign(defaultMap, map);
- })
- .catch(() => {
- ui.showHint(`Error: Map "${worldName}" is not installed`);
- });
+ // Check for, and return local map data if available
+ const localMapRes = await fetch(
+ 'images/maps/' + worldName + '/map.json',
+ { cache: "no-store" }
+ );
+ if (localMapRes.status === 200) {
+ try {
+ return Object.assign(defaultMap, await localMapRes.json());
+ } catch (error) {
+ //ui.showHint(`Error: parsing local map.json`);
+ console.error('Error parsing local map.json', error.message || error);
+ }
+ }
+
+ // Fallback to cloud CDN if enabled
+ if (ui.useCloudTiles) {
+ let cloudMapRes;
+ try {
+ cloudMapRes = await fetch(
+ `https://maps.ocap2.com/${worldName}/map.json`,
+ { cache: "no-store" }
+ );
+ } catch (error) {
+ // clone default map if not found
+ Object.assign(defaultMap, {
+ "imageSize": 30720,
+ "worldSize": 30720,
+ "multiplier": 1,
+ "worldName": worldName
+ });
+ console.warn("World not found, using blank map")
+ alert(`The map for this mission (worldName: ${worldName}) is not available locally or in the cloud.\n\nA placeholder will be shown instead. Please report this issue on the OCAP2 Discord.\n\nhttps://discord.gg/wQusAQnrBP`);
+ worldName = "";
+
+ return Promise.resolve(defaultMap);
+ };
+ if (cloudMapRes.status === 200) {
+ try {
+ return Object.assign(defaultMap, await cloudMapRes.json(), { _useCloudTiles: true });
+ } catch (error) {
+ console.error('Error parsing cloud map.json', error.message || error);
+ return Promise.reject(`Cloud map "${worldName}" data parsing failed.`);
+ }
+ } else {
+ // clone default map if not found
+ Object.assign(defaultMap, {
+ "imageSize": 30720,
+ "worldSize": 30720,
+ "multiplier": 1,
+ "worldName": worldName
+ });
+ worldName = "";
+ console.warn("World not found, using blank map")
+ alert(`The map for this mission (worldName: ${worldName}) is not available locally or in the cloud.\n\nA placeholder will be shown instead. Please report this issue on the OCAP2 Discord.\n\nhttps://discord.gg/wQusAQnrBP`);
+
+ return Promise.resolve(defaultMap);
+ // return Promise.reject(`Map "${worldName}" is not available on cloud (${cloudMapRes.status})`);
+ }
+ } else {
+ return Promise.reject(`Map "${worldName}" is not installed`);
+ }
}
function initMap (world) {
// Bad
mapMaxNativeZoom = world.maxZoom
- mapMaxZoom = mapMaxNativeZoom + 3
+ mapMaxZoom = mapMaxNativeZoom + 2
+
+ imageSize = world.imageSize;
+ multiplier = world.multiplier;
+
+ var factorx = multiplier;
+ var factory = multiplier;
+ // var factorx = 1;
+ // var factory = 1;
+
+ L.CRS.OCAP = L.extend({}, L.CRS.Simple, {
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(factorx, 0, -factory, 0),
+ // Changing the transformation is the key part, everything else is the same.
+ // By specifying a factor, you specify what distance in meters one pixel occupies (as it still is CRS.Simple in all other regards).
+ // In this case, I have a tile layer with 256px pieces, so Leaflet thinks it's only 256 meters wide.
+ // I know the map is supposed to be 2048x2048 meters, so I specify a factor of 0.125 to multiply in both directions.
+ // In the actual project, I compute all that from the gdal2tiles tilemapresources.xml,
+ // which gives the necessary information about tilesizes, total bounds and units-per-pixel at different levels.
+
+
+ // Scale, zoom and distance are entirely unchanged from CRS.Simple
+ scale: function (zoom) {
+ return Math.pow(2, zoom);
+ },
+
+ zoom: function (scale) {
+ return Math.log(scale) / Math.LN2;
+ },
+
+ distance: function (latlng1, latlng2) {
+ var dx = latlng2.lng - latlng1.lng,
+ dy = latlng2.lat - latlng1.lat;
+
+ return Math.sqrt(dx * dx + dy * dy);
+ },
+ infinite: true
+ });
+
// Create map
map = L.map('map', {
- //maxZoom: mapMaxZoom,
+ center: [0, 0],
+ zoom: 0,
+ maxNativeZoom: mapMaxNativeZoom,
+ maxZoom: mapMaxZoom,
+ minNativeZoom: 0,
+ minZoom: 0,
+ // zoominfoControl: true, // moved for custom position
zoomControl: false,
- zoomAnimation: true,
scrollWheelZoom: true,
+ zoomAnimation: true,
fadeAnimation: true,
- crs: L.CRS.Simple,
- attributionControl: false,
- zoomSnap: 0.1,
+ crs: L.CRS.OCAP,
+ attributionControl: true,
+ zoomSnap: 1,
zoomDelta: 1,
closePopupOnClick: false,
- preferCanvas: false
+ preferCanvas: true
});
+
// Hide marker popups once below a certain zoom level
map.on("zoom", function () {
ui.hideMarkerPopups = map.getZoom() <= 4;
+ // if (map.getZoom() <= 5 && geoJsonHouses != null) {
+ // geoJsonHouses.setStyle(function (geoJsonFeature) {
+ // return {
+ // color: "#4D4D4D",
+ // interactive: false,
+ // fill: true,
+ // opacity: 0,
+ // fillOpacity: 0,
+ // noClip: true,
+ // // renderer: L.canvas()
+ // // weight: geoJsonFeature.properties.width * window.multiplier,
+ // };
+ // });
+ // } else if (geoJsonHouses != null) {
+ // geoJsonHouses.setStyle(function (geoJsonFeature) {
+ // return {
+ // color: "#4D4D4D",
+ // interactive: false,
+ // fill: true,
+ // opacity: 1,
+ // fillOpacity: 1,
+ // noClip: true,
+ // // renderer: L.canvas()
+ // // weight: geoJsonFeature.properties.width * window.multiplier,
+ // };
+ // });
+ // }
});
let playbackPausedBeforeZoom;
@@ -221,32 +373,256 @@ function initMap (world) {
}
});
- console.log("Got world: ", world);
- imageSize = world.imageSize;
- multiplier = world.multiplier;
- let args = getArguments();
- if (!args.x || !args.y || !args.zoom) {
- map.setView(map.unproject([imageSize / 2, imageSize / 2]), mapMinZoom);
+ // Setup tile layers
+ var baseLayers = [];
+
+ entitiesLayerGroup.addTo(map);
+ markersLayerGroup.addTo(map);
+ systemMarkersLayerGroup.addTo(map);
+ projectileMarkersLayerGroup.addTo(map);
+
+
+ // worldName = world.worldName;
+
+
+ let topoLayerUrl = "";
+ let topoDarkLayerUrl = "";
+ let topoReliefLayerUrl = "";
+ let colorReliefLayerUrl = "";
+
+
+ if (worldName === "") {
+ console.log("World name missing or not rendered. Using default map.")
+ // if default map is used as placeholder, use custom topo layer url
+ topoLayerUrl = 'https://maps.ocap2.com/missing_tiles.png';
+ } else if (Boolean(world._useCloudTiles)) {
+ console.log("Streaming map tiles from the cloud (maps.ocap2.com).")
+ topoLayerUrl = ('https://maps.ocap2.com/' + worldName.toLowerCase() + '/{z}/{x}/{y}.png');
+ topoDarkLayerUrl = ('https://maps.ocap2.com/' + worldName.toLowerCase() + '/topoDark/{z}/{x}/{y}.png');
+ topoReliefLayerUrl = ('https://maps.ocap2.com/' + worldName.toLowerCase() + '/topoRelief/{z}/{x}/{y}.png');
+ colorReliefLayerUrl = ('https://maps.ocap2.com/' + worldName.toLowerCase() + '/colorRelief/{z}/{x}/{y}.png');
+ } else {
+ console.log("Streaming map tiles from the local OCAP2 installation.")
+ topoLayerUrl = ('images/maps/' + worldName + '/{z}/{x}/{y}.png');
+ topoDarkLayerUrl = ('images/maps/' + worldName + '/topoDark/{z}/{x}/{y}.png');
+ topoReliefLayerUrl = ('images/maps/' + worldName + '/topoRelief/{z}/{x}/{y}.png');
+ colorReliefLayerUrl = ('images/maps/' + worldName + '/colorRelief/{z}/{x}/{y}.png');
}
- var mapBounds = new L.LatLngBounds(
- map.unproject([0, imageSize], mapMaxNativeZoom),
- map.unproject([imageSize, 0], mapMaxNativeZoom)
- );
- map.fitBounds(mapBounds);
+ console.log("Getting bounds for layers...")
+ mapBounds = getMapImageBounds()
+
+ if (world.hasTopo) {
+ topoLayer = L.tileLayer(topoLayerUrl, {
+ maxNativeZoom: world.maxZoom,
+ // maxZoom: mapMaxZoom,
+ minNativeZoom: world.minZoom,
+ bounds: mapBounds,
+ label: "Topographic",
+ attribution: "Map Data © " + world.attribution,
+ noWrap: true,
+ tms: false,
+ keepBuffer: 4,
+ // opacity: 0.7,
+ errorTileUrl: 'https://maps.ocap2.com/missing_tiles.png'
+ });
+ baseLayers.push(topoLayer);
+ }
- // Setup tile layer
- L.tileLayer('images/maps/' + worldName + '/{z}/{x}/{y}.png', {
- maxNativeZoom: mapMaxNativeZoom,
- maxZoom: mapMaxZoom,
- minZoom: mapMinZoom,
- bounds: mapBounds,
- //attribution: 'MisterGoodson',
- noWrap: true,
- tms: false
+ if (world.hasTopoDark) {
+ topoDarkLayer = L.tileLayer(topoDarkLayerUrl, {
+ maxNativeZoom: world.maxZoom,
+ // maxZoom: mapMaxZoom,
+ minNativeZoom: world.minZoom,
+ bounds: mapBounds,
+ label: "Topographic Dark",
+ attribution: "Map Data © " + world.attribution,
+ noWrap: true,
+ tms: false,
+ keepBuffer: 4,
+ // opacity: 0.8,
+ errorTileUrl: 'https://maps.ocap2.com/missing_tiles.png'
+ });
+ baseLayers.push(topoDarkLayer);
+ }
+
+ if (world.hasTopoRelief) {
+ topoReliefLayer = L.tileLayer(topoReliefLayerUrl, {
+ maxNativeZoom: world.maxZoom,
+ // maxZoom: mapMaxZoom,
+ minNativeZoom: world.minZoom,
+ bounds: mapBounds,
+ label: "Topographic Relief",
+ attribution: "Map Data © " + world.attribution,
+ noWrap: true,
+ tms: false,
+ keepBuffer: 4,
+ // opacity: 0.9,
+ errorTileUrl: 'https://maps.ocap2.com/missing_tiles.png'
+ });
+ baseLayers.push(topoReliefLayer);
+ }
+
+ if (world.hasColorRelief) {
+ colorReliefLayer = L.tileLayer(colorReliefLayerUrl, {
+ maxNativeZoom: world.maxZoom,
+ // maxZoom: mapMaxZoom,
+ minNativeZoom: world.minZoom,
+ bounds: mapBounds,
+ attribution: "Map Data © " + world.attribution,
+ label: "Colored Relief",
+ noWrap: true,
+ tms: false,
+ keepBuffer: 4,
+ // opacity: 1,
+ errorTileUrl: 'https://maps.ocap2.com/missing_tiles.png'
+ });
+ baseLayers.push(colorReliefLayer);
+ }
+
+
+ // setup controls
+
+ overlayLayerControl = L.control.layers({}, {
+ // overlay layers
+ "Units and Vehicles": entitiesLayerGroup,
+ "Selected Side Markers": markersLayerGroup,
+ "Editor/Briefing Markers": systemMarkersLayerGroup,
+ "Projectile Markers": projectileMarkersLayerGroup
+ }, {
+ position: 'bottomright',
+ collapsed: false
+ });
+ overlayLayerControl.addTo(map);
+
+
+ baseLayerControl = L.control.basemaps({
+ basemaps: baseLayers,
+ tileX: 2, // tile X coordinate
+ tileY: 6, // tile Y coordinate
+ tileZ: 4 // tile zoom level
+ }, {
+ position: 'bottomright',
+ });
+ baseLayerControl.addTo(map);
+
+
+ // Add zoom control
+ L.control.zoominfo({
+ position: 'bottomright'
}).addTo(map);
+
+ function test () {
+ // Add marker to map on click
+ map.on("click", function (e) {
+ // latLng, layerPoint, containerPoint, originalEvent
+ console.debug("latLng");
+ console.debug(e.latlng);
+ console.debug("LayerPoint");
+ console.debug(e.layerPoint);
+ console.debug("Projected");
+ console.debug(map.project(e.latlng, mapMaxNativeZoom));
+ })
+ }
+
+
+ map.on("baselayerchange", (event) => {
+ // console.log(event.name); // Print out the new active layer
+ // console.log(event);
+ // multiplier = event.name
+ });
+ map.on("overlayadd", (event) => {
+ // console.log(event.name); // Print out the new active layer
+ // console.log(event);
+ switch (event.name) {
+ case "Units and Vehicles": {
+ if (ui.hideMarkerPopups == false) {
+ entitiesLayerGroup.eachLayer(layer => {
+ layer.openPopup();
+ });
+ }
+ break;
+ };
+ case "Selected Side Markers": {
+ markersLayerGroup.eachLayer(layer => {
+ layer.remove()
+ })
+ markers.forEach(marker => {
+ if (marker._player instanceof Unit) {
+ marker._marker = null;
+ }
+ })
+ // for (const marker of markers) {
+ // marker.manageFrame(playbackFrame);
+ // }
+ break;
+ };
+ case "Editor/Briefing Markers": {
+ if (ui.markersEnable == true) {
+ systemMarkersLayerGroup.eachLayer(layer => {
+ layer.openPopup();
+ })
+ }
+ break;
+ };
+ case "Projectile Markers": {
+ projectileMarkersLayerGroup.getLayers().forEach(layer => {
+ layer.remove()
+ })
+ markers.forEach(marker => {
+ if (marker.isMagIcon()) {
+ marker._marker = null;
+ }
+ })
+ break;
+ };
+
+ default: {
+ break;
+ };
+ };
+ });
+ map.on("overlayremove", (event) => {
+ // console.log(event.name); // Print out the new active layer
+ // console.log(event);
+ switch (event.name) {
+ case "Units and Vehicles": {
+ // ui.hideMarkerPopups = false;
+ // entitiesLayerGroup.eachLayer(layer => {
+ // layer.openPopup();
+ // });
+ break;
+ };
+ case "Selected Side Markers": {
+ markersLayerGroup.eachLayer(layer => {
+ // layer.remove()
+ })
+ break;
+ };
+ case "Editor/Briefing Markers": {
+ // systemMarkersLayerGroup.eachLayer(layer => {
+ // layer.openPopup();
+ // })
+ break;
+ };
+ case "Projectile Markers": {
+ projectileMarkersLayerGroup.getLayers().forEach(layer => {
+ layer.remove()
+ })
+
+ break;
+ };
+
+ default: {
+ break;
+ };
+ };
+ });
+
+
+
// Add keypress event listener
mapDiv.addEventListener("keypress", function (event) {
//console.log(event);
@@ -258,20 +634,12 @@ function initMap (world) {
}
});
+
+
createInitialMarkers();
- let boundaryMarks = markers.filter(item => {
- return item._type === "moduleCoverMap"
- });
- if (boundaryMarks.length === 4) {
- let boundaryPoints = boundaryMarks.map(item => armaToLatLng(item._positions[0][1]));
- let boundaryPolygon = L.polygon(boundaryPoints, { color: "#000000", fill: false, interactive: false, noClip: true }).addTo(map);
- map.flyToBounds(boundaryPolygon.getBounds());
- } else {
- map.flyToBounds(map.getBounds());
- }
document.dispatchEvent(new Event("mapInited"));
- //test();
+ // test();
}
function createInitialMarkers () {
@@ -284,6 +652,57 @@ function createInitialMarkers () {
});
}
+function getMapImageBounds () {
+ console.debug("Calculating map bounds from map image size");
+ mapBounds = new L.LatLngBounds(
+ map.unproject([0, worldObject.imageSize], mapMaxNativeZoom),
+ map.unproject([worldObject.imageSize, 0], mapMaxNativeZoom)
+ );
+ return mapBounds;
+}
+
+function getMapMarkerBounds () {
+
+ let boundaryMarks = markers.filter(item => {
+ return item._type === "moduleCoverMap"
+ });
+
+ if (boundaryMarks.length === 4) {
+ console.debug("Found boundary marks from BIS_moduleCoverMap")
+ let boundaryPoints = boundaryMarks.map(item => armaToLatLng(item._positions[0][1]));
+ let boundaryPolygon = L.polygon(boundaryPoints, { color: "#000000", fill: false, interactive: false, noClip: true }).addTo(map);
+
+ return boundaryPolygon.getBounds();
+ }
+
+ // calculate map bounds from markers
+ console.debug(`Calculating map bounds from ${markers.length} markers`)
+ var markerBounds = L.latLngBounds()
+ let invalidMarkers = [];
+ markers.forEach(item => {
+ if (item._positions[0] === undefined) {
+ return invalidMarkers.push(item)
+ }
+ if (item._positions[0][1] === undefined) {
+ return invalidMarkers.push(item)
+ }
+
+ // some marker positions are nested in an array, account for this
+ if (Array.isArray(item._positions[0][1][0])) {
+ return markerBounds.extend(armaToLatLng(item._positions[0][1][0]));
+ } else {
+ return markerBounds.extend(armaToLatLng(item._positions[0][1]));
+ };
+ });
+
+ if (invalidMarkers.length > 0) {
+ console.debug(`Found ${invalidMarkers.length} potentially invalid markers, ignoring them`, invalidMarkers)
+ }
+
+
+ return markerBounds;
+}
+
function defineIcons () {
icons = {
man: {},
@@ -319,7 +738,7 @@ function defineIcons () {
let imgPathUnknown = "images/markers/unknown/";
- let imgs = ["blufor", "opfor", "ind", "civ", "unknown", "dead", "hit", "follow", "unconscious"];
+ let imgs = ["blufor", "opfor", "ind", "civ", "logic", "unknown", "dead", "hit", "follow", "unconscious"];
imgs.forEach((img, i) => {
icons.man[img] = L.icon({ className: "animation", iconSize: [16, 16], iconUrl: `${imgPathMan}${img}.svg` });
// icons.manMG[img] = L.icon({ className: "animation", iconSize: [16, 16], iconUrl: `${imgPathManMG}${img}.svg` });
@@ -357,10 +776,11 @@ function goFullscreen () {
element.msRequestFullscreen();
}
}
-
+// http://127.0.0.1:5000/?file=2021_08_20__21_24_FNF_TheMountain_Youre_A_Towel_V2_Destroy_EU.json&frame=87&zoom=1&x=-134.6690319189602&y=78.0822715759277
// Converts Arma coordinates [x,y] to LatLng
function armaToLatLng (coords) {
- const pixelCoords = [(coords[0] * multiplier) + trim, (imageSize - (coords[1] * multiplier)) + trim];
+ var pixelCoords;
+ pixelCoords = [(coords[0] * multiplier) + trim, (imageSize - (coords[1] * multiplier)) + trim];
return map.unproject(pixelCoords, mapMaxNativeZoom);
}
@@ -369,69 +789,7 @@ function dateToLittleEndianString (date) {
return (date.getUTCDate() + "/" + (date.getUTCMonth() + 1) + "/" + date.getUTCFullYear());
}
-function test () {
- // Add marker to map on click
- map.on("click", function (e) {
- //console.log(e.latlng);
-
- console.log(map.project(e.latlng, mapMaxNativeZoom));
-
- brushPattern = {
- color: "#FF0000",
- opacity: 1,
- angle: 45,
- weight: 2,
- spaceWeight: 6
- };
-
- var brushPatternObj = new L.StripePattern(brushPattern);
- brushPatternObj.addTo(map)
-
- shapeOptions = {
- color: "#FF0000",
- stroke: true,
- fill: true,
- fillPattern: brushPatternObj
- };
-
- var circleMarker;
- circleMarker = L.circle(e.latlng, shapeOptions);
- L.Util.setOptions(circleMarker, { radius: 20, interactive: false });
- circleMarker.addTo(map);
-
-
- let pos = e.latlng;
- let startX = pos.lat;
- let startY = pos.lng;
- let sizeX = 75;
- let sizeY = 75;
-
- let pointsRaw = [
- [startX - sizeX, startY + sizeY], // top left
- [startX + sizeX, startY + sizeY], // top right
- [startX + sizeX, startY - sizeY], // bottom right
- [startX - sizeX, startY - sizeY] // bottom left
- ];
-
- const sqMarker = L.polygon(pointsRaw, {noClip: true, interactive: false});
- L.Util.setOptions(sqMarker, shapeOptions);
- // if (brushPattern) {
- // L.Util.setOptions(sqMarker, { fillPattern: brushPatternObj, fillOpacity: 1.0});
- // };
- sqMarker.addTo(map);
-
- // var marker = L.circleMarker(e.latlng).addTo(map);
- // marker.setRadius(5);
- });
-
- // var marker = L.circleMarker(armaToLatLng([2438.21, 820])).addTo(map);
- // marker.setRadius(5);
-
- // var marker = L.circleMarker(armaToLatLng([2496.58, 5709.34])).addTo(map);
- // marker.setRadius(5);
-}
-
-function dateToTimeString(date, isUtc = false) {
+function dateToTimeString (date, isUtc = false) {
let hours = date.getHours();
let minutes = date.getMinutes();
let seconds = date.getSeconds();
@@ -523,22 +881,57 @@ async function loadOperationByFilename(filename) {
}
// Read operation JSON data and create unit objects
-function processOp (filepath) {
+function processOp (filepath, opRecord) {
console.log("Processing operation: (" + filepath + ")...");
const time = new Date();
fileName = filepath.substr(5, filepath.length);
+ let data;
return fetch(filepath)
.then((res) => res.json())
- .then((data) => {
+ .then((json) => {
+ data = json;
worldName = data.worldName.toLowerCase();
- return Promise.all([data, getWorldByName(worldName)]);
+ return worldName;
})
- .then(([data, world]) => {
- var multiplier = world.multiplier;
+ .then((wn) => getWorldByName(wn))
+ .then((world) => {
+ worldObject = world;
+ document.dispatchEvent(new Event("worldLoaded"))
+ multiplier = world.multiplier;
missionName = data.missionName;
- ui.setMissionName(missionName);
+ let playedDate;
+ if (opRecord) {
+ playedDate = opRecord.date;
+ } else {
+ // try to parse from filename
+ // if filename has "\d__\d" format, use that
+ // else no date, in the event a temp file is referenced
+ let dateMatch = fileName.match(/^\d{4}_\d{2}_\d{2}/);
+ if (dateMatch) {
+ playedDate = dateMatch[0].replace(/_/g, "-");
+ } else {
+ playedDate = "";
+ }
+ }
+
+ let worldDisplayName;
+ if ([undefined, "NOT FOUND"].includes(world.displayName)) {
+ if (world.name == "NOT FOUND") {
+ worldDisplayName = world.worldName
+ } else {
+ worldDisplayName = world.name
+ }
+ } else {
+ worldDisplayName = world.displayName
+ }
+ ui.setMissionName(`${missionName} - Recorded ${playedDate} on ${worldDisplayName}`);
+
+ extensionVersion = data.extensionVersion;
+ ui.setExtensionVersion(extensionVersion);
+ addonVersion = data.addonVersion;
+ ui.setAddonVersion(addonVersion);
endFrame = data.endFrame;
frameCaptureDelay = data.captureDelay * 1000;
ui.setMissionEndTime(endFrame);
@@ -667,7 +1060,7 @@ function processOp (filepath) {
markers.push(marker);
}
} catch (err) {
- console.error(`Failed to process ${markerJSON[9]} with text "${markerJSON[1]}"\nError: ${err}`);
+ console.error(`Failed to process ${markerJSON[9]} with type ${markerJSON[0]} and text "${markerJSON[1]}"\nError: ${err}\nMarkerJSON: ${JSON.stringify(markerJSON, null, 2)}`)
}
});
}
@@ -703,11 +1096,13 @@ function processOp (filepath) {
}
// Loop through events
+ var invalidHitKilledEvents = [];
data.events.forEach(function (eventJSON) {
var frameNum = eventJSON[0];
var type = eventJSON[1];
var gameEvent = null;
+
switch (true) {
case (type == "killed" || type == "hit"):
const causedByInfo = eventJSON[3];
@@ -728,16 +1123,22 @@ function processOp (filepath) {
// TODO: Find out why victim/causedBy can sometimes be null
if (causedBy == null || victim == null) {
- console.warn("unknown victim/causedBy", victim, causedBy);
+ invalidHitKilledEvents.push({
+ "reason": "null/unknown victim/causedBy",
+ "victim": victim,
+ "causedBy": causedBy,
+ "event": eventJSON
+ });
}
// Incrememt kill/death count for killer/victim
if (type === "killed" && (causedBy != null)) {
- if (causedBy !== victim && causedBy._side === victim._side) {
- causedBy.teamKillCount++;
- }
if (causedBy !== victim) {
- causedBy.killCount++;
+ if (causedBy._side === victim._side) {
+ causedBy.teamKillCount++;
+ } else {
+ causedBy.killCount++;
+ }
}
victim.deathCount++;
}
@@ -789,21 +1190,45 @@ function processOp (filepath) {
case (type == "endMission"):
gameEvent = new endMissionEvent(frameNum, type, eventJSON[2][0], eventJSON[2][1]);
break;
+ case (type == "generalEvent"):
+ gameEvent = new generalEvent(frameNum, type, eventJSON[2]);
+ break;
}
+
// Add event to gameEvents list
if (gameEvent != null) {
gameEvents.addEvent(gameEvent);
}
});
+ if (invalidHitKilledEvents.length > 0) {
+ console.warn("WARNING: " + invalidHitKilledEvents.length + " hit/killed events will use 'something' as the victim/killer. See the debug stream for a full list.");
+ console.debug(invalidHitKilledEvents);
+ }
+
gameEvents.init();
console.log("Finished processing operation (" + (new Date() - time) + "ms).");
+ console.debug("Addon version: " + data.addonVersion);
+ console.debug("Extension version: " + data.extensionVersion);
+ console.debug("Extension build: " + data.extensionBuild);
+ console.debug("Total frames: " + data.endFrame);
+ console.debug("Total entities: " + data.entities.length);
+ console.debug("Total markers: " + data.Markers.length);
+ console.debug("Total events: " + data.events.length);
+ if (data.Markers.length > 50000) {
+ console.warn("WARNING: This mission contains more than 50,000 markers. This may cause performance issues and indicate configured or malformed marker exclusion settings in the addon.");
+ }
+ console.log("Initializing map...");
+ console.debug(JSON.stringify(world, null, 2));
initMap(world);
startPlaybackLoop();
toggleHitEvents(false);
// playPause();
ui.hideModal();
+
+ // fire event
+ document.dispatchEvent(new Event('operationLoaded'));
}).catch((error) => {
ui.modalBody.innerHTML = `Error: "${filepath}" failed to load.
${error}.`;
console.error(error);
@@ -947,10 +1372,19 @@ function startPlaybackLoop () {
}
for (const marker of markers) {
marker.manageFrame(playbackFrame);
- if (ui.markersEnable) {
- marker.hideMarkerPopup(false);
- } else {
- marker.hideMarkerPopup(true);
+ if (!marker.isMagIcon()) {
+ if (ui.markersEnable) {
+ marker.hideMarkerPopup(false);
+ } else {
+ marker.hideMarkerPopup(true);
+ }
+ }
+ if (marker.isMagIcon()) {
+ if (ui.nicknameEnable) {
+ marker.hideMarkerPopup(false);
+ } else {
+ marker.hideMarkerPopup(true);
+ }
}
}
@@ -993,7 +1427,7 @@ function startPlaybackLoop () {
var playbackTimeout = setTimeout(playbackFunction, frameCaptureDelay / playbackMultiplier);
}
-function colorElement(element, color) {
+function colorElement (element, color) {
if (!color) {
return;
}
@@ -1011,7 +1445,7 @@ function colorElement(element, color) {
}
}
-function getMarkerColor(color, defaultColor = "ffffff") {
+function getMarkerColor (color, defaultColor = "ffffff") {
let hexColor = defaultColor;
if (!color) {
return hexColor;
@@ -1033,12 +1467,12 @@ function getMarkerColor(color, defaultColor = "ffffff") {
return hexColor;
}
-function colorMarkerIcon(element, icon, color) {
+function colorMarkerIcon (element, icon, color) {
element.src = `/images/markers/${icon}/${getMarkerColor(color)}.png`;
}
-function getPulseMarkerColor(color, defaultColor = "000000") {
+function getPulseMarkerColor (color, defaultColor = "000000") {
let hexColor = defaultColor;
if (!color) {
return hexColor;
@@ -1067,7 +1501,7 @@ String.prototype.encodeHTMLEntities = function () {
});
}
-function closestEquivalentAngle(from, to) {
+function closestEquivalentAngle (from, to) {
const delta = ((((to - from) % 360) + 540) % 360) - 180;
return from + delta;
}
@@ -1344,6 +1778,7 @@ async function processOpStreaming(operationId, format = 'protobuf') {
// Get world info and init map
const world = await getWorldByName(worldName);
+ worldObject = world; // Set global for getMapImageBounds()
initMap(world);
ui.updateLoadingProgress(4, 4, 'Starting playback...');
diff --git a/static/scripts/ocap.marker.js b/static/scripts/ocap.marker.js
index c36f9bb1b..f9aec03df 100644
--- a/static/scripts/ocap.marker.js
+++ b/static/scripts/ocap.marker.js
@@ -65,22 +65,7 @@ class Marker {
if (!(undefined === brush && undefined === shape)) {
this._brush = brush;
-
- var brushPattern;
- if (["Cross", "Grid", "DiagGrid"].includes(brush)) {
- // var patternShape = new L.PatternPath({ d: 'M10 0 L7 20 L25 20 Z', fill: true });
- // brushPattern = new L.Pattern({
- // patternUnits: "objectBoundingBox",
- // patternContentUnits: "objectBoundingBox",
- // color: this._color,
- // opacity: 1
- // });
- // brushPattern.addShape(patternShape);
- brushPattern = new L.StripePattern({ renderer: L.svg() });
- } else if (["Horizontal", "Vertical", "FDiagonal", "BDiagonal"].includes(brush)) {
- brushPattern = new L.StripePattern({ renderer: L.svg() });
- }
- this._brushPattern = brushPattern;
+ this._brushPattern = null;
this._brushPatternOptions = null;
switch (brush) {
case "solid":
@@ -113,7 +98,7 @@ class Marker {
color: this._color,
stroke: false,
fill: true,
- fillOpacity: 1
+ fillOpacity: 0.2
};
break;
case "vertical":
@@ -128,7 +113,7 @@ class Marker {
color: this._color,
stroke: false,
fill: true,
- fillOpacity: 1
+ fillOpacity: 0.2
};
break;
case "grid":
@@ -144,7 +129,7 @@ class Marker {
color: this._color,
stroke: false,
fill: true,
- fillOpacity: 1
+ fillOpacity: 0.2
};
break;
case "fdiagonal":
@@ -160,7 +145,7 @@ class Marker {
color: this._color,
stroke: false,
fill: true,
- fillOpacity: 1
+ fillOpacity: 0.2
};
break;
case "bdiagonal":
@@ -176,7 +161,7 @@ class Marker {
color: this._color,
stroke: false,
fill: true,
- fillOpacity: 1
+ fillOpacity: 0.2
};
break;
case "diaggrid":
@@ -186,13 +171,14 @@ class Marker {
opacity: 0.8,
angle: 45,
weight: 1,
- spaceWeight: 1
+ spaceWeight: 3,
+ spaceOpacity: 0.0
};
this._shapeOptions = {
color: this._color,
stroke: false,
fill: true,
- fillOpacity: 1,
+ fillOpacity: 0.2,
};
break;
case "cross":
@@ -208,7 +194,7 @@ class Marker {
color: this._color,
stroke: false,
fill: true,
- fillOpacity: 1
+ fillOpacity: 0.2
};
break;
case "border":
@@ -232,6 +218,11 @@ class Marker {
default:
break;
}
+
+ // Create stripe pattern if brush options were set
+ if (this._brushPatternOptions) {
+ this._brushPattern = new L.StripePattern(this._brushPatternOptions);
+ }
} else {
this._shapeOptions = {
color: this._color,
@@ -247,7 +238,7 @@ class Marker {
this._systemMarkers = ["ObjectMarker", "moduleCoverMap", "safeStart"];
}
- updateRender(f) {
+ updateRender (f) {
if (this._shape === "RECTANGLE") {
const frameIndex = this._markerOnFrame(f);
if (frameIndex >= 0 && (this._side === ui.currentSide || this._side === "GLOBAL")) {
@@ -256,12 +247,21 @@ class Marker {
}
}
+ removeMarker () {
+ let marker = this._marker;
+ if (marker != null) {
+ marker.remove();
+ this._marker = null;
+ }
+ }
+
manageFrame (f) {
const frameIndex = this._markerOnFrame(f);
if (frameIndex != null && (this._side === ui.currentSide || this._side === "GLOBAL")) {
this._updateAtFrame(frameIndex);
} else {
- this.hide();
+ // this.hide();
+ this.removeMarker();
}
}
@@ -273,7 +273,7 @@ class Marker {
let alpha = frameData[3];
if (this._shape === "RECTANGLE" && Array.isArray(pos[0])) {
- console.warn("wrong RECTANGLE positions, converting to POLYLINE");
+ console.debug("wrong RECTANGLE positions, converting to POLYLINE");
this._shape = "POLYLINE";
}
@@ -368,14 +368,18 @@ class Marker {
// check if update is needed
let variance = 0;
- let curMarkerCenter = this._marker.getCenter();
- variance = variance + Math.abs((Math.abs(curMarkerCenter.lat) - Math.abs(latLng.lat)));
- variance = variance + Math.abs((Math.abs(curMarkerCenter.lng) - Math.abs(latLng.lng)));
+ try {
+ curMarkerCenter = this._marker.getCenter(); variance = variance + Math.abs((Math.abs(curMarkerCenter.lat) - Math.abs(latLng.lat)));
+ variance = variance + Math.abs((Math.abs(curMarkerCenter.lng) - Math.abs(latLng.lng)));
+
+ // if (variance > 5) {
+ // process rotation around center
+ let pointsRotate = this._rotatePoints(armaToLatLng(pos), points, dir);
+ this._marker.setLatLngs(pointsRotate).redraw();
+ } catch {
+ // If the layer is hidden, this will error because _marker.getCenter() will fail, but that's fine, we don't need to update it if it's hidden
+ };
- // if (variance > 5) {
- // process rotation around center
- let pointsRotate = this._rotatePoints(armaToLatLng(pos), points, dir);
- this._marker.setLatLngs(pointsRotate).redraw();
// };
} else if (this._shape === "POLYLINE") {
if (alpha === undefined || alpha === null) { alpha = 1 }
@@ -405,10 +409,23 @@ class Marker {
return res
}
+ isMagIcon () {
+ if (
+ // projectiles
+ (
+ this._type.search("magIcons") > -1 ||
+ this._type === "Minefield" ||
+ this._type === "mil_triangle"
+ ) &&
+ this._side === "GLOBAL"
+ ) { return true } else { return false };
+ }
+
hide () {
// if (this._isShow == true) {
this._isShow = false;
this.setMarkerOpacity(0);
+ this.hideMarkerPopup(true);
// };
}
@@ -429,20 +446,24 @@ class Marker {
let marker;
let popupText = "";
-
if ((this._player === -1 || this._player === false) && this._shape === "ICON") {
// objNull passed, no owner. system marker with basic popup
let markerCustomText = "";
if (this._text) { markerCustomText = this._text.encodeHTMLEntities(); }
- marker = L.marker(latLng, { icon: this._icon, interactive: false, rotationOrigin: "50% 50%" }).addTo(map);
- let popup = this._createPopup(markerCustomText);
- marker.bindPopup(popup).openPopup();
+
+ marker = L.marker(latLng, { icon: this._icon, interactive: false, rotationOrigin: "50% 50%" })
+ marker.addTo(systemMarkersLayerGroup);
+
+ if (markerCustomText != "") {
+ let popup = this._createPopup(markerCustomText);
+ marker.bindPopup(popup).openPopup();
+ };
// Set direction
marker.setRotationAngle(dir);
- } else if (this._shape === "ICON") {
+ } else if (this._player instanceof Unit && this._shape === "ICON") {
let interactiveVal = false;
let markerCustomText = "";
@@ -478,7 +499,22 @@ class Marker {
popupText = `${this._side} ${this._player.getName().encodeHTMLEntities()} ${markerCustomText}`;
}
- marker = L.marker(latLng, { icon: this._icon, interactive: interactiveVal, rotationOrigin: "50% 50%" }).addTo(map);
+ marker = L.marker(latLng, { icon: this._icon, interactive: interactiveVal, rotationOrigin: "50% 50%" })
+ if (
+ // projectiles
+ (
+ this._type.search("magIcons") > -1 ||
+ this._type === "Minefield" ||
+ this._type === "mil_triangle"
+ ) &&
+ this._side === "GLOBAL"
+ ) {
+ marker.addTo(projectileMarkersLayerGroup);
+ } else if (this._player instanceof Unit) {
+ marker.addTo(markersLayerGroup);
+ } else {
+ marker.addTo(systemMarkersLayerGroup);
+ }
let popup = this._createPopup(popupText);
marker.bindPopup(popup).openPopup();
@@ -490,28 +526,33 @@ class Marker {
let rad = this._size[0] * 0.015 * window.multiplier;
if (this._brushPattern) {
- L.Util.setOptions(this._brushPattern, this._brushPatternOptions);
this._brushPattern.addTo(map);
marker = L.circle(latLng, { radius: rad, noClip: false, interactive: false, fillPattern: this._brushPattern });
L.Util.setOptions(marker, this._shapeOptions);
} else {
- marker = L.circle(latLng, { radius: rad, noClip: false, interactive: false/* , renderer: L.canvas() */ });
+ marker = L.circle(latLng, { radius: rad, noClip: false, interactive: false });
L.Util.setOptions(marker, this._shapeOptions);
}
- marker.addTo(map);
+ marker.addTo(systemMarkersLayerGroup);
} else if (this._shape === "RECTANGLE") {
if (this._brushPattern) {
- L.Util.setOptions(this._brushPattern, this._brushPatternOptions);
this._brushPattern.addTo(map);
marker = L.polygon(latLng, { noClip: false, interactive: false, fillPattern: this._brushPattern });
L.Util.setOptions(marker, this._shapeOptions);
} else {
- marker = L.polygon(latLng, { noClip: false, interactive: false/* , renderer: L.canvas() */ });
+ marker = L.polygon(latLng, { noClip: false, interactive: false });
L.Util.setOptions(marker, this._shapeOptions);
}
- marker.addTo(map);
+
+ marker.addTo(systemMarkersLayerGroup);
} else if (this._shape === "POLYLINE") {
- marker = L.polyline(latLng, { color: this._color, opacity: 1, noClip: true, lineCap: 'butt', lineJoin: 'round', interactive: false }).addTo(map);
+ marker = L.polyline(latLng, { color: this._color, opacity: 1, noClip: true, lineCap: 'butt', lineJoin: 'round', interactive: false })
+
+ if (this._player === -1 || this._player === false) {
+ marker.addTo(systemMarkersLayerGroup)
+ } else {
+ marker.addTo(markersLayerGroup);
+ }
}
this._marker = marker;
@@ -579,10 +620,10 @@ class Marker {
}
if (this._shape == "ICON") {
this._marker.setOpacity(opacity);
- let popup = this._marker.getPopup();
- if (popup != null) {
- popup.getElement().style.opacity = opacity;
- }
+ // let popup = this._marker.getPopup();
+ // if (popup != null) {
+ // if (opacity > 0) { popup.openPopup() } else { this.hideMarkerPopup(true) };
+ // }
} else if (this._shape == "ELLIPSE") {
this._marker.setStyle({ opacity: strokeOpacity, fillOpacity: fillOpacity });
} else if (this._shape == "RECTANGLE") {
@@ -594,17 +635,19 @@ class Marker {
}
hideMarkerPopup (bool) {
- if (this._marker != null) {
- let popup = this._marker.getPopup();
- if (popup == null) { return }
+ if (!this._marker) return;
+ let popup = this._marker.getPopup();
+ if (popup == null) { return }
- let element = popup.getElement();
+ let element = popup.getElement();
+ if (element) {
let display = "inherit";
if (bool) { display = "none" }
- if (element.style.display != display) {
+ if (element.style.display !== display) {
element.style.display = display;
}
}
+ return true;
}
}
diff --git a/static/scripts/ocap.ui.js b/static/scripts/ocap.ui.js
index 335fa7e4c..ecefd6f2c 100644
--- a/static/scripts/ocap.ui.js
+++ b/static/scripts/ocap.ui.js
@@ -23,6 +23,8 @@ class UI {
this.statsDialog = null;
this.statsDialogBody = null;
this.missionName = null;
+ this.extensionVersion = null;
+ this.addonVersion = null;
//this.loadOpButton = null;
this.playPauseButton = null;
this.playbackSpeedSliderContainer = null;
@@ -62,11 +64,12 @@ class UI {
this.elapsedTime = null;
this.disableKillCount = false;
+ this.useCloudTiles = true;
this._init();
};
- _init() {
+ _init () {
// Setup top panel
this.missionName = document.getElementById("missionName");
@@ -130,7 +133,7 @@ class UI {
// Change time view
this.toggleTime = document.getElementById("toggleTime");
- for (const timeValue of ["elapsed","mission","system"]) {
+ for (const timeValue of ["elapsed", "mission", "system"]) {
const option = document.createElement("option");
option.value = timeValue;
localizable(option, `time_${timeValue}`, "", "");
@@ -249,8 +252,8 @@ class UI {
// Hide/show ui on keypress
var left_title = document.getElementById("leftPanel").getElementsByClassName("title")[0];
var right_title = document.getElementById("rightPanel").getElementsByClassName("title")[0];
- left_title.onclick = () => {this.toggleLeftPanel()};
- right_title.onclick = () => {this.toggleRightPanel()};
+ left_title.onclick = () => { this.toggleLeftPanel() };
+ right_title.onclick = () => { this.toggleRightPanel() };
mapDiv.addEventListener("keypress", (event) => {
if (event.charCode === 101) this.toggleLeftPanel();
else if (event.charCode === 114) this.toggleRightPanel();
@@ -306,7 +309,7 @@ class UI {
this.frameSliderWidthInPercent = (this.frameSlider.offsetWidth / this.frameSlider.parentElement.offsetWidth) * 100;
};
- checkAvailableTimes() {
+ checkAvailableTimes () {
for (const option of this.toggleTime.options) {
if (option.value === "system") {
option.disabled = !this.systemTime;
@@ -316,7 +319,7 @@ class UI {
}
}
- getTimeString(frame) {
+ getTimeString (frame) {
let date = new Date(frame * frameCaptureDelay);
let isUTC = true;
if (this.timeToShow === "system") {
@@ -329,7 +332,7 @@ class UI {
return dateToTimeString(date, isUTC);
}
- showCursorTooltip(text) {
+ showCursorTooltip (text) {
let tooltip = this.cursorTooltip;
tooltip.textContent = text;
tooltip.className = "cursorTooltip";
@@ -347,15 +350,23 @@ class UI {
console.log(this.cursorTooltip);
}
- _moveCursorTooltip(event) {
+ _moveCursorTooltip (event) {
ui.cursorTooltip.style.transform = `translate3d(${event.pageX}px, ${event.pageY}px, 0px)`;
}
- setMissionName(name) {
+ setMissionName (name) {
this.missionName.textContent = name;
}
- detectTimes(times) {
+ setAddonVersion (version) {
+ this.addonVersion = version;
+ }
+
+ setExtensionVersion (version) {
+ this.extensionVersion = version;
+ }
+
+ detectTimes (times) {
for (const time of times) {
if (time.frameNum === 0) {
this.systemTime = new Date(time.systemTimeUTC + "Z");
@@ -379,8 +390,8 @@ class UI {
// Set mission time based on given frame
// Move playback + slider to given frame in time
- setMissionCurTime(f) {
- missionCurDate.setTime(f*frameCaptureDelay);
+ setMissionCurTime (f) {
+ missionCurDate.setTime(f * frameCaptureDelay);
this.updateCurrentTime(f);
this.setFrameSliderVal(f);
playbackFrame = +f;
@@ -390,17 +401,17 @@ class UI {
}
}
- updateCurrentTime(f = playbackFrame) {
+ updateCurrentTime (f = playbackFrame) {
this.missionCurTime.textContent = ui.getTimeString(f);
}
- setMissionEndTime(f) {
+ setMissionEndTime (f) {
this.updateEndTime(f);
this.setFrameSliderMax(f);
}
- updateEndTime(f = this.frameSlider.max) {
- let date = new Date(f*frameCaptureDelay);
+ updateEndTime (f = this.frameSlider.max) {
+ let date = new Date(f * frameCaptureDelay);
let isUTC = true;
if (this.systemTime && this.timeToShow === "system") {
date = new Date(this.systemTime.getTime() + (this.frameSlider.max * frameCaptureDelay));
@@ -412,30 +423,34 @@ class UI {
this.missionEndTime.textContent = dateToTimeString(date, isUTC);
}
- setFrameSliderMax(f) {
+ setFrameSliderMax (f) {
this.frameSlider.max = f;
};
- setFrameSliderVal(f) {
+ setFrameSliderVal (f) {
this.frameSlider.value = f;
};
- toggleLeftPanel() {
+ toggleLeftPanel () {
if (this.leftPanel.style.display == "none") {
this.leftPanel.style.display = "initial";
+ // adjust customize logo to accomodate left panel
+ this.customizeLogo ? this.customizeLogo.style.left = "375px" : null;
} else {
this.leftPanel.style.display = "none";
+ // adjust customize logo to accomodate left panel
+ this.customizeLogo ? this.customizeLogo.style.left = "15px" : null;
}
};
- updateTitleSide() {
+ updateTitleSide () {
sideCiv.textContent = "CIV\n\r(" + countCiv + ")";
sideEast.textContent = "OPFOR\n\r(" + countEast + ")";
sideGuer.textContent = "IND\n\r(" + countGuer + ")";
sideWest.textContent = "BLUFOR\n\r(" + countWest + ")";
};
- switchSide(side) {
+ switchSide (side) {
this.currentSide = side;
this.sideEast.style.backgroundColor = "rgba(255, 183, 38,0.1)";
this.listEast.style.display = "none";
@@ -460,7 +475,7 @@ class UI {
}
};
- toggleRightPanel() {
+ toggleRightPanel () {
if (this.rightPanel.style.display == "none") {
this.rightPanel.style.display = "initial";
} else {
@@ -468,32 +483,32 @@ class UI {
}
};
- showModalOpSelection() {
+ showModalOpSelection () {
// Set header/body
localizable(this.modalHeader, "select_mission");
localizable(this.modalBody, "list_compilation");
// Add buttons
-/* var playButton = document.createElement("div");
- playButton.className = "modalButton";
- playButton.textContent = "Play";
- var cancelButton = document.createElement("div");
- cancelButton.className = "modalButton";
- cancelButton.textContent = "Cancel";
- var hideModal = this.hideModal;
- cancelButton.addEventListener("click", function() {
- this.hideModal();
- });
-
- this.modalButtons.appendChild(cancelButton);
- this.modalButtons.appendChild(playButton);*/
+ /* var playButton = document.createElement("div");
+ playButton.className = "modalButton";
+ playButton.textContent = "Play";
+ var cancelButton = document.createElement("div");
+ cancelButton.className = "modalButton";
+ cancelButton.textContent = "Cancel";
+ var hideModal = this.hideModal;
+ cancelButton.addEventListener("click", function() {
+ this.hideModal();
+ });
+
+ this.modalButtons.appendChild(cancelButton);
+ this.modalButtons.appendChild(playButton);*/
// Show modal
this.showModal();
this.modalFilter.style.display = "inherit";
};
- setModalOpList() {
+ setModalOpList () {
var OpList;
var n = filterTagGameInput.options.selectedIndex;
var tag = n != -1 ? filterTagGameInput.options[n].value : "";
@@ -533,7 +548,7 @@ class UI {
var headerRow = document.createElement("tr");
var columnNames = ["mission", "map", "data", "durability", "tag"];
- columnNames.forEach(function(name) {
+ columnNames.forEach(function (name) {
var th = document.createElement("th");
localizable(th, name);
th.className = "medium";
@@ -552,7 +567,7 @@ class UI {
secondsToTimeString(op.mission_duration),
op.tag
];
- vals.forEach(function(val) {
+ vals.forEach(function (val) {
var cell = document.createElement("td");
cell.textContent = val;
row.appendChild(cell);
@@ -569,7 +584,7 @@ class UI {
});
};
- makeModalButton(text, func) {
+ makeModalButton (text, func) {
var button = document.createElement("div");
button.className = "modalButton";
button.textContent = text;
@@ -578,7 +593,7 @@ class UI {
return button;
};
- showModalStats() {
+ showModalStats () {
// localizable(this.statsDialogHeader, "info");
const units = [];
@@ -591,7 +606,9 @@ class UI {
if (unit) {
unit.killCount += entity.killCount;
unit.teamKillCount += entity.teamKillCount;
- unit.deathCount += entity.deathCount;
+ if (entity.deathCount > 0) {
+ unit.deathCount += entity.deathCount - 1;
+ }
} else {
units.push({
name: entity._name,
@@ -602,7 +619,7 @@ class UI {
}
}
}
- units.sort((a,b) => {
+ units.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
@@ -645,13 +662,15 @@ class UI {
this.statsDialog.classList.remove("closed");
};
- showModalAbout() {
+ showModalAbout () {
localizable(this.modalHeader, "info");
this.modalBody.innerHTML = `
Operation Capture And Playback
- GitHub Link
+ GitHub Link
+
+
@@ -663,25 +682,29 @@ class UI {
-
+
`;
+ localizable(document.getElementById("versionInfo-extension"), "version-extension");
+ document.getElementById("versionInfo-extension").innerHTML += this.extensionVersion;
+ localizable(document.getElementById("versionInfo-addon"), "version-addon");
+ document.getElementById("versionInfo-addon").innerHTML += this.addonVersion;
localizable(document.getElementById("keyControl-playPause"), "play-pause");
localizable(document.getElementById("keyControl-leftPanel"), "show-hide-left-panel");
localizable(document.getElementById("keyControl-rightPanel"), "show-hide-right-panel");
localizable(document.getElementById("keyControl-experimental"), "show-experimental");
localizable(document.getElementById("keyControl-lang"), "language");
- document.getElementById("switchLang").onchange = function(){switchLocalizable(this.value)};
+ document.getElementById("switchLang").onchange = function () { switchLocalizable(this.value) };
deleteLocalizable(this.modalBody);
this.modalButtons.innerHTML = "";
- this.modalButtons.appendChild(this.makeModalButton("Close", function() {
+ this.modalButtons.appendChild(this.makeModalButton("Close", function () {
ui.hideModal();
}));
this.showModal();
};
- showModalShare() {
+ showModalShare () {
this.modal.wasStopped = false;
if (!playbackPaused) {
this.modal.wasStopped = true;
@@ -700,12 +723,12 @@ class UI {
let line = document.getElementById("ShareLink");
line.value = text;
- line.addEventListener("click", function(event) {
+ line.addEventListener("click", function (event) {
this.select();
});
this.modalButtons.innerHTML = "";
- this.modalButtons.appendChild(this.makeModalButton(getLocalizable("close"), function() {
+ this.modalButtons.appendChild(this.makeModalButton(getLocalizable("close"), function () {
ui.hideModal();
if (ui.modal.wasStopped) {
playPause();
@@ -715,11 +738,11 @@ class UI {
this.showModal();
};
- showModal() {
+ showModal () {
this.modal.style.display = "inherit";
};
- hideModal() {
+ hideModal () {
this.modal.style.display = "none";
this.modalFilter.style.display = "none";
};
@@ -732,11 +755,11 @@ class UI {
this.playbackSpeedSlider.style.display = "inherit";
};
- hidePlaybackSpeedSlider() {
+ hidePlaybackSpeedSlider () {
this.playbackSpeedSlider.style.display = "none";
};
- removeEvent(event) {
+ removeEvent (event) {
var el = event.getElement();
el.classList.remove("reveal");
@@ -746,7 +769,7 @@ class UI {
}
};
- addEvent(event) {
+ addEvent (event) {
if (typeof event.updateTime === "function") {
event.updateTime();
}
@@ -767,24 +790,24 @@ class UI {
}
}
-/* if (event.type == "hit") {
- if (this.showHitEvents) {
- el.style.display = "inherit";
- } else {
- el.style.display = "none";
- };
- };*/
+ /* if (event.type == "hit") {
+ if (this.showHitEvents) {
+ el.style.display = "inherit";
+ } else {
+ el.style.display = "none";
+ };
+ };*/
this.filterEvent(event);
}
- updateEventTimes() {
+ updateEventTimes () {
for (const event of gameEvents.getActiveEvents()) {
event.updateTime();
}
}
- showHint(text) {
+ showHint (text) {
this.hint.textContent = text;
this.hint.style.display = "inherit";
@@ -793,7 +816,7 @@ class UI {
}, 5000);
};
- addTickToTimeline(frameNum) {
+ addTickToTimeline (frameNum) {
var frameWidth = this.frameSliderWidthInPercent / endFrame;
var tick = document.createElement("div");
@@ -803,7 +826,7 @@ class UI {
this.eventTimeline.appendChild(tick);
};
- filterEvent(event) {
+ filterEvent (event) {
var el = event.getElement();
var filterText = this.filterEventsInput.value.toLowerCase();
@@ -819,7 +842,7 @@ class UI {
el.style.display = "none";
} else if (el.innerHTML.toLowerCase().includes(filterText)) {
el.style.display = "inherit";
- //console.log("Matches filter (" + filterText + ")");
+ //console.log("Matches filter (" + filterText + ")");
} else {
el.style.display = "none";
}
@@ -848,13 +871,16 @@ class UI {
} else {
container.prepend(logo);
}
+
+ this.customizeLogo = logo;
}
this.disableKillCount = data.disableKillCount;
+ // this.useCloudTiles = data.useCloudTiles;
});
}
- showExperimental() {
+ showExperimental () {
this.statsButton.classList.remove("hiddenExperimental");
const container = document.getElementById("container");
container.classList.add("marker-transition");
diff --git a/static/scripts/ocap.unit.js b/static/scripts/ocap.unit.js
index 7db82d432..61ac9c50a 100644
--- a/static/scripts/ocap.unit.js
+++ b/static/scripts/ocap.unit.js
@@ -45,6 +45,10 @@ class Unit extends Entity {
sideClass = "unknown";
sideColour = "#b29900";
break;
+ case "LOGIC":
+ sideClass = "unknown";
+ sideColour = "#C3B1E1";
+ break;
}
this._sideClass = sideClass;
@@ -54,7 +58,7 @@ class Unit extends Entity {
this._markerRotationOrigin = "50% 60%";
}
- updateName(position) {
+ updateName (position) {
let content = "";
let name = position.name;
if (position.isPlayer === 0) {
@@ -62,12 +66,13 @@ class Unit extends Entity {
}
if (this._name !== name) {
this._name = name;
- this._marker.getPopup()._contentNode.textContent = name;
+ var popupNode = this._marker.getPopup()._contentNode
+ if (popupNode) { popupNode.textContent = name; }
this.updateElementText();
}
}
- updateElementText() {
+ updateElementText () {
let text = "";
if (this._role) {
text = `(${this._role}) ${this._name}`;
@@ -80,7 +85,7 @@ class Unit extends Entity {
this._element.textContent = text;
}
- createMarker(latLng) {
+ createMarker (latLng) {
super.createMarker(latLng);
// Only create a nametag label (popup) for players
@@ -93,14 +98,14 @@ class Unit extends Entity {
this._marker.bindPopup(popup).openPopup();
}
- _updateAtFrame(relativeFrameIndex) {
+ _updateAtFrame (relativeFrameIndex) {
super._updateAtFrame(relativeFrameIndex);
this.setIsInVehicle(this._positions[relativeFrameIndex].isInVehicle);
this.addCountList(this);
this.updateName(this._positions[relativeFrameIndex]);
}
- setIsInVehicle(isInVehicle) {
+ setIsInVehicle (isInVehicle) {
this._isInVehicle = isInVehicle;
if (isInVehicle) {
@@ -148,7 +153,7 @@ class Unit extends Entity {
// Check if unit fired on given frame
// If true, return position of projectile impact
- firedOnFrame(f) {
+ firedOnFrame (f) {
for (let i = 0; i < (this._framesFired.length - 1); i++) {
let frameNum = this._framesFired[i][0];
let projectilePos = this._framesFired[i][1];
@@ -157,16 +162,16 @@ class Unit extends Entity {
return;
}
- remove() {
+ remove () {
super.remove();
this._group.removeUnit(this);
}
- getSide() {
+ getSide () {
return this._side;
}
- makeElement(liTarget) { // Make and add element to UI target list
+ makeElement (liTarget) { // Make and add element to UI target list
let liUnit = document.createElement("li");
liUnit.className = "liUnit";
liUnit.addEventListener("click", () => {
@@ -181,11 +186,11 @@ class Unit extends Entity {
liTarget.appendChild(liUnit);
}
- getSideColour() { return this._sideColour }
+ getSideColour () { return this._sideColour }
- getSideClass() { return this._sideClass }
+ getSideClass () { return this._sideClass }
- setAlive(alive) {
+ setAlive (alive) {
super.setAlive(alive);
this._group.addUnit(this);
switch (alive) {
@@ -193,7 +198,7 @@ class Unit extends Entity {
this._element.style.opacity = 0.4;
break;
case 1:
- this._element.style.opacity = 1;
+ this._element.style.opacity = 1;
break;
case 2:
this._element.style.opacity = 0.8;
@@ -203,21 +208,21 @@ class Unit extends Entity {
}
}
- addCountList(unit) {
+ addCountList (unit) {
let side = unit._side;
- if (unit._alive != 1) {return}
+ if (unit._alive != 1) { return }
switch (side) {
case "WEST":
- countWest ++;
+ countWest++;
break;
case "EAST":
- countEast ++;
+ countEast++;
break;
case "GUER":
- countGuer ++;
+ countGuer++;
break;
case "CIV":
- countCiv ++;
+ countCiv++;
break;
default:
break;
diff --git a/static/scripts/ocap.vehicle.js b/static/scripts/ocap.vehicle.js
index 9c39b7cb3..733249d26 100644
--- a/static/scripts/ocap.vehicle.js
+++ b/static/scripts/ocap.vehicle.js
@@ -100,7 +100,7 @@ class Vehicle extends Entity {
let content = "";
if (ui.nicknameEnable) {
this._crew = crew;
- //this._marker.getPopup().setContent(`Test`); // Very slow (no need to recalc layout), use ._content instead
+ // this._marker.getPopup().setContent(`Test`); // Very slow (no need to recalc layout), use ._content instead
let crewLength = crew.length;
content = `${this._name.encodeHTMLEntities()} (0)`;
@@ -125,11 +125,16 @@ class Vehicle extends Entity {
}
}
}
- let popupNode = this._marker.getPopup()._contentNode;
-
- if (popupNode.innerHTML !== content) {
- popupNode.innerHTML = content;
+ let popup = this._marker.getPopup();
+ if (popup) {
+ // let popupContent = popup._content;
+
+ // if (popupContent !== content) {
+ // popupContent = content;
+ // }
+ popup.setContent(content);
}
+
}
getCrew() {
@@ -145,9 +150,9 @@ class Vehicle extends Entity {
let unit = entities.getById(unitId);
// Only include player names
- if (unit.isPlayer) {
+ // if (unit.isPlayer) {
str += (unit.getName().encodeHTMLEntities() + "
");
- }
+ // }
//};
});
return str;
diff --git a/static/style/index.css b/static/style/index.css
index 200d7620f..1c9e52635 100644
--- a/static/style/index.css
+++ b/static/style/index.css
@@ -1,8 +1,10 @@
-html, body, #map {
- width:100%;
- height:100%;
- margin:0;
- padding:0;
+html,
+body,
+#map {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
font-family: Roboto, Arial, sans-serif;
color: #F2F2F2;
overflow: hidden;
@@ -19,8 +21,8 @@ html, body, #map {
.customize.logo {
position: absolute;
- bottom: 65px;
- right: 5px;
+ bottom: 85px;
+ left: 375px;
z-index: 1;
opacity: 0.8;
}
@@ -54,6 +56,7 @@ html, body, #map {
background-repeat: no-repeat;
float: right;
}
+
#aboutButton {
font-size: 30px;
position: relative;
@@ -63,6 +66,7 @@ html, body, #map {
float: right;
margin: 12px;
}
+
#statsButton {
font-size: 30px;
position: relative;
@@ -103,7 +107,8 @@ html, body, #map {
display: inline-block;
}
-#leftPanel .panelContent, #rightPanel .panelContent {
+#leftPanel .panelContent,
+#rightPanel .panelContent {
overflow-y: auto;
overflow-x: auto;
height: calc(100% - 10px - 10px - 50px - 40px);
@@ -113,14 +118,17 @@ html, body, #map {
#controlSide {
height: 50px;
width: 350px;
- background-color: rgba(0,0,0,0.5);
+ background-color: rgba(0, 0, 0, 0.5);
}
-#sideWest, #sideEast, #sideGuer, #sideCiv {
- display : inline-block;
- width : calc(25% - 3px);
- height : 50px;
- cursor : pointer;
+#sideWest,
+#sideEast,
+#sideGuer,
+#sideCiv {
+ display: inline-block;
+ width: calc(25% - 3px);
+ height: 50px;
+ cursor: pointer;
text-align: center;
padding-top: 13px;
vertical-align: top;
@@ -130,8 +138,9 @@ html, body, #map {
height: calc(100% - 40px - 60px);
}
-#leftPanel, #rightPanel {
- background-color: rgba(0,0,0,0.75);
+#leftPanel,
+#rightPanel {
+ background-color: rgba(0, 0, 0, 0.75);
position: absolute;
top: 40px;
font-size: 14px;
@@ -149,8 +158,9 @@ html, body, #map {
height: 50%;
}
-#leftPanel .title, #rightPanel .title {
- background-color: rgba(0,0,0,0.5);
+#leftPanel .title,
+#rightPanel .title {
+ background-color: rgba(0, 0, 0, 0.5);
width: 100%;
height: 30px;
text-align: center;
@@ -159,13 +169,14 @@ html, body, #map {
}
.filterBox {
- background-color: rgba(0,0,0,0.25);
+ background-color: rgba(0, 0, 0, 0.25);
width: 100%;
height: 25px;
padding-top: 4px;
}
-.filterConnect, .filterHit {
+.filterConnect,
+.filterHit {
float: left;
display: inline-block;
height: 18px;
@@ -183,7 +194,8 @@ html, body, #map {
margin-left: 5px;
}
-#filterEventsInput, #filterUnitsInput {
+#filterEventsInput,
+#filterUnitsInput {
float: left;
display: inline-block;
padding-left: 2px;
@@ -202,25 +214,31 @@ html, body, #map {
width: 186px;
}
-#filterEventsInput, #filterUnitsInput::-webkit-input-placeholder {
+#filterEventsInput,
+#filterUnitsInput::-webkit-input-placeholder {
color: #666666;
}
-#filterEventsInput, #filterUnitsInput:focus {
+#filterEventsInput,
+#filterUnitsInput:focus {
outline: none;
}
-#leftPanel ul, #rightPanel ul {
+#leftPanel ul,
+#rightPanel ul {
padding: 0;
list-style-type: none;
}
-#listWest, #listGuer, #listEast, #listCiv {
+#listWest,
+#listGuer,
+#listEast,
+#listCiv {
display: none;
}
.extraInfoBox {
- background-color: rgba(0,0,0,0.75);
+ background-color: rgba(0, 0, 0, 0.75);
position: absolute;
right: 0;
bottom: 40px;
@@ -234,7 +252,7 @@ html, body, #map {
}
#bottomPanel {
- background-color: rgba(0,0,0,0.8);
+ background-color: rgba(0, 0, 0, 0.8);
position: absolute;
bottom: 0;
left: 0;
@@ -249,14 +267,24 @@ html, body, #map {
height: 100%;
}
-#timecodeContainer, #toggleFirelines, #toggleNickname, #toggleMapMarker, #versionInfoContainer, #playbackSpeedSliderContainer, #playbackSpeedSliderContainer, .fullscreenButton {
+#timecodeContainer,
+#toggleFirelines,
+#toggleNickname,
+#toggleMapMarker,
+#versionInfoContainer,
+#playbackSpeedSliderContainer,
+#playbackSpeedSliderContainer,
+.fullscreenButton {
margin-top: 5px;
margin-left: 10px;
margin-right: 10px;
display: inline-block;
}
-#toggleFirelines, #toggleMapMarker, #toggleNickname, .fullscreenButton {
+#toggleFirelines,
+#toggleMapMarker,
+#toggleNickname,
+.fullscreenButton {
background-image: url("../images/bullets.svg");
background-size: auto 20px;
height: 20px;
@@ -265,6 +293,7 @@ html, body, #map {
float: right;
cursor: pointer;
}
+
.toggleTime {
float: right;
height: 26px;
@@ -312,13 +341,16 @@ html, body, #map {
height: 16px;
padding: 3px 0;
}
+
#frameSliderContainer .frameSliderContainer2 {
position: relative;
bottom: 14px;
}
+
#frameSliderContainer .frameSliderContainer2 input::-moz-range-track {
background-color: transparent;
}
+
#frameSliderContainer .frameSliderContainer2 input::-webkit-slider-runnable-track {
background-color: transparent;
}
@@ -383,6 +415,7 @@ html, body, #map {
#playbackSpeedSlider::-webkit-slider-thumb {
background-color: #404040;
}
+
#playbackSpeedSlider::-moz-range-thumb {
background-color: #404040;
}
@@ -407,8 +440,10 @@ html, body, #map {
}
input[type=range] {
- -webkit-appearance: none; /* Hides the slider so that custom slider can be made */
- width: 100%; /* Specific width is required for Firefox. */
+ -webkit-appearance: none;
+ /* Hides the slider so that custom slider can be made */
+ width: 100%;
+ /* Specific width is required for Firefox. */
background: transparent;
}
@@ -444,10 +479,12 @@ input[type=range]::-webkit-slider-runnable-track {
border-bottom: 1px solid rgba(0, 0, 0, 0.3);
opacity: 0;
}
+
.liEvent.reveal {
opacity: 1;
transition: opacity 1s;
}
+
.liEvent.action {
cursor: pointer;
}
@@ -467,8 +504,8 @@ input[type=range]::-webkit-slider-runnable-track {
}
.liUnit:hover {
- background-color: rgb(205,134,20);
- background-color: rgba(205,134,20,0.9);
+ background-color: rgb(205, 134, 20);
+ background-color: rgba(205, 134, 20, 0.9);
}
.sideTitle {
@@ -501,27 +538,30 @@ input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 100%;
overflow: auto;
- background-color: rgba(0,0,0,0.4);
+ background-color: rgba(0, 0, 0, 0.4);
}
+
.modalContent {
background: none;
margin: 10% auto auto;
/* border: 1px solid black;*/
width: 1200px;
- height: 65%;
+ height: 65%;
}
+
.modalHeader {
box-sizing: border-box;
font-size: 16px;
- background-color: rgba(155,0,0,0.9);
+ background-color: rgba(155, 0, 0, 0.9);
width: 100%;
margin-bottom: 3px;
padding: 5px 10px;
font-weight: 500;
}
+
.modalFilter {
box-sizing: border-box;
- background-color: rgba(0,0,0,0.6);
+ background-color: rgba(0, 0, 0, 0.6);
width: 100%;
height: 50px;
padding: 4px 16px;
@@ -529,9 +569,10 @@ input[type=range]::-webkit-slider-runnable-track {
overflow-y: auto;
overflow-x: auto;
}
+
.modalBody {
box-sizing: border-box;
- background-color: rgba(0,0,0,0.6);
+ background-color: rgba(0, 0, 0, 0.6);
width: 100%;
min-height: 200px;
max-height: 400px;
@@ -548,22 +589,25 @@ input[type=range]::-webkit-slider-runnable-track {
top: 0;
height: 100vh;
width: 100vw;
- background-color: rgba(0,0,0,0.4);
+ background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
+
.modalDialog.closed {
display: none;
}
+
.modalDialog .dialogBase {
position: relative;
z-index: 601;
display: flex;
flex-direction: column
}
+
.modalDialog .dialogHeader {
- background-color: rgba(155,0,0,0.9);
+ background-color: rgba(155, 0, 0, 0.9);
margin-bottom: 3px;
color: white;
border-bottom: 1px solid black;
@@ -572,32 +616,40 @@ input[type=range]::-webkit-slider-runnable-track {
font-weight: 500;
font-size: 16px;
}
+
.modalDialog .dialogBody {
background-color: #000000ed;
overflow-y: scroll;
min-width: 1300px;
- min-height: 700px; /* title bar is 50 so we need the extra 70 here */
- max-height: calc(75vh - 50px); /* compensate for title bar */
+ min-height: 700px;
+ /* title bar is 50 so we need the extra 70 here */
+ max-height: calc(75vh - 50px);
+ /* compensate for title bar */
max-width: 75vw;
- flex:1;
+ flex: 1;
padding: 5px;
}
+
.modalDialog .dialogFooter {
margin-top: 3px;
}
+
.modalDialog .dialogFooter .modalButton {
float: right;
}
#ShareLink {
- width:100%;
- padding:10px 0;
- color:white;
- background-color:rgba(0,0,0,0.4);
- border:1px solid black;
+ width: 100%;
+ padding: 10px 0;
+ color: white;
+ background-color: rgba(0, 0, 0, 0.4);
+ border: 1px solid black;
}
-.filterTagGameInput, #filterGameInput, #calendar1, #calendar2 {
+.filterTagGameInput,
+#filterGameInput,
+#calendar1,
+#calendar2 {
margin: 4px;
}
@@ -639,22 +691,27 @@ input[type=range]::-webkit-slider-runnable-track {
border: none;
width: 100%;
}
+
.modalBody th {
- background-color: rgba(0,0,0,0.7);
+ background-color: rgba(0, 0, 0, 0.7);
padding: 3px;
font-weight: 500;
}
+
.modalBody td {
padding-right: 20px;
padding: 3px;
}
+
.modalBody tr {
text-align: left;
}
+
.modalBody tr:hover {
- background-color: rgb(205,134,20);
- background-color: rgba(205,134,20,0.9);
+ background-color: rgb(205, 134, 20);
+ background-color: rgba(205, 134, 20, 0.9);
}
+
.modalButtons {
box-sizing: border-box;
width: 100%;
@@ -663,13 +720,14 @@ input[type=range]::-webkit-slider-runnable-track {
margin-top: 3px;
height: 15px;
}
+
.modalButton {
display: inline-block;
background-color: none;
height: 100%;
min-width: 100px;
- background-color: rgba(0,0,0);
- background-color: rgba(0,0,0,0.7);
+ background-color: rgba(0, 0, 0);
+ background-color: rgba(0, 0, 0, 0.7);
margin-right: 10px;
padding: 5px;
text-transform: uppercase;
@@ -687,8 +745,8 @@ input[type=range]::-webkit-slider-runnable-track {
.hint {
display: none;
- background-color: rgb(0,0,0);
- background-color: rgba(0,0,0,0.7);
+ background-color: rgb(0, 0, 0);
+ background-color: rgba(0, 0, 0, 0.7);
position: absolute;
margin: auto;
left: 0;
@@ -728,50 +786,138 @@ input[type=range]::-webkit-slider-runnable-track {
/* Override leaflet styles */
/* */
-.leaflet-right {
- right: 18%;
- bottom: 4%;
-}
+/* .leaflet-top {
+ top: 55%;
+} */
-.leaflet-tile {
- /*border: solid black 1px;*/
+/* .leaflet-right {
+ right: 1%;
+} */
+
+.leaflet-left {
+ left: 360px;
+ top: 50px;
}
-.leaflet-tile-container {
- image-rendering: pixelated; /* 'pixelated' preserves sharpness of tiles */
+.leaflet-bottom {
+ bottom: 65px;
}
+/* .leaflet-control-attribution {
+ right: 0px;
+ bottom: 0px;
+} */
+
+/* this was custom positioning code for the basemaps control */
+/* .basemaps {
+ bottom: 223px;
+} */
+
+/* .leaflet-tile { */
+/* border: solid black 1px; */
+/* } */
+
+/* .leaflet-tile-container { */
+/* 'pixelated' preserves sharpness of tiles */
+/* image-rendering: pixelated; */
+/* } */
+
+
.leaflet-popup {
pointer-events: none;
}
-#container.marker-transition:not(.zooming) .leaflet-marker-icon.animation { transition: transform .15s linear; }
-#container.marker-transition.speed-9:not(.zooming) .leaflet-marker-icon.animation { transition: transform .2s linear; }
-#container.marker-transition.speed-8:not(.zooming) .leaflet-marker-icon.animation { transition: transform .3s linear; }
-#container.marker-transition.speed-7:not(.zooming) .leaflet-marker-icon.animation { transition: transform .4s linear; }
-#container.marker-transition.speed-6:not(.zooming) .leaflet-marker-icon.animation { transition: transform .5s linear; }
-#container.marker-transition.speed-5:not(.zooming) .leaflet-marker-icon.animation { transition: transform .6s linear; }
-#container.marker-transition.speed-4:not(.zooming) .leaflet-marker-icon.animation { transition: transform .7s linear; }
-#container.marker-transition.speed-3:not(.zooming) .leaflet-marker-icon.animation { transition: transform .8s linear; }
-#container.marker-transition.speed-2:not(.zooming) .leaflet-marker-icon.animation { transition: transform .9s linear; }
-#container.marker-transition.speed-1:not(.zooming) .leaflet-marker-icon.animation { transition: transform 1s linear; }
-#container.marker-transition:not(.zooming) .leaflet-popup.animation { transition: transform .15s linear !important; }
-#container.marker-transition.speed-9:not(.zooming) .leaflet-popup.animation { transition: transform .2s linear !important; }
-#container.marker-transition.speed-8:not(.zooming) .leaflet-popup.animation { transition: transform .3s linear !important; }
-#container.marker-transition.speed-7:not(.zooming) .leaflet-popup.animation { transition: transform .4s linear !important; }
-#container.marker-transition.speed-6:not(.zooming) .leaflet-popup.animation { transition: transform .5s linear !important; }
-#container.marker-transition.speed-5:not(.zooming) .leaflet-popup.animation { transition: transform .6s linear !important; }
-#container.marker-transition.speed-4:not(.zooming) .leaflet-popup.animation { transition: transform .7s linear !important; }
-#container.marker-transition.speed-3:not(.zooming) .leaflet-popup.animation { transition: transform .8s linear !important; }
-#container.marker-transition.speed-2:not(.zooming) .leaflet-popup.animation { transition: transform .9s linear !important; }
-#container.marker-transition.speed-1:not(.zooming) .leaflet-popup.animation { transition: transform 1s linear !important; }
+#container.marker-transition:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .15s linear;
+}
+
+#container.marker-transition.speed-9:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .2s linear;
+}
+
+#container.marker-transition.speed-8:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .3s linear;
+}
+
+#container.marker-transition.speed-7:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .4s linear;
+}
+
+#container.marker-transition.speed-6:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .5s linear;
+}
+
+#container.marker-transition.speed-5:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .6s linear;
+}
+
+#container.marker-transition.speed-4:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .7s linear;
+}
+
+#container.marker-transition.speed-3:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .8s linear;
+}
+
+#container.marker-transition.speed-2:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform .9s linear;
+}
+
+#container.marker-transition.speed-1:not(.zooming) .leaflet-marker-icon.animation {
+ transition: transform 1s linear;
+}
+
+#container.marker-transition:not(.zooming) .leaflet-popup.animation {
+ transition: transform .15s linear !important;
+}
+
+#container.marker-transition.speed-9:not(.zooming) .leaflet-popup.animation {
+ transition: transform .2s linear !important;
+}
+
+#container.marker-transition.speed-8:not(.zooming) .leaflet-popup.animation {
+ transition: transform .3s linear !important;
+}
+
+#container.marker-transition.speed-7:not(.zooming) .leaflet-popup.animation {
+ transition: transform .4s linear !important;
+}
+
+#container.marker-transition.speed-6:not(.zooming) .leaflet-popup.animation {
+ transition: transform .5s linear !important;
+}
+
+#container.marker-transition.speed-5:not(.zooming) .leaflet-popup.animation {
+ transition: transform .6s linear !important;
+}
+
+#container.marker-transition.speed-4:not(.zooming) .leaflet-popup.animation {
+ transition: transform .7s linear !important;
+}
+
+#container.marker-transition.speed-3:not(.zooming) .leaflet-popup.animation {
+ transition: transform .8s linear !important;
+}
+
+#container.marker-transition.speed-2:not(.zooming) .leaflet-popup.animation {
+ transition: transform .9s linear !important;
+}
+
+#container.marker-transition.speed-1:not(.zooming) .leaflet-popup.animation {
+ transition: transform 1s linear !important;
+}
.leaflet-popup-content-wrapper {
+ /* background: white; */
background: none;
box-shadow: none;
padding: 0;
margin: 0;
- margin-top: 10px;
+ /* background-blend-mode: screen; */
+ /* box-shadow: inset; */
+ /* opacity: 0.65; */
+ /* padding-right: 2px; */
+ /* margin-top: 10px; */
}
.leaflet-popup-tip {
@@ -779,10 +925,13 @@ input[type=range]::-webkit-slider-runnable-track {
}
.leaflet-popup-content {
+ color: white;
+ text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
padding: 0;
margin: 0;
font-weight: bold;
font-size: 12px;
+ /* width: 100%; */
}
.leaflet-div-icon {
@@ -793,7 +942,10 @@ input[type=range]::-webkit-slider-runnable-track {
.stats .name {
width: 200px;
}
-.stats .kills, .stats .tkills, .stats .deaths {
+
+.stats .kills,
+.stats .tkills,
+.stats .deaths {
width: 70px;
text-align: right;
}