1
0
forked from 0ad/0ad

Visualise auras by adding status icons to affected units. Implement the visualisation for the temple aura. Fixes #3180

This was SVN commit r16611.
This commit is contained in:
sanderd17 2015-05-02 16:15:59 +00:00
parent 18a97cc82a
commit 27560e72fe
6 changed files with 237 additions and 174 deletions

Binary file not shown.

View File

@ -39,24 +39,26 @@ AuraManager.prototype.ensureExists = function(name, value, id, key, defaultData)
return k;
};
AuraManager.prototype.ApplyBonus = function(value, ent, data, key)
AuraManager.prototype.ApplyBonus = function(value, ents, data, key)
{
var dataList = this.ensureExists("modifications", value, ent, key, {"add":0, "multiply":1});
for (let ent of ents)
{
var dataList = this.ensureExists("modifications", value, ent, key, {"add":0, "multiply":1});
dataList.push(data);
dataList.push(data);
if (dataList.length > 1)
return;
if (dataList.length > 1)
continue;
// first time added this aura
if (data.multiply)
this.modificationsCache[value][ent].multiply *= data.multiply;
// first time added this aura
if (data.multiply)
this.modificationsCache[value][ent].multiply *= data.multiply;
if (data.add)
this.modificationsCache[value][ent].add += data.add;
// post message to the entity to notify it about the change
Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
if (data.add)
this.modificationsCache[value][ent].add += data.add;
// post message to the entity to notify it about the change
Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
}
};
AuraManager.prototype.ApplyTemplateBonus = function(value, player, classes, data, key)
@ -84,33 +86,35 @@ AuraManager.prototype.ApplyTemplateBonus = function(value, player, classes, data
Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": player, "component": value.split("/")[0], "valueNames": [value] });
};
AuraManager.prototype.RemoveBonus = function(value, ent, key)
AuraManager.prototype.RemoveBonus = function(value, ents, key)
{
var v = this.modifications[value];
if (!v)
return;
var e = v[ent];
if (!e)
return;
var dataList = e[key];
if (!dataList || !dataList.length)
return;
for (let ent of ents)
{
var v = this.modifications[value];
if (!v)
continue;
var e = v[ent];
if (!e)
continue;
var dataList = e[key];
if (!dataList || !dataList.length)
continue;
// get the applied data to remove again
var data = dataList.pop();
// get the applied data to remove again
var data = dataList.pop();
if (dataList.length > 0)
return;
if (dataList.length > 0)
continue;
// out of last aura of this kind, remove modifications
if (data.add)
this.modificationsCache[value][ent].add -= data.add;
// out of last aura of this kind, remove modifications
if (data.add)
this.modificationsCache[value][ent].add -= data.add;
if (data.multiply)
this.modificationsCache[value][ent].multiply /= data.multiply;
// post message to the entity to notify it about the change
Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
if (data.multiply)
this.modificationsCache[value][ent].multiply /= data.multiply;
// post message to the entity to notify it about the change
Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
}
};
AuraManager.prototype.RemoveTemplateBonus = function(value, player, classes, key)

View File

@ -47,6 +47,11 @@ Auras.prototype.Schema =
"<text/>" +
"</element>" +
"</optional>" +
"<optional>" +
"<element name='OverlayIcon' a:help='Icon to show on the entities affected by this aura'>" +
"<text/>" +
"</element>" +
"</optional>" +
"<element name='Affects' a:help='Affected classes'>" +
"<text/>" +
"</element>" +
@ -103,6 +108,16 @@ Auras.prototype.GetAuraNames = function()
return Object.keys(this.template);
};
Auras.prototype.GetOverlayIcon = function(name)
{
return this.template[name].OverlayIcon || "";
};
Auras.prototype.GetAffectedEntities = function(name)
{
return this[name].targetUnits;
};
Auras.prototype.GetRange = function(name)
{
if (!this.IsRangeAura(name))
@ -136,17 +151,16 @@ Auras.prototype.CalculateAffectedPlayers = function(name)
this.affectedPlayers[name] = [];
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
var cmpPlayer = QueryOwnerInterface(this.entity);
if (!cmpPlayer)
return;
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var numPlayers = cmpPlayerManager.GetNumPlayers();
var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (var i = 0; i < numPlayers; ++i)
{
for each (var p in affectedPlayers)
for (let p of affectedPlayers)
{
if (p == "Player" ? cmpPlayer.GetPlayerID() == i : cmpPlayer["Is" + p](i))
{
@ -159,17 +173,17 @@ Auras.prototype.CalculateAffectedPlayers = function(name)
Auras.prototype.HasFormationAura = function()
{
return this.GetAuraNames().some(this.IsFormationAura.bind(this));
return this.GetAuraNames().some(n => this.IsFormationAura(n));
};
Auras.prototype.HasGarrisonAura = function()
{
return this.GetAuraNames().some(this.IsGarrisonAura.bind(this));
return this.GetAuraNames().some(n => this.IsGarrisonAura(n));
};
Auras.prototype.HasGarrisonedUnitsAura = function()
{
return this.GetAuraNames().some(this.IsGarrisonedUnitsAura.bind(this));
return this.GetAuraNames().some(n => this.IsGarrisonedUnitsAura(n));
};
Auras.prototype.GetType = function(name)
@ -211,7 +225,7 @@ Auras.prototype.Clean = function()
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var auraNames = this.GetAuraNames();
// remove all bonuses
for each (var name in auraNames)
for (let name of auraNames)
{
if (!this[name])
continue;
@ -219,14 +233,13 @@ Auras.prototype.Clean = function()
if (this.IsGlobalAura(name))
this.RemoveTemplateBonus(name);
for each(var ent in this[name].targetUnits)
this.RemoveBonus(name, ent);
this.RemoveBonus(name, this[name].targetUnits);
if (this[name].rangeQuery)
cmpRangeManager.DestroyActiveQuery(this[name].rangeQuery);
}
for each (var name in auraNames)
for (let name of auraNames)
{
// only calculate the affected players on re-applying the bonuses
// this makes sure the template bonuses are removed from the correct players
@ -277,89 +290,36 @@ Auras.prototype.GiveMembersWithValidClass = function(auraName, entityList)
Auras.prototype.OnRangeUpdate = function(msg)
{
var auraNames = this.GetAuraNames();
for each (var n in auraNames)
var auraNames = this.GetAuraNames().filter(n => msg.tag == this[n].rangeQuery);
for (let name of auraNames)
{
if (msg.tag == this[n].rangeQuery)
{
var name = n;
break;
}
this.ApplyBonus(name, msg.added);
this.RemoveBonus(name, msg.removed);
}
if (!name)
return;
var targetUnits = this[name].targetUnits;
var classes = this.GetClasses(name);
if (msg.added.length > 0)
{
var validList = this.GiveMembersWithValidClass(name, msg.added);
for each (var e in validList)
{
targetUnits.push(e);
this.ApplyBonus(name, e);
}
}
if (msg.removed.length > 0)
{
for each (var e in msg.removed)
{
targetUnits.splice(targetUnits.indexOf(e), 1);
this.RemoveBonus(name, e);
}
}
};
Auras.prototype.OnGarrisonedUnitsChanged = function(msg)
{
var auraNames = this.GetAuraNames();
for each (var name in auraNames)
var auraNames = this.GetAuraNames().filter(n => this.IsGarrisonedUnitsAura(n));
for (let name of auraNames)
{
if (!this.IsGarrisonedUnitsAura(name))
continue;
for each (var ent in msg.added)
this.ApplyBonus(name, ent);
for each (var ent in msg.removed)
this.RemoveBonus(name, ent);
this.ApplyBonus(name, msg.added);
this.RemoveBonus(name, msg.removed);
}
};
Auras.prototype.ApplyFormationBonus = function(memberList)
{
var auraNames = this.GetAuraNames();
for each (var name in auraNames)
{
if (!this.IsFormationAura(name))
continue;
var validList = this.GiveMembersWithValidClass(name, memberList);
for each (var ent in validList)
{
this[name].targetUnits.push(ent);
this.ApplyBonus(name,ent);
}
}
var auraNames = this.GetAuraNames().filter(n => this.IsFormationAura(n));
for (let name of auraNames)
this.ApplyBonus(name, memberList);
};
Auras.prototype.ApplyGarrisonBonus = function(structure)
{
var auraNames = this.GetAuraNames();
for each (var name in auraNames)
{
if (!this.IsGarrisonAura(name))
continue;
var validList = this.GiveMembersWithValidClass(name, [structure]);
if (validList.length)
{
this[name].targetUnits.push(validList[0]);
this.ApplyBonus(name,validList[0]);
}
}
var auraNames = this.GetAuraNames().filter(n => this.IsGarrisonAura(n));
for (let name of auraNames)
this.ApplyBonus(name, [structure]);
};
Auras.prototype.ApplyTemplateBonus = function(name, players)
@ -370,38 +330,23 @@ Auras.prototype.ApplyTemplateBonus = function(name, players)
var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
var classes = this.GetClasses(name);
for each (var mod in modifications)
for each (var player in players)
for (let mod of modifications)
for (let player of players)
cmpAuraManager.ApplyTemplateBonus(mod.value, player, classes, mod, this.templateName + "/" + name + "/" + mod.value);
};
Auras.prototype.RemoveFormationBonus = function(memberList)
{
var auraNames = this.GetAuraNames();
for each (var name in auraNames)
{
if (!this.IsFormationAura(name))
continue;
for each (var ent in memberList)
{
this.RemoveBonus(name,ent);
this[name].targetUnits.splice(this[name].targetUnits.indexOf(ent), 1);
}
}
var auraNames = this.GetAuraNames().filter(n => this.IsFormationAura(n));
for (let name of auraNames)
this.RemoveBonus(name, memberList);
};
Auras.prototype.RemoveGarrisonBonus = function(structure)
{
var auraNames = this.GetAuraNames();
for each (var name in auraNames)
{
if (!this.IsGarrisonAura(name))
continue;
this.RemoveBonus(name,structure);
this[name].targetUnits.splice(this[name].targetUnits.indexOf(structure), 1);
}
var auraNames = this.GetAuraNames().filter(n => this.IsGarrisonAura(n));
for (let name of auraNames)
this.RemoveBonus(name, [structure]);
};
Auras.prototype.RemoveTemplateBonus = function(name)
@ -419,22 +364,46 @@ Auras.prototype.RemoveTemplateBonus = function(name)
cmpAuraManager.RemoveTemplateBonus(mod.value, player, classes, this.templateName + "/" + name + "/" + mod.value);
};
Auras.prototype.ApplyBonus = function(name, ent)
Auras.prototype.ApplyBonus = function(name, ents)
{
var validEnts = this.GiveMembersWithValidClass(name, ents);
if (!validEnts.length)
return;
this[name].targetUnits = this[name].targetUnits.concat(validEnts);
var modifications = this.GetModifications(name);
var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
for each (var mod in modifications)
cmpAuraManager.ApplyBonus(mod.value, ent, mod, this.templateName + "/" + name + "/" + mod.value);
for each (let mod in modifications)
cmpAuraManager.ApplyBonus(mod.value, validEnts, mod, this.templateName + "/" + name + "/" + mod.value);
// update status bars if this has an icon
if (!this.GetOverlayIcon(name))
return;
for (let ent of validEnts)
{
var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars)
if (cmpStatusBars)
cmpStatusBars.AddAuraSource(this.entity, name);
}
};
Auras.prototype.RemoveBonus = function(name, ent)
Auras.prototype.RemoveBonus = function(name, ents)
{
var validEnts = this.GiveMembersWithValidClass(name, ents);
if (!validEnts.length)
return;
this[name].targetUnits = this[name].targetUnits.filter(v => validEnts.indexOf(v) == -1);
var modifications = this.GetModifications(name);
var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
for each (var mod in modifications)
cmpAuraManager.RemoveBonus(mod.value, ent, this.templateName + "/" + name + "/" + mod.value);
for each (let mod in modifications)
cmpAuraManager.RemoveBonus(mod.value, validEnts, this.templateName + "/" + name + "/" + mod.value);
// update status bars if this has an icon
if (!this.GetOverlayIcon(name))
return;
for (let ent of validEnts)
{
var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars)
if (cmpStatusBars)
cmpStatusBars.RemoveAuraSource(this.entity, name);
}
};
Auras.prototype.OnOwnershipChanged = function(msg)

View File

@ -32,6 +32,7 @@ GuiInterface.prototype.Init = function()
this.timeNotificationID = 1;
this.timeNotifications = [];
this.entsRallyPointsDisplayed = [];
this.entsWithAuraAndStatusBars = new Set();
};
/*
@ -829,13 +830,43 @@ GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)
}
};
GuiInterface.prototype.GetEntitiesWithStatusBars = function()
{
return [...this.entsWithAuraAndStatusBars];
};
GuiInterface.prototype.SetStatusBars = function(player, cmd)
{
for each (var ent in cmd.entities)
let affectedEnts = new Set();
for (let ent of cmd.entities)
{
var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars);
let cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars);
if (!cmpStatusBars)
continue;
cmpStatusBars.SetEnabled(cmd.enabled);
let cmpAuras = Engine.QueryInterface(ent, IID_Auras);
if (!cmpAuras)
continue;
for (let name of cmpAuras.GetAuraNames())
{
if (!cmpAuras.GetOverlayIcon(name))
continue;
for (let e of cmpAuras.GetAffectedEntities(name))
affectedEnts.add(e);
if (cmd.enabled)
this.entsWithAuraAndStatusBars.add(ent);
else
this.entsWithAuraAndStatusBars.delete(ent);
}
}
for (let ent of affectedEnts)
{
let cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars)
if (cmpStatusBars)
cmpStatusBars.SetEnabled(cmd.enabled);
cmpStatusBars.RegenerateSprites();
}
};

View File

@ -14,17 +14,21 @@ StatusBars.prototype.Schema =
StatusBars.prototype.Init = function()
{
this.enabled = false;
this.auraSources = {};
};
// Because this is enabled directly by the GUI and is not
// network-synchronised (it only affects local rendering),
// we disable serialization in order to prevent OOS errors
StatusBars.prototype.Serialize = null;
StatusBars.prototype.Deserialize = function()
/**
* Don't serialise this.enabled since it's modified by the GUI
*/
StatusBars.prototype.Serialize = function()
{
return {"auraSources": this.auraSources};
};
StatusBars.prototype.Deserialize = function(data)
{
// Use default initialisation
this.Init();
this.auraSources = data.auraSources;
};
StatusBars.prototype.SetEnabled = function(enabled)
@ -33,14 +37,26 @@ StatusBars.prototype.SetEnabled = function(enabled)
if (enabled == this.enabled)
return;
// Update the displayed sprites
this.enabled = enabled;
if (enabled)
this.RegenerateSprites();
// Update the displayed sprites
this.RegenerateSprites();
};
StatusBars.prototype.AddAuraSource = function(source, auraName)
{
if (this.auraSources[source])
this.auraSources[source].push(auraName);
else
this.ResetSprites();
this.auraSources[source] = [auraName];
this.RegenerateSprites();
};
StatusBars.prototype.RemoveAuraSource = function(source, auraName)
{
let names = this.auraSources[source];
names.splice(names.indexOf(auraName), 1);
this.RegenerateSprites();
};
StatusBars.prototype.OnHealthChanged = function(msg)
@ -61,17 +77,58 @@ StatusBars.prototype.OnPackProgressUpdate = function(msg)
this.RegenerateSprites();
};
StatusBars.prototype.ResetSprites = function()
{
var cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
cmpOverlayRenderer.Reset();
};
StatusBars.prototype.RegenerateSprites = function()
{
var cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
let cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
cmpOverlayRenderer.Reset();
let yoffset = 0;
if (this.enabled)
yoffset = this.AddBars(cmpOverlayRenderer, yoffset);
yoffset = this.AddAuraIcons(cmpOverlayRenderer, yoffset);
return yoffset;
};
// Internal helper functions
StatusBars.prototype.AddAuraIcons = function(cmpOverlayRenderer, yoffset)
{
let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
let sources = cmpGuiInterface.GetEntitiesWithStatusBars().filter(e => this.auraSources[e] && this.auraSources[e].length);
if (!sources.length)
return yoffset;
let iconSet = new Set();
for (let ent of sources)
{
let cmpAuras = Engine.QueryInterface(ent, IID_Auras);
if (!cmpAuras) // probably the ent just died
continue;
for (let name of this.auraSources[ent])
iconSet.add(cmpAuras.GetOverlayIcon(name));
}
// World-space offset from the unit's position
var offset = { "x": 0, "y": +this.template.HeightOffset, "z": 0 };
let iconSize = +this.template.BarWidth / 2;
let xoffset = -iconSize * (iconSet.size - 1) * 0.6
for (let icon of iconSet)
{
cmpOverlayRenderer.AddSprite(
icon,
{ "x": xoffset - iconSize/2, "y": yoffset },
{ "x": xoffset + iconSize/2, "y": yoffset + iconSize },
offset
);
xoffset += iconSize * 1.2;
}
return yoffset + iconSize + this.template.BarHeight / 2;
};
StatusBars.prototype.AddBars = function(cmpOverlayRenderer, yoffset)
{
// Size of health bar (in world-space units)
var width = +this.template.BarWidth;
var height = +this.template.BarHeight;
@ -79,26 +136,23 @@ StatusBars.prototype.RegenerateSprites = function()
// World-space offset from the unit's position
var offset = { "x": 0, "y": +this.template.HeightOffset, "z": 0 };
// Billboard offset of next bar
var yoffset = 0;
var AddBar = function(type, amount)
{
cmpOverlayRenderer.AddSprite(
"art/textures/ui/session/icons/"+type+"_bg.png",
{ "x": -width/2, "y": -height/2 + yoffset },
{ "x": width/2, "y": height/2 + yoffset },
{ "x": -width/2, "y":yoffset },
{ "x": width/2, "y": height + yoffset },
offset
);
cmpOverlayRenderer.AddSprite(
"art/textures/ui/session/icons/"+type+"_fg.png",
{ "x": -width/2, "y": -height/2 + yoffset },
{ "x": width*(amount - 0.5), "y": height/2 + yoffset },
{ "x": -width/2, "y": yoffset },
{ "x": width*(amount - 0.5), "y": height + yoffset },
offset
);
yoffset -= height * 1.2;
yoffset += height * 1.2;
};
var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
@ -113,6 +167,7 @@ StatusBars.prototype.RegenerateSprites = function()
if (cmpResourceSupply)
AddBar("supply", cmpResourceSupply.IsInfinite() ? 1 : cmpResourceSupply.GetCurrentAmount() / cmpResourceSupply.GetMaxAmount());
return yoffset;
/*
// Rank icon disabled for now - see discussion around
// http://www.wildfiregames.com/forum/index.php?s=&showtopic=13608&view=findpost&p=212154

View File

@ -10,6 +10,7 @@
</Modifications>
<AuraName>Healing Aura</AuraName>
<AuraDescription>Heals nearby units at 1 HP per second.</AuraDescription>
<OverlayIcon>art/textures/ui/session/auras/heal.png</OverlayIcon>
</heal>
</Auras>
<BuildRestrictions>