@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
302 lines (229 loc) • 5.67 kB
JavaScript
import { assert } from "../../../core/assert.js";
import { isArrayEqualStrict } from "../../../core/collection/array/isArrayEqualStrict.js";
import { computeStringHash } from "../../../core/primitives/strings/computeStringHash.js";
/**
* Stores textual tags, useful for tagging entities.
*/
export class Tag {
/**
* @private
* @type {string[]}
*/
values = [];
/**
*
* @returns {number}
*/
get count() {
return this.values.length;
}
/**
*
* @param {number} i
* @returns {string}
*/
get(i) {
assert.isNonNegativeInteger(i, 'i');
return this.values[i];
}
clear() {
this.values.splice(0, this.values.length);
}
/**
* Once the tag is added to the dataset it should be considered immutable, hence why this method is protected
* @param {string} value
* @returns {boolean}
*/
add(value) {
assert.isString(value, 'value');
if (this.contains(value)) {
return false;
}
this.values.push(value);
return true;
}
/**
* Add multiple tags
* @param {string[]} values
*/
addAll(values) {
assert.isArray(values,'values');
const n = values.length;
for (let i = 0; i < n; i++) {
const value = values[i];
this.add(value);
}
}
/**
*
* @returns {string}
*/
getFirst() {
return this.values[0];
}
/**
*
* @param {string} value
* @returns {boolean}
*/
contains(value) {
assert.isString(value, 'value');
return this.values.indexOf(value) !== -1;
}
/**
*
* @param {string[]} values
*/
containsAll(values) {
assert.isArray(values, 'values');
const search_value_count = values.length;
const these_values = this.values;
const these_values_count = these_values.length;
let i = 0, j = 0;
main: for (; i < search_value_count; i++) {
const v = values[i];
for (j = 0; j < these_values_count; j++) {
const tag = these_values[j];
if (tag === v) {
continue main;
}
}
//tag not found
return false;
}
return true;
}
/**
*
* @param {string[]} values
* @returns {boolean}
*/
containsOneOf(values) {
const s0 = this.values;
const s1 = values;
const n0 = s0.length;
const n1 = s1.length;
for (let i = 0; i < n0; i++) {
const v0 = s0[i];
for (let j = 0; j < n1; j++) {
const v1 = s1[j];
if (v0 === v1) {
return true;
}
}
}
return false;
}
/**
* NOTE: do not modify this value
* @returns {string[]}
*/
getValues() {
return this.values;
}
/**
*
* @param {function(string)} visitor
* @param {*} [thisArg]
*/
traverse(visitor, thisArg) {
this.values.forEach(visitor, thisArg);
}
/**
*
* @return {number}
*/
hash() {
let hash = 0;
const values = this.values;
const n = values.length;
for (let i = 0; i < n; i++) {
const value = values[i];
hash = ((hash << 5) - hash) + computeStringHash(value);
hash |= 0; // Convert to 32bit integer
}
return hash;
}
/**
*
* @param {Tag} other
* @return {boolean}
*/
equals(other) {
return isArrayEqualStrict(this.values, other.values);
}
toJSON() {
return this.values;
}
/**
*
* @param {string[]|string} json
*/
fromJSON(json) {
this.clear();
if (typeof json === "string") {
this.add(json);
} else {
assert.isArray(json, 'json');
const n = json.length;
for (let i = 0; i < n; i++) {
this.add(json[i]);
}
}
}
/**
* Find all entities that contain specified tags. Entity must have every tag to qualify
* @param {string[]} tags
* @param {EntityComponentDataset} ecd
* @returns {number[]} entities
*/
static find(tags, ecd) {
/**
*
* @type {number[]}
*/
const result = [];
ecd.traverseComponents(Tag, (tag, entity) => {
if (tag.containsAll(tags)) {
result.push(entity);
}
});
return result;
}
/**
*
* @param {string} tag
* @returns {Tag}
*/
static fromOne(tag) {
const r = new Tag();
r.add(tag);
return r;
}
/**
*
* @param json
* @returns {Tag}
*/
static fromJSON(json) {
const r = new Tag();
r.fromJSON(json);
return r;
}
/**
* Utility constructor
* @param {string} label
* @return {Tag}
*/
static from(...label){
const r = new Tag();
r.addAll(label);
return r;
}
}
/**
* @readonly
* @type {string}
*/
Tag.typeName = "Tag";
export default Tag;