Source: filters.js

[No frames]
/**
 * @fileOverview Provides a factory of configurable entity filters.
 */

/**
 * @class
 * @summary A class-factory for entity-collections using filters.
 */
API3.Filters = {

	/**
	 * @summary Creates a filtering by entity type (template).
	 * @param type {String} The instantiated name of an entity
	 *                      template, e.g.
	 *                      <code>"units/viking_longboat"</code>.
	 * @return {API3.Filter} A filter object which selects entities of
	 *                       the specified template type.
	 */
	byType: function(type){
		return {"func" : function(ent){
			return ent.templateName() === type;
		},
		"dynamicProperties": []};
	},

	/**
	 * @summary Creates a filtering by class tag.
	 * @description A class tag is an identifier which describes a
	 *              category of entities. These tags are defined at the
	 *              [template level]{@link API3.Template} and cannot be
	 *              changed by the AI.
	 * @param cls {String} The name of an entity class to match, e.g.
	 *                     <code>"worker"</code>
	 * @return {API3.Filter} A filter object which selects only those
	 *                       entities belonging to the specified
	 *                       class.
	 */
	byClass: function(cls){
		return {"func" : function(ent){
			return ent.hasClass(cls);
		},
		"dynamicProperties": []};
	},
	
	byClassesAnd: function(clsList){
		return {"func" : function(ent){
			let ret = true;
			for (var i = 0; i < clsList.length; i++)
				ret = ret && ent.hasClass(clsList[i]);
			return ret;
		}, 
		"dynamicProperties": []};
	},
	
	byClassesOr: function(clsList){
		return {"func" : function(ent){
			let ret = false;
			for (var i = 0; i < clsList.length; i++)
				ret = ret || ent.hasClass(clsList[i]);
			return ret;
		}, 
		"dynamicProperties": []};
	},

	byMetadata: function(player, key, value){
		return {"func" : function(ent){
			return (ent.getMetadata(player, key) == value);
		}, 
		"dynamicProperties": ['metadata.' + key]};
	},

	// can be used for stuffs which won't change once entities are created.
	byStaticMetadata: function(player, key, value){
		return {"func" : function(ent){
			return (ent.getMetadata(player, key) == value);
		},
			"dynamicProperties": []};
	},
	
	byHasMetadata: function(player, key){
		return {"func" : function(ent){
			return (ent.getMetadata(player, key) != undefined);
		},
			"dynamicProperties": ['metadata.' + key]};
	},

	and: function(filter1, filter2){
		return {"func": function(ent){
			return filter1.func(ent) && filter2.func(ent);
		}, 
		"dynamicProperties": filter1.dynamicProperties.concat(filter2.dynamicProperties)};
	},
	
	or: function(filter1, filter2){
		return {"func" : function(ent){
			return filter1.func(ent) || filter2.func(ent);
		}, 
		"dynamicProperties": filter1.dynamicProperties.concat(filter2.dynamicProperties)};
	},
	
	not: function(filter){
		return {"func": function(ent){
			return !filter.func(ent);
		},
		"dynamicProperties": filter.dynamicProperties};
	},
	
	byOwner: function(owner){
		return {"func" : function(ent){
			return (ent.owner() === owner);
		}, 
		"dynamicProperties": ['owner']};
	},
	
	byNotOwner: function(owner){
		return {"func" : function(ent){
			return (ent.owner() !== owner);
		}, 
		"dynamicProperties": ['owner']};
	},
	
	byOwners: function(owners){
		return {"func" : function(ent){
			for (var i = 0; i < owners.length; i++)
				if (ent.owner() === owners[i])
					return true;
			return false;
		}, 
		"dynamicProperties": ['owner']};
	},

	byCanGarrison: function(){
		return {"func" : function(ent){
			return ent.garrisonMax() > 0;
		},
			"dynamicProperties": []};
	},
	byTrainingQueue: function(){
		return {"func" : function(ent){
			return ent.trainingQueue();
		}, 
		"dynamicProperties": ['trainingQueue']};
	},
	byResearchAvailable: function(){
		return {"func" : function(ent){
			return ent.researchableTechs() !== undefined;
		},
			"dynamicProperties": []};
	},
	byTargetedEntity: function(targetID){
		return {"func": function(ent) {
			return (ent.unitAIOrderData().length && ent.unitAIOrderData()[0]["target"] && ent.unitAIOrderData()[0]["target"] == targetID);
		},
			"dynamicProperties": ['unitAIOrderData']};
	},
	
	byCanAttack: function(saidClass){
		return {"func" : function(ent){
			return ent.canAttackClass(saidClass);
		},
			"dynamicProperties": []};
	},
	
	isGarrisoned: function(){
		return {"func" : function(ent){
			return ent.position() === undefined;
		},
			"dynamicProperties": []};
	},

	isSoldier: function(){
		return {"func" : function(ent){
			return Filters.byClassesOr(["CitizenSoldier", "Super"])(ent);
		}, 
		"dynamicProperties": []};
	},
	
	isIdle: function(){
		return {"func" : function(ent){
			return ent.isIdle();
		}, 
		"dynamicProperties": ['idle']};
	},
	
	isFoundation: function(){
		return {"func": function(ent){
			return ent.foundationProgress() !== undefined;
		},
		"dynamicProperties": []};
	},
	
	byDistance: function(startPoint, dist){
		return {"func": function(ent){
			if (ent.position() === undefined)
				return false;
			else
				return (API3.SquareVectorDistance(startPoint, ent.position()) < dist*dist);
		},
		"dynamicProperties": ['position']};
	},
	
	// Distance filter with no auto updating, use with care 
	byStaticDistance: function(startPoint, dist){
		return {"func": function(ent){
			if (!ent.position())
				return false;
			else
				return (API3.SquareVectorDistance(startPoint, ent.position()) < dist*dist);
		},
		"dynamicProperties": []};
	},
	
	byTerritory: function(Map, territoryIndex){
		return {"func": function(ent){
			if (Map.point(ent.position()) == territoryIndex)
				return true;
			else
				return false;
		},
		"dynamicProperties": ['position']};
	},

	isDropsite: function(resourceType){
		return {"func": function(ent){
			return (ent.resourceDropsiteTypes() && (resourceType === undefined || ent.resourceDropsiteTypes().indexOf(resourceType) !== -1));
		},
		"dynamicProperties": []};
	},
	
	byResource: function(resourceType){
		return {"func" : function(ent){
			var type = ent.resourceSupplyType();
			if (!type)
				return false;
			var amount = ent.resourceSupplyMax();
			if (!amount)
				return false;
			
			// Skip targets that are too hard to hunt
			if (ent.isUnhuntable())
				return false;
			
			// And don't go for the bloody fish! TODO: better accessibility checks 
			if (ent.hasClass("SeaCreature"))
				return false;
			
			// Don't go for floating treasures since we won't be able to reach them and it kills the pathfinder.
			if (ent.templateName() == "other/special_treasure_shipwreck_debris" || 
					ent.templateName() == "other/special_treasure_shipwreck" )
				return false;
			
			if (type.generic == "treasure")
				return (resourceType == type.specific);
			else
				return (resourceType == type.generic);
		},
		"dynamicProperties": []};
	},

	isHuntable: function(){
		return {"func" : function(ent){
			if (!ent.hasClass("Animal"))
				return false;
			var amount = ent.resourceSupplyMax();
			if (!amount)
				return false;
			// Skip targets that are too hard to hunt
			if (!ent.isHuntable())
				return false;
			// And don't go for the fish! TODO: better accessibility checks 
			if (ent.hasClass("SeaCreature"))
				return false;
			return true;
		},
		"dynamicProperties": []};
	},

	isFishable: function(){
		return {"func" : function(ent){
			if (ent.get("UnitMotion"))   // temporarily do not fish moving fish (i.e. whales)
				return false;
			if (ent.hasClass("SeaCreature") && ent.resourceSupplyMax())
				return true;
			return false;
		},
		"dynamicProperties": []};
	}
};

// -------------------------------------------------------------------
//  End of file
// -------------------------------------------------------------------