forked from le-jeu/iitc-plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
redfection: base from filters branch
- Loading branch information
Showing
6 changed files
with
403 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* Filters API | ||
Filters work by exclusion, following the old layer system. | ||
A feature that matches a filter is removed from the map. | ||
A filter applies to a combinaison of portal/link/field and is described by | ||
- data properties that must (all) match | ||
- or a predicate for complex filter | ||
{ portal: true, link: true, data: { team: 'E' }} | ||
filters any ENL portal/link | ||
[{ link: true, data: { oGuid: "some guid" }}, { link: true, data: { dGuid: "some guid" }}] | ||
filters any links on portal with guid "some guid" | ||
{ field: true, pred: function (f) { return f.options.timestamp < Date.parse('2021-10-31'); } } | ||
filters any fields made before Halloween 2021 | ||
*/ | ||
|
||
/** | ||
* @type {Object.<string, FilterDesc>} | ||
*/ | ||
const _filters = {}; | ||
|
||
/** | ||
* @callback FilterPredicate | ||
* @param {Object} ent - IITC entity | ||
* @returns {boolean} | ||
*/ | ||
|
||
/** | ||
* @typedef FilterDesc | ||
* @type {object} | ||
* @property {boolean} filterDesc.portal apply to portal | ||
* @property {boolean} filterDesc.link apply to link | ||
* @property {boolean} filterDesc.field apply to field | ||
* @property {object} [filterDesc.data] entity data properties that must match | ||
* @property {FilterPredicate} [filterDesc.pred] predicate on the entity | ||
*/ | ||
|
||
/** | ||
* @param {string} name filter name | ||
* @param {FilterDesc | FilterDesc[]} filterDesc filter description (OR) | ||
*/ | ||
export function set(name, filterDesc) { | ||
_filters[name] = filterDesc; | ||
} | ||
|
||
export function has(name) { | ||
return name in _filters; | ||
} | ||
|
||
export function remove(name) { | ||
return delete _filters[name]; | ||
} | ||
|
||
function simpleFilter(type, entity, filter) { | ||
// type must match | ||
if (!filter[type]) return false; | ||
// use predicate if available | ||
if (typeof filter.pred === 'function') return filter.pred(entity); | ||
// if no constraint, match | ||
if (!filter.data) return true; | ||
// else must match all constraints | ||
for (const prop in filter.data) if (entity.options.data[prop] !== filter.data[prop]) return false; | ||
return true; | ||
} | ||
|
||
function arrayFilter(type, entity, filters) { | ||
if (!Array.isArray(filters)) filters = [filters]; | ||
filters = filters.flat(); | ||
for (let i = 0; i < filters.length; i++) if (simpleFilter(type, entity, filters[i])) return true; | ||
return false; | ||
} | ||
|
||
/** | ||
* | ||
* @param {object} portal Portal to test | ||
* @returns {boolean} `true` if the the portal matches one of the filters | ||
*/ | ||
export function filterPortal(portal) { | ||
return arrayFilter('portal', portal, Object.values(_filters)); | ||
} | ||
|
||
/** | ||
* | ||
* @param {object} link Link to test | ||
* @returns {boolean} `true` if the the link matches one of the filters | ||
*/ | ||
export function filterLink(link) { | ||
return arrayFilter('link', link, Object.values(_filters)); | ||
} | ||
|
||
/** | ||
* | ||
* @param {object} field Field to test | ||
* @returns {boolean} `true` if the the field matches one of the filters | ||
*/ | ||
export function filterField(field) { | ||
return arrayFilter('field', field, Object.values(_filters)); | ||
} | ||
|
||
export function filterEntities() { | ||
for (const guid in window.portals) { | ||
const p = window.portals[guid]; | ||
if (filterPortal(p)) p.remove(); | ||
else p.addTo(window.map); | ||
} | ||
for (const guid in window.links) { | ||
const link = window.links[guid]; | ||
if (filterLink(link)) link.remove(); | ||
else link.addTo(window.map); | ||
} | ||
for (const guid in window.fields) { | ||
const field = window.fields[guid]; | ||
if (filterField(field)) field.remove(); | ||
else field.addTo(window.map); | ||
} | ||
} | ||
|
||
/** | ||
* @class FilterLayer | ||
* @description Layer abstraction to control with the layer chooser a filter. | ||
* The filter is disabled on layer add, and enabled on layer remove. | ||
* @extends L.Layer | ||
* @param {{name: string, filter: FilterDesc}} options | ||
*/ | ||
export const FilterLayer = L.Layer.extend({ | ||
options: { | ||
name: null, | ||
filter: {}, | ||
}, | ||
|
||
initialize: function (options) { | ||
L.setOptions(this, options); | ||
set(this.options.name, this.options.filter); | ||
}, | ||
|
||
onAdd: function () { | ||
remove(this.options.name); | ||
filterEntities(); | ||
}, | ||
|
||
onRemove: function () { | ||
set(this.options.name, this.options.filter); | ||
filterEntities(); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { createDefaultOverlays } from './map'; | ||
import { | ||
bringPortalsToFront, | ||
createFieldEntity, | ||
createLinkEntity, | ||
deletePortalEntity, | ||
deleteLinkEntity, | ||
deleteFieldEntity, | ||
addPortalToMapLayer, | ||
removePortalFromMapLayer, | ||
} from './map_data_render'; | ||
|
||
function setup() { | ||
window.Render.prototype.bringPortalsToFront = bringPortalsToFront; | ||
window.Render.prototype.createLinkEntity = createLinkEntity; | ||
window.Render.prototype.createFieldEntity = createFieldEntity; | ||
window.Render.prototype.deletePortalEntity = deletePortalEntity; | ||
window.Render.prototype.deleteLinkEntity = deleteLinkEntity; | ||
window.Render.prototype.deleteFieldEntity = deleteFieldEntity; | ||
window.Render.prototype.addPortalToMapLayer = addPortalToMapLayer; | ||
window.Render.prototype.removePortalFromMapLayer = removePortalFromMapLayer; | ||
|
||
const LayerChooser = window.LayerChooser; | ||
window.LayerChooser = LayerChooser.extend({ | ||
initialize: function (baseLayers, overlays, options) { | ||
for (const key in overlays) delete overlays[key]; | ||
const newOverlays = createDefaultOverlays(); | ||
for (const key in newOverlays) overlays[key] = newOverlays[key]; | ||
LayerChooser.prototype.initialize.apply(this, arguments); | ||
}, | ||
}); | ||
} | ||
|
||
setup.priority = 'boot'; | ||
|
||
export default setup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { FilterLayer } from './filters'; | ||
|
||
export function createDefaultOverlays() { | ||
var addLayers = {}; | ||
|
||
var portalsLayers = []; | ||
portalsLayers[0] = new FilterLayer({ | ||
name: 'Unclaimed/Placeholder Portals', | ||
filter: [ | ||
{ portal: true, data: { team: 'N' } }, | ||
{ portal: true, data: { level: undefined } }, | ||
], | ||
}); | ||
addLayers['Unclaimed/Placeholder Portals'] = portalsLayers[0]; | ||
for (var i = 1; i <= 8; i++) { | ||
var t = 'Level ' + i + ' Portals'; | ||
portalsLayers[i] = new FilterLayer({ | ||
name: t, | ||
filter: [ | ||
{ portal: true, data: { level: i, team: 'R' } }, | ||
{ portal: true, data: { level: i, team: 'E' } }, | ||
], | ||
}); | ||
addLayers[t] = portalsLayers[i]; | ||
} | ||
|
||
var fieldsLayer = new FilterLayer({ | ||
name: 'Fields', | ||
filter: { field: true }, | ||
}); | ||
addLayers['Fields'] = fieldsLayer; | ||
|
||
var linksLayer = new FilterLayer({ | ||
name: 'Links', | ||
filter: { link: true }, | ||
}); | ||
addLayers['Links'] = linksLayer; | ||
|
||
// faction-specific layers | ||
var resistanceLayer = new FilterLayer({ | ||
name: 'Resistance', | ||
filter: { portal: true, link: true, field: true, data: { team: 'R' } }, | ||
}); | ||
var enlightenedLayer = new FilterLayer({ | ||
name: 'Enlightened', | ||
filter: { portal: true, link: true, field: true, data: { team: 'E' } }, | ||
}); | ||
|
||
// to avoid any favouritism, we'll put the player's own faction layer first | ||
if (window.PLAYER.team === 'RESISTANCE') { | ||
addLayers['Resistance'] = resistanceLayer; | ||
addLayers['Enlightened'] = enlightenedLayer; | ||
} else { | ||
addLayers['Enlightened'] = enlightenedLayer; | ||
addLayers['Resistance'] = resistanceLayer; | ||
} | ||
|
||
// compatibility | ||
addLayers.Neutral = L.layerGroup(); | ||
|
||
return addLayers; | ||
} |
Oops, something went wrong.