Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/ModifiersManager.js
- This file was added.
function ModifiersManager() {} | |||||
ModifiersManager.prototype.Schema = | |||||
"<empty/>"; | |||||
ModifiersManager.prototype.Serialize = function() | |||||
{ | |||||
// The cache will be affected by property reads from the GUI and other places so we shouldn't | |||||
// serialize it. | |||||
var ret = {}; | |||||
for (var i in this) | |||||
{ | |||||
if (this.hasOwnProperty(i)) | |||||
ret[i] = this[i]; | |||||
} | |||||
ret.cachedValues = new Map(); | |||||
return ret; | |||||
}; | |||||
ModifiersManager.prototype.Init = function() | |||||
{ | |||||
// TODO: | |||||
// add a way to show an icon for a given modification ID. | |||||
// By property name only. | |||||
this.globalModifiers = new Map(); | |||||
// By property name and entity. | |||||
this.localModifiers = new Map(); | |||||
// By entity | |||||
this.cachedValues = new Map(); | |||||
}; | |||||
// Wrapper for SetGlobalModifier, to set multiple modifiers with the same ID and priority easily | |||||
ModifiersManager.prototype.SetGlobalModifiers = function(modificationID, priority, modificationArray) | |||||
{ | |||||
for (let propertyName in modificationArray) | |||||
this.SetGlobalModifier(propertyName, modificationID, priority, modificationArray[propertyName]); | |||||
} | |||||
// Wrapper for RemoveGlobalModifier, to remove any modifier with this ID | |||||
ModifiersManager.prototype.RemoveGlobalModifiers = function(modificationID) | |||||
{ | |||||
for (let propertyName of this.globalModifiers) | |||||
this.RemoveGlobalModifier(propertyName[0], modificationID); | |||||
} | |||||
// Wrapper for HasGlobalModifier, will return true if there is at least one modifier with this ID for any property. | |||||
ModifiersManager.prototype.HasAnyGlobalModifierWithID = function(modificationID) | |||||
{ | |||||
for (let propertyName of this.globalModifiers) | |||||
if (this.HasGlobalModifier(propertyName[0], modificationID)) | |||||
return true; | |||||
return false; | |||||
} | |||||
ModifiersManager.prototype.SetGlobalModifier = function(propertyName, modificationID, priority, modification) | |||||
{ | |||||
if (!this.globalModifiers.get(propertyName)) | |||||
this.globalModifiers.set(propertyName, {"version": 0, "modifications": []}); | |||||
let modifier = this.globalModifiers.get(propertyName); | |||||
// check whether we already have that modifier | |||||
let existing = modifier.modifications.filter(modification => { return modification.ID == modificationID; }); | |||||
if (existing.length > 1) | |||||
{ | |||||
error("Code bug: two global modifiers have the same modificationID " + modificationID); | |||||
return; | |||||
} | |||||
if (existing.length) | |||||
// update it. | |||||
existing.effect = modification; | |||||
wraitii: I believe this is buggy and there should be a [0] here. | |||||
else | |||||
{ | |||||
// create a new modifier at the correct position. | |||||
let index = 0; | |||||
// TODO: do a binary search if this ever becomes a performance problem. | |||||
for (index = 0; index < modifier.modifications.length; ++index) | |||||
// in case of equal priority, insert in chronological order. | |||||
if (modifier.modifications[index].priority > priority) | |||||
break; | |||||
modifier.modifications.splice(index, 0, {"ID": modificationID ,"priority": priority, "effect": modification}); | |||||
} | |||||
// bump version to invalidate caches | |||||
++modifier.version; | |||||
// send messages | |||||
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); | |||||
wraitiiAuthorUnsubmitted Not Done Inline ActionsThis and the similar bit below will be refactored. wraitii: This and the similar bit below will be refactored. | |||||
if (!cmpPlayer || cmpPlayer.GetPlayerID() === undefined) | |||||
return; | |||||
let playerID = cmpPlayer.GetPlayerID(); | |||||
// TODO: would be nice to do this optionally in a meta-component with several property names or something. | |||||
Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": playerID, "component": propertyName.split("/")[0], "valueNames": [propertyName]}); | |||||
// AIInterface wants the entities potentially affected. TODO: improve on this | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
let ents = cmpRangeManager.GetEntitiesByPlayer(playerID); | |||||
Engine.BroadcastMessage(MT_ValueModification, { "entities": ents, "component": propertyName.split("/")[0], "valueNames": [propertyName]}); | |||||
} | |||||
ModifiersManager.prototype.RemoveGlobalModifier = function(propertyName, modificationID) | |||||
{ | |||||
if (!this.globalModifiers.get(propertyName)) | |||||
return; | |||||
let modifier = this.globalModifiers.get(propertyName); | |||||
// remove modifier. | |||||
let size = modifier.modifications.length; | |||||
modifier.modifications = modifier.modifications.filter(modification => { return modification.ID != modificationID; }); | |||||
if (modifier.modifications.length != size) | |||||
// bump version to invalidate caches | |||||
++modifier.version; | |||||
// send messages | |||||
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); | |||||
if (!cmpPlayer || cmpPlayer.GetPlayerID() === undefined) | |||||
return; | |||||
let playerID = cmpPlayer.GetPlayerID(); | |||||
// TODO: would be nice to do this optionally in a meta-component with several property names or something. | |||||
Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": playerID, "component": propertyName.split("/")[0], "valueNames": [propertyName]}); | |||||
// AIInterface wants the entities potentially affected. TODO: improve on this | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
let ents = cmpRangeManager.GetEntitiesByPlayer(playerID); | |||||
Engine.BroadcastMessage(MT_ValueModification, { "entities": ents, "component": propertyName.split("/")[0], "valueNames": [propertyName]}); | |||||
} | |||||
ModifiersManager.prototype.HasGlobalModifier = function(propertyName, modificationID) | |||||
{ | |||||
if (!this.globalModifiers.get(propertyName)) | |||||
return false; | |||||
return this.globalModifiers.get(propertyName).modifications.some(modification => { return modification.ID == modificationID; }); | |||||
} | |||||
// Caching layer in front of ApplyModificationsWorker | |||||
// Note: be careful with the type of curValue, if it should be a numerical | |||||
// value and is derived from template data, you must convert the string | |||||
// from the template to a number using the + operator, before calling | |||||
// this function! | |||||
ModifiersManager.prototype.ApplyModifications = function(propertyName, originalValue, ent) | |||||
{ | |||||
let value = originalValue; | |||||
// sanity check | |||||
if (Array.isArray(originalValue)) | |||||
value = originalValue.slice(); | |||||
else if (typeof originalValue == "object") | |||||
{ | |||||
error("ModifiersManager ApplyModifications called with an object"); | |||||
return; | |||||
} | |||||
if (!this.cachedValues.get(ent)) | |||||
this.cachedValues.set(ent, new Map()); | |||||
if (!this.cachedValues.get(ent).get(propertyName)) | |||||
this.cachedValues.get(ent).set(propertyName, { "version": 0, "value": value}); | |||||
let propertyValue = this.cachedValues.get(ent).get(propertyName); | |||||
// global modifiers | |||||
if (this.globalModifiers.get(propertyName)) | |||||
if (propertyValue.version < this.globalModifiers.get(propertyName).version) | |||||
{ | |||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); | |||||
if (!cmpIdentity) | |||||
return originalValue; | |||||
let modifications = {}; | |||||
modifications[propertyName] = []; // TODO: change this once GetTechModifiedProperty is updated I guess. | |||||
for (let modifier of this.globalModifiers.get(propertyName).modifications) | |||||
modifications[propertyName].push(modifier.effect); | |||||
let newValue = GetTechModifiedProperty(modifications, cmpIdentity.GetClassesList(), propertyName, value); | |||||
this.cachedValues.get(ent).get(propertyName).version = this.globalModifiers.get(propertyName).version; | |||||
this.cachedValues.get(ent).get(propertyName).value = newValue; | |||||
} | |||||
// TODO: local modifiers go here. | |||||
return this.cachedValues.get(ent).get(propertyName).value; | |||||
}; | |||||
// Alternative version of ApplyModifications, applies to templates instead of entities | |||||
ModifiersManager.prototype.ApplyModificationsTemplate = function(propertyName, originalValue, template) | |||||
{ | |||||
if (!template || !template.Identity || !this.globalModifiers.has(propertyName)) | |||||
return originalValue; | |||||
let modifications = {}; | |||||
modifications[propertyName] = []; // TODO: change this once GetTechModifiedProperty is updated I guess. | |||||
for (let modifier of this.globalModifiers.get(propertyName).modifications) | |||||
modifications[propertyName].push(modifier.effect); | |||||
return GetTechModifiedProperty(modifications, GetIdentityClasses(template.Identity), propertyName, originalValue); | |||||
}; | |||||
ModifiersManager.prototype.OnGlobalOwnershipChanged = function(msg) | |||||
{ | |||||
// Send ValueModification messages to new entities. | |||||
// TODO: this is quite wasteful, we should cache templates affected by technologies and 360 quickscope it. | |||||
let playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID(); | |||||
if (msg.to != playerID) | |||||
return; | |||||
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); | |||||
let template = cmpTemplateManager.GetCurrentTemplateName(msg.entity); | |||||
// TODO : save existing modifiers if from !== -1 in a clever way. | |||||
if (msg.from != -1) | |||||
return; | |||||
let cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity); | |||||
if (!cmpIdentity) | |||||
return; | |||||
let classes = cmpIdentity.GetClassesList(); | |||||
// local modifiers will be added by the relevant components, so no need to check for them here. | |||||
let modifiedComponents = {}; | |||||
for (let property of this.globalModifiers) | |||||
{ | |||||
// We only need to find one one tech per component for a match | |||||
var modifications = property[1].modifications; | |||||
var component = property[0].split("/")[0]; | |||||
for (let modif of modifications) | |||||
if (DoesModificationApply(modif.effect, classes)) | |||||
{ | |||||
if (!modifiedComponents[component]) | |||||
modifiedComponents[component] = []; | |||||
modifiedComponents[component].push(property[0]); | |||||
} | |||||
} | |||||
// Send message(s) to the entity so it knows about researched techs | |||||
for (let component in modifiedComponents) | |||||
Engine.PostMessage(msg.entity, MT_ValueModification, { "entities": [msg.entity], "component": component, "valueNames": modifiedComponents[component] }); | |||||
} | |||||
Engine.RegisterComponentType(IID_ModifiersManager, "ModifiersManager", ModifiersManager); |
Wildfire Games · Phabricator
I believe this is buggy and there should be a [0] here.