Index: ps/trunk/source/simulation2/components/ICmpIdentity.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpIdentity.h (revision 22585)
+++ ps/trunk/source/simulation2/components/ICmpIdentity.h (revision 22586)
@@ -1,35 +1,37 @@
-/* Copyright (C) 2011 Wildfire Games.
+/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_ICMPIDENTITY
#define INCLUDED_ICMPIDENTITY
#include "simulation2/system/Interface.h"
/**
* Identity data.
* (This interface only includes the functions needed by native code for entity selection)
*/
class ICmpIdentity : public IComponent
{
public:
virtual std::string GetSelectionGroupName() = 0;
+ virtual std::wstring GetPhenotype() = 0;
+
DECLARE_INTERFACE_TYPE(Identity)
};
#endif // INCLUDED_ICMPIDENTITY
Index: ps/trunk/binaries/data/mods/public/simulation/components/Identity.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Identity.js (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Identity.js (revision 22586)
@@ -1,169 +1,182 @@
function Identity() {}
Identity.prototype.Schema =
"Specifies various names and values associated with the unit type, typically for GUI display to users." +
"" +
"athen" +
"Athenian Hoplite" +
"Hoplī́tēs Athēnaïkós" +
"units/athen_infantry_spearman.png" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
- "" +
+ "" +
+ "" +
+ "tokens" +
+ "" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"Basic" +
"Advanced" +
"Elite" +
"" +
"" +
"" +
"" +
"" +
"" +
"tokens" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"tokens" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"tokens" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"";
Identity.prototype.Init = function()
{
this.classesList = GetIdentityClasses(this.template);
this.visibleClassesList = GetVisibleIdentityClasses(this.template);
+ if (this.template.Phenotype)
+ this.phenotype = pickRandom(this.GetPossiblePhenotypes());
+ else
+ this.phenotype = "default";
};
-Identity.prototype.Deserialize = function ()
-{
- this.Init();
-};
-
-Identity.prototype.Serialize = null; // we have no dynamic state to save
-
Identity.prototype.GetCiv = function()
{
return this.template.Civ;
};
Identity.prototype.GetLang = function()
{
return this.template.Lang || "greek"; // ugly default
};
-Identity.prototype.GetGender = function()
+/**
+ * Get a list of possible Phenotypes.
+ * @return {string[]} A list of possible phenotypes.
+ */
+Identity.prototype.GetPossiblePhenotypes = function()
+{
+ return this.template.Phenotype._string.split(/\s+/);
+};
+
+/**
+ * Get the current Phenotype.
+ * @return {string} The current phenotype.
+ */
+Identity.prototype.GetPhenotype = function()
{
- return this.template.Gender || "male"; // ugly default
+ return this.phenotype;
};
Identity.prototype.GetRank = function()
{
return this.template.Rank || "";
};
Identity.prototype.GetClassesList = function()
{
return this.classesList;
};
Identity.prototype.GetVisibleClassesList = function()
{
return this.visibleClassesList;
};
Identity.prototype.HasClass = function(name)
{
return this.GetClassesList().indexOf(name) != -1;
};
Identity.prototype.GetFormationsList = function()
{
if (this.template.Formations && this.template.Formations._string)
return this.template.Formations._string.split(/\s+/);
return [];
};
Identity.prototype.CanUseFormation = function(template)
{
return this.GetFormationsList().indexOf(template) != -1;
};
Identity.prototype.GetSelectionGroupName = function()
{
return this.template.SelectionGroupName || "";
};
Identity.prototype.GetGenericName = function()
{
return this.template.GenericName;
};
Identity.prototype.IsUndeletable = function()
{
return this.template.Undeletable == "true";
};
Engine.RegisterComponentType(IID_Identity, "Identity", Identity);
Index: ps/trunk/binaries/data/mods/public/simulation/components/Sound.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Sound.js (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Sound.js (revision 22586)
@@ -1,53 +1,53 @@
function Sound() {}
Sound.prototype.Schema =
"Lists the sound groups associated with this unit." +
"" +
"" +
"actor/human/movement/walk.xml" +
"actor/human/movement/walk.xml" +
"attack/weapon/sword.xml" +
"actor/human/death/death.xml" +
"" +
"" +
"" +
"" + /* TODO: make this more specific, like a list of specific elements */
"" +
"" +
"" +
"" +
"" +
"";
Sound.prototype.Init = function()
{
};
Sound.prototype.Serialize = null; // we have no dynamic state to save
Sound.prototype.GetSoundGroup = function(name)
{
return this.template.SoundGroups[name] || "";
};
Sound.prototype.PlaySoundGroup = function(name)
{
if (name in this.template.SoundGroups)
{
// Replace the "{lang}" codes with this entity's civ ID
- var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
+ let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
if (!cmpIdentity)
return;
- var lang = cmpIdentity.GetLang();
- // Replace the "{gender}" codes with this entity's gender ID
- var gender = cmpIdentity.GetGender();
-
- var soundName = this.template.SoundGroups[name].replace(/\{lang\}/g, lang)
- .replace(/\{gender\}/g, gender);
- var cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager);
+ let lang = cmpIdentity.GetLang();
+ // Replace the "{phenotype}" codes with this entity's phenotype ID
+ let phenotype = cmpIdentity.GetPhenotype();
+
+ let soundName = this.template.SoundGroups[name].replace(/\{lang\}/g, lang)
+ .replace(/\{phenotype\}/g, phenotype);
+ let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager);
if (cmpSoundManager)
cmpSoundManager.PlaySoundGroup(soundName, this.entity);
}
};
Engine.RegisterComponentType(IID_Sound, "Sound", Sound);
Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Identity.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Identity.js (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Identity.js (revision 22586)
@@ -1,52 +1,62 @@
Engine.LoadComponentScript("Identity.js");
let cmpIdentity = ConstructComponent(5, "Identity", {
"Civ": "iber",
- "GenericName": "Iberian Skirmisher"
+ "GenericName": "Iberian Skirmisher",
+ "Phenotype": { "_string": "male" },
});
TS_ASSERT_EQUALS(cmpIdentity.GetCiv(), "iber");
TS_ASSERT_EQUALS(cmpIdentity.GetLang(), "greek");
-TS_ASSERT_EQUALS(cmpIdentity.GetGender(), "male");
+TS_ASSERT_EQUALS(cmpIdentity.GetPhenotype(), "male");
TS_ASSERT_EQUALS(cmpIdentity.GetRank(), "");
TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetClassesList(), []);
TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetVisibleClassesList(), []);
TS_ASSERT_EQUALS(cmpIdentity.HasClass("CitizenSoldier"), false);
TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetFormationsList(), []);
TS_ASSERT_EQUALS(cmpIdentity.CanUseFormation("special/formations/skirmish"), false);
TS_ASSERT_EQUALS(cmpIdentity.GetSelectionGroupName(), "");
TS_ASSERT_EQUALS(cmpIdentity.GetGenericName(), "Iberian Skirmisher");
cmpIdentity = ConstructComponent(6, "Identity", {
"Civ": "iber",
"Lang": "iberian",
- "Gender": "female",
+ "Phenotype": { "_string": "female" },
"GenericName": "Iberian Skirmisher",
"SpecificName": "Lusitano Ezpatari",
"SelectionGroupName": "units/iber_infantry_javelinist_b",
"Tooltip": "Basic ranged infantry",
"History": "Iberians, especially the Lusitanians, were good at" +
" ranged combat and ambushing enemy columns. They throw heavy iron" +
" javelins and sometimes even add burning pitch to them, making them" +
" good as a cheap siege weapon.",
"Rank": "Basic",
"Classes": { "_string": "CitizenSoldier Human Organic" },
"VisibleClasses": { "_string": "Javelin" },
"Formations": { "_string": "special/formations/skirmish" },
"Icon": "units/iber_infantry_javelinist.png",
"RequiredTechnology": "phase_town"
});
TS_ASSERT_EQUALS(cmpIdentity.GetCiv(), "iber");
TS_ASSERT_EQUALS(cmpIdentity.GetLang(), "iberian");
-TS_ASSERT_EQUALS(cmpIdentity.GetGender(), "female");
+TS_ASSERT_EQUALS(cmpIdentity.GetPhenotype(), "female");
TS_ASSERT_EQUALS(cmpIdentity.GetRank(), "Basic");
TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetClassesList(), ["CitizenSoldier", "Human", "Organic", "Javelin", "Basic"]);
TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetVisibleClassesList(), ["Javelin"]);
TS_ASSERT_EQUALS(cmpIdentity.HasClass("CitizenSoldier"), true);
TS_ASSERT_EQUALS(cmpIdentity.HasClass("Female"), false);
TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetFormationsList(), ["special/formations/skirmish"]);
TS_ASSERT_EQUALS(cmpIdentity.CanUseFormation("special/formations/skirmish"), true);
TS_ASSERT_EQUALS(cmpIdentity.CanUseFormation("special/formations/line"), false);
TS_ASSERT_EQUALS(cmpIdentity.GetSelectionGroupName(), "units/iber_infantry_javelinist_b");
TS_ASSERT_EQUALS(cmpIdentity.GetGenericName(), "Iberian Skirmisher");
+
+cmpIdentity = ConstructComponent(7, "Identity", {
+ "Phenotype": { "_string": "First Second" },
+});
+TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetPossiblePhenotypes(), ["First", "Second"]);
+TS_ASSERT(["First", "Second"].indexOf(cmpIdentity.GetPhenotype()) !== -1);
+
+cmpIdentity = ConstructComponent(8, "Identity", {});
+TS_ASSERT_EQUALS(cmpIdentity.GetPhenotype(), "default");
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml (revision 22586)
@@ -1,145 +1,146 @@
1
1
15
1
0
1
0
0
0
0
false
false
30.0
0.01
0.0
2.5
corpse
100
0
0
false
gaia
Unit
Unit ConquestCritical
special/formations/null
special/formations/box
special/formations/column_closed
special/formations/line_closed
special/formations/column_open
special/formations/line_open
special/formations/flank
special/formations/battle_line
+ male
false
unit
true
true
false
false
true
false
false
false
0
upright
false
0.0
6.0
2.0
1.0
1
10
10
10
10
circle/128x128.png
circle/128x128_mask.png
interface/alarm/alarm_attackplayer.xml
attack/weapon/sword.xml
2.0
0.333
5.0
aggressive
12.0
false
true
true
false
default
9.0
1.67
false
false
false
false
12
false
true
false
false
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml (revision 22586)
@@ -1,106 +1,106 @@
3
1
15
2
4
1000
Field Palisade SiegeWall StoneWall
100.0
0.0
0.0
2
1
15
100
7.5
160
CitizenSoldier Human Organic
Cavalry Citizen Soldier
Cavalry
Basic
special/formations/wedge
130
10
5
0
0
pitch
150
2.0
1.0
5
20
20
20
20
circle/128x128.png
circle/128x128_mask.png
-
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
actor/mounted/movement/walk.xml
actor/mounted/movement/walk.xml
attack/impact/arrow_metal.xml
attack/weapon/sword.xml
attack/weapon/arrowfly.xml
actor/fauna/death/death_horse.xml
interface/alarm/alarm_create_cav.xml
6.5
1.95
92
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml (revision 22586)
@@ -1,66 +1,66 @@
7
5
20
1
30
250
0
0
100
5.0
240
Champion Cavalry
Cavalry
special/formations/wedge
pitch
arrow/256x256.png
arrow/256x256_mask.png
-
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
actor/mounted/movement/walk.xml
actor/mounted/movement/walk.xml
attack/weapon/sword.xml
actor/fauna/death/death_horse.xml
interface/alarm/alarm_create_cav.xml
6.5
2.25
96
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml (revision 22586)
@@ -1,50 +1,50 @@
5
5
20
20
125
0
0
75
100
Infantry
Champion Infantry
arrow/128x128.png
arrow/128x128_mask.png
interface/alarm/alarm_create_infantry.xml
-
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_repair.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_repair.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
actor/human/movement/walk.xml
actor/human/movement/walk.xml
attack/weapon/sword.xml
- actor/human/death/{gender}_death.xml
+ actor/human/death/{phenotype}_death.xml
84
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero.xml (revision 22586)
@@ -1,79 +1,79 @@
10.0
20.0
15.0
15
4
1000
Field Palisade SiegeWall StoneWall
units/heroes/hero_garrison
2
40
100
250
600
Hero
Organic Human
Hero Soldier
phase_city
400
10
0
0
25
hero
star/256x256.png
star/256x256_mask.png
-
+
interface/alarm/alarm_create_infantry.xml
- voice/{lang}/civ/civ_{gender}_heal.xml
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_repair.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+ voice/{lang}/civ/civ_{phenotype}_heal.xml
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_repair.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
attack/weapon/sword.xml
attack/impact/arrow_metal.xml
attack/weapon/arrowfly.xml
actor/human/movement/walk.xml
actor/human/movement/walk.xml
- actor/human/death/{gender}_death.xml
+ actor/human/death/{phenotype}_death.xml
Hero
88
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml (revision 22586)
@@ -1,128 +1,128 @@
2
4
15
2
4
1000
Field Palisade SiegeWall StoneWall
50.0
0.0
0.0
2
1.0
structures/{civ}_house
structures/{civ}_storehouse
structures/{civ}_farmstead
structures/{civ}_field
structures/{civ}_corral
structures/{civ}_outpost
other/wallset_palisade
structures/{civ}_sentry_tower
structures/{civ}_dock
structures/{civ}_barracks
structures/{civ}_blacksmith
structures/{civ}_temple
structures/{civ}_market
structures/{civ}_defense_tower
structures/{civ}_wallset_stone
structures/{civ}_civil_centre
structures/{civ}_fortress
structures/{civ}_wonder
10
50
0
0
0
2.5
80
Infantry
CitizenSoldier Human Organic
Citizen Infantry Soldier Worker
Basic
100
5
0
0
0
100
2.0
1.0
0.5
0.25
1
0.75
5
0.5
2
0.5
circle/128x128.png
circle/128x128_mask.png
-
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_repair.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_repair.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
actor/human/movement/walk.xml
actor/human/movement/run.xml
attack/impact/arrow_metal.xml
attack/weapon/sword.xml
attack/weapon/arrowfly.xml
- actor/human/death/{gender}_death.xml
+ actor/human/death/{phenotype}_death.xml
resource/construction/con_wood.xml
resource/foraging/forage_leaves.xml
resource/farming/farm.xml
resource/lumbering/lumbering.xml
resource/mining/pickaxe.xml
resource/mining/mining.xml
resource/mining/mining.xml
interface/alarm/alarm_create_infantry.xml
80
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml (revision 22586)
@@ -1,97 +1,97 @@
2.0
0
0.0
3
500
1000
25.0
0.0
0.0
2
units/female_inspiration
1.0
structures/{civ}_house
structures/{civ}_storehouse
structures/{civ}_farmstead
structures/{civ}_field
structures/{civ}_corral
structures/{civ}_dock
structures/{civ}_temple
structures/{civ}_market
structures/{civ}_civil_centre
structures/{civ}_wonder
50
25
Female Citizen
- female
+ female
FemaleCitizen
Citizen Worker
2.0
1.0
1
0.5
1
0.7
5
0.35
2
0.35
interface/alarm/alarm_create_female.xml
-
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_build.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_repair.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_build.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_repair.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
attack/weapon/sword.xml
- actor/human/death/{gender}_death.xml
+ actor/human/death/{phenotype}_death.xml
resource/construction/con_wood.xml
resource/foraging/forage_leaves.xml
resource/farming/farm.xml
resource/lumbering/lumbering.xml
resource/mining/pickaxe.xml
resource/mining/mining.xml
resource/mining/mining.xml
false
32
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml (revision 22586)
@@ -1,60 +1,60 @@
250
12
5
2000
Human
heal_overlay_range.png
heal_overlay_range_mask.png
0.35
85
-ConquestCritical
Healer
Basic
phase_town
Heal units.
Healer
150
plus/128x128.png
plus/128x128_mask.png
interface/alarm/alarm_create_infantry.xml
-
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_repair.xml
- voice/{lang}/civ/civ_{gender}_heal.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_repair.xml
+ voice/{lang}/civ/civ_{phenotype}_heal.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
actor/human/movement/walk.xml
actor/human/movement/run.xml
- actor/human/death/{gender}_death.xml
+ actor/human/death/{phenotype}_death.xml
30
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_slave.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_slave.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_slave.xml (revision 22586)
@@ -1,85 +1,85 @@
0.5
structures/{civ}_house
structures/{civ}_storehouse
structures/{civ}_farmstead
structures/{civ}_field
structures/{civ}_corral
structures/{civ}_outpost
other/wallset_palisade
structures/{civ}_dock
structures/{civ}_barracks
structures/{civ}_blacksmith
structures/{civ}_temple
structures/{civ}_market
structures/{civ}_defense_tower
structures/{civ}_wallset_stone
structures/{civ}_civil_centre
structures/{civ}_fortress
0
20
0
0
0
50
-0.25
true
Slave Worker
Slave
units/global_slave.png
Gatherer with a finite life span. Bonused at mining and lumbering.
1
0
1
1
1
1.0
0.5
0.5
0.35
1
1.0
5
1.0
5
1.0
-
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_repair.xml
- voice/{lang}/civ/civ_{gender}_garrison.xml
+
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_repair.xml
+ voice/{lang}/civ/civ_{phenotype}_garrison.xml
actor/human/movement/walk.xml
actor/human/movement/run.xml
- actor/human/death/{gender}_death.xml
+ actor/human/death/{phenotype}_death.xml
resource/construction/con_wood.xml
resource/foraging/forage_leaves.xml
resource/farming/farm.xml
resource/lumbering/lumbering.xml
resource/mining/pickaxe.xml
resource/mining/mining.xml
actor/singlesteps/steps_gravel_trained.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml (revision 22586)
@@ -1,53 +1,53 @@
15
100
80
100
-ConquestCritical
Trader Bribable
Trader
Trade resources between your own markets and those of your allies.
-
- voice/{lang}/civ/civ_{gender}_trade.xml
- voice/{lang}/civ/civ_{gender}_walk.xml
- voice/{lang}/civ/civ_{gender}_attack.xml
- voice/{lang}/civ/civ_{gender}_gather.xml
- voice/{lang}/civ/civ_{gender}_repair.xml
+
+ voice/{lang}/civ/civ_{phenotype}_trade.xml
+ voice/{lang}/civ/civ_{phenotype}_walk.xml
+ voice/{lang}/civ/civ_{phenotype}_attack.xml
+ voice/{lang}/civ/civ_{phenotype}_gather.xml
+ voice/{lang}/civ/civ_{phenotype}_repair.xml
actor/human/movement/walk.xml
actor/human/movement/run.xml
attack/weapon/sword.xml
- actor/human/death/{gender}_death.xml
+ actor/human/death/{phenotype}_death.xml
resource/construction/con_wood.xml
resource/foraging/forage_leaves.xml
resource/farming/farm.xml
resource/lumbering/lumbering.xml
resource/mining/pickaxe.xml
resource/mining/mining.xml
0.75
false
false
60
true
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca.xml (revision 22586)
@@ -1,21 +1,21 @@
units/heroes/brit_hero_boudicca
5.0
brit
Chariot
Boudicca (Chariot)
Boadicea
- female
+ female
units/brit_hero_boudicca.png
units/britons/hero_chariot_javelinist_boudicca_m.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca_cavalry_javelinist.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca_cavalry_javelinist.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca_cavalry_javelinist.xml (revision 22586)
@@ -1,16 +1,16 @@
units/heroes/brit_hero_boudicca
brit
Boudicca (Sword)
Boadicea
- female
+ female
units/brit_hero_boudicca.png
units/britons/hero_cavalry_javelinist_boudicca_m.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca_sword.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca_sword.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca_sword.xml (revision 22586)
@@ -1,16 +1,16 @@
units/heroes/brit_hero_boudicca
brit
Boudicca (Sword)
Boadicea
- female
+ female
units/brit_hero_boudicca.png
units/britons/hero_infantry_swordsman_boudicca.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/cart_support_healer_b.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/cart_support_healer_b.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/cart_support_healer_b.xml (revision 22586)
@@ -1,17 +1,17 @@
cart
- female
+ female
units/cart_support_healer_b
Kehinit
units/cart_support_healer.png
Basic
units/cart_support_healer_a
units/carthaginians/healer.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/iber_support_healer_b.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/iber_support_healer_b.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/iber_support_healer_b.xml (revision 22586)
@@ -1,18 +1,18 @@
iber
- female
+ female
units/iber_support_healer_b
Priestess of Ataekina
Emakumezko Apaiz de Ataekina
units/iber_support_healer.png
Basic
units/iber_support_healer_a
units/iberians/healer.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/kush_hero_amanirenas.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/kush_hero_amanirenas.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/kush_hero_amanirenas.xml (revision 22586)
@@ -1,16 +1,16 @@
units/heroes/kush_hero_amanirenas
kush
Amanirenas
Amnirense qore li kdwe li
- female
+ female
units/kush_hero_amanirenas.png
units/kushites/hero_infantry_archer_amanirenas.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/kush_hero_amanirenas_chariot.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/kush_hero_amanirenas_chariot.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/kush_hero_amanirenas_chariot.xml (revision 22586)
@@ -1,16 +1,16 @@
units/heroes/kush_hero_amanirenas
kush
Amanirenas
Amnirense qore li kdwe li
- female
+ female
units/kush_hero_amanirenas.png
units/kushites/hero_chariot_archer_amanirenas_m.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/maur_champion_maiden.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/maur_champion_maiden.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/maur_champion_maiden.xml (revision 22586)
@@ -1,14 +1,14 @@
maur
- female
+ female
Maiden Guard
Visha Kanya
units/maur_champion_maiden
units/maur_champion_maiden.png
units/mauryas/infantry_swordsman_c.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/maur_champion_maiden_archer.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/maur_champion_maiden_archer.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/maur_champion_maiden_archer.xml (revision 22586)
@@ -1,13 +1,13 @@
maur
- female
+ female
Maiden Guard Archer
Visha Kanya
units/maur_champion_maiden_archer.png
units/mauryas/infantry_archer_c.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/ptol_hero_cleopatra.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/ptol_hero_cleopatra.xml (revision 22585)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/ptol_hero_cleopatra.xml (revision 22586)
@@ -1,18 +1,18 @@
units/heroes/ptol_hero_cleopatra_1
units/heroes/ptol_hero_cleopatra_2
units/heroes/ptol_hero_cleopatra_3
ptol
- female
+ female
Cleopatra VII
Kleopatra H' Philopator
units/ptol_hero_cleopatra.png
units/ptolemies/hero_infantry_archer_cleopatra.xml
Index: ps/trunk/source/simulation2/components/CCmpVisualActor.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpVisualActor.cpp (revision 22585)
+++ ps/trunk/source/simulation2/components/CCmpVisualActor.cpp (revision 22586)
@@ -1,740 +1,778 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpVisual.h"
#include "simulation2/MessageTypes.h"
#include "ICmpFootprint.h"
+#include "ICmpIdentity.h"
#include "ICmpUnitRenderer.h"
#include "ICmpOwnership.h"
#include "ICmpPosition.h"
#include "ICmpTemplateManager.h"
#include "ICmpTerrain.h"
#include "ICmpUnitMotion.h"
#include "ICmpValueModificationManager.h"
#include "ICmpVisibility.h"
#include "ICmpSound.h"
#include "simulation2/serialization/SerializeTemplates.h"
#include "graphics/Decal.h"
#include "graphics/Frustum.h"
#include "graphics/Model.h"
#include "graphics/ObjectBase.h"
#include "graphics/ObjectEntry.h"
#include "graphics/Unit.h"
#include "graphics/UnitAnimation.h"
#include "graphics/UnitManager.h"
#include "maths/BoundingSphere.h"
#include "maths/Matrix3D.h"
#include "maths/Vector3D.h"
#include "ps/CLogger.h"
#include "ps/GameSetup/Config.h"
#include "renderer/Scene.h"
class CCmpVisualActor : public ICmpVisual
{
public:
static void ClassInit(CComponentManager& componentManager)
{
componentManager.SubscribeToMessageType(MT_InterpolatedPositionChanged);
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
componentManager.SubscribeToMessageType(MT_ValueModification);
componentManager.SubscribeToMessageType(MT_TerrainChanged);
+ componentManager.SubscribeToMessageType(MT_Create);
componentManager.SubscribeToMessageType(MT_Destroy);
}
DEFAULT_COMPONENT_ALLOCATOR(VisualActor)
private:
std::wstring m_BaseActorName, m_ActorName;
bool m_IsFoundationActor;
// Not initialized in non-visual mode
CUnit* m_Unit;
+ CModelAbstract::CustomSelectionShape* m_ShapeDescriptor = nullptr;
fixed m_R, m_G, m_B; // shading color
// Current animation state
std::string m_AnimName;
bool m_AnimOnce;
fixed m_AnimSpeed;
std::wstring m_SoundGroup;
fixed m_AnimDesync;
fixed m_AnimSyncRepeatTime; // 0.0 if not synced
fixed m_AnimSyncOffsetTime;
std::map m_VariantSelections;
u32 m_Seed; // seed used for random variations
bool m_ConstructionPreview;
bool m_VisibleInAtlasOnly;
bool m_IsActorOnly; // an in-world entity should not have this or it might not be rendered.
+ bool m_SilhouetteDisplay;
+ bool m_SilhouetteOccluder;
+ bool m_DisableShadows;
+
ICmpUnitRenderer::tag_t m_ModelTag;
public:
static std::string GetSchema()
{
return
"Display the unit using the engine's actor system."
""
"units/hellenes/infantry_spearman_b.xml"
""
""
"structures/hellenes/barracks.xml"
"structures/fndn_4x4.xml"
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
"0.0"
""
""
""
""
"0.0"
""
""
""
""
"0.0"
""
""
""
""
""
""
"0.0"
""
""
""
""
"0.0"
""
""
""
""
""
""
""
""
"";
}
virtual void Init(const CParamNode& paramNode)
{
m_Unit = NULL;
m_R = m_G = m_B = fixed::FromInt(1);
m_ConstructionPreview = paramNode.GetChild("ConstructionPreview").IsOk();
m_Seed = GetEntityId();
m_IsFoundationActor = paramNode.GetChild("Foundation").IsOk() && paramNode.GetChild("FoundationActor").IsOk();
- if (m_IsFoundationActor)
- m_BaseActorName = m_ActorName = paramNode.GetChild("FoundationActor").ToString();
- else
- m_BaseActorName = m_ActorName = paramNode.GetChild("Actor").ToString();
+
+ m_BaseActorName = paramNode.GetChild(m_IsFoundationActor ? "FoundationActor" : "Actor").ToString();
+ ParseActorName(m_BaseActorName);
m_VisibleInAtlasOnly = paramNode.GetChild("VisibleInAtlasOnly").ToBool();
m_IsActorOnly = paramNode.GetChild("ActorOnly").IsOk();
- InitModel(paramNode);
-
- SelectAnimation("idle");
+ m_SilhouetteDisplay = paramNode.GetChild("SilhouetteDisplay").ToBool();
+ m_SilhouetteOccluder = paramNode.GetChild("SilhouetteOccluder").ToBool();
+ m_DisableShadows = paramNode.GetChild("DisableShadows").ToBool();
+
+ // Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
+ // Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
+ // shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
+ // which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
+ // initialize the selection shape descriptor on-demand.
+ InitSelectionShapeDescriptor(paramNode);
}
virtual void Deinit()
{
if (m_Unit)
{
GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
m_Unit = NULL;
}
}
template
void SerializeCommon(S& serialize)
{
serialize.NumberFixed_Unbounded("r", m_R);
serialize.NumberFixed_Unbounded("g", m_G);
serialize.NumberFixed_Unbounded("b", m_B);
serialize.StringASCII("anim name", m_AnimName, 0, 256);
serialize.Bool("anim once", m_AnimOnce);
serialize.NumberFixed_Unbounded("anim speed", m_AnimSpeed);
serialize.String("sound group", m_SoundGroup, 0, 256);
serialize.NumberFixed_Unbounded("anim desync", m_AnimDesync);
serialize.NumberFixed_Unbounded("anim sync repeat time", m_AnimSyncRepeatTime);
serialize.NumberFixed_Unbounded("anim sync offset time", m_AnimSyncOffsetTime);
SerializeMap()(serialize, "variation", m_VariantSelections);
serialize.NumberU32_Unbounded("seed", m_Seed);
serialize.String("actor", m_ActorName, 0, 256);
// TODO: store actor variables?
}
virtual void Serialize(ISerializer& serialize)
{
// TODO: store the actor name, if !debug and it differs from the template
if (serialize.IsDebug())
{
serialize.String("base actor", m_BaseActorName, 0, 256);
}
SerializeCommon(serialize);
}
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
{
Init(paramNode);
u32 oldSeed = GetActorSeed();
SerializeCommon(deserialize);
+ InitModel();
+ SelectAnimation("idle");
+
// If we serialized a different seed or different actor, reload actor
if (oldSeed != GetActorSeed() || m_BaseActorName != m_ActorName)
ReloadActor();
else
ReloadUnitAnimation();
if (m_Unit)
{
CmpPtr cmpOwnership(GetEntityHandle());
if (cmpOwnership)
m_Unit->GetModel().SetPlayerID(cmpOwnership->GetOwner());
}
}
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
{
switch (msg.GetType())
{
case MT_OwnershipChanged:
{
if (!m_Unit)
break;
const CMessageOwnershipChanged& msgData = static_cast (msg);
m_Unit->GetModel().SetPlayerID(msgData.to);
break;
}
case MT_TerrainChanged:
{
if (!m_Unit)
break;
const CMessageTerrainChanged& msgData = static_cast (msg);
m_Unit->GetModel().SetTerrainDirty(msgData.i0, msgData.j0, msgData.i1, msgData.j1);
break;
}
case MT_ValueModification:
{
const CMessageValueModification& msgData = static_cast (msg);
if (msgData.component != L"VisualActor")
break;
CmpPtr cmpValueModificationManager(GetSystemEntity());
std::wstring newActorName;
if (m_IsFoundationActor)
newActorName = cmpValueModificationManager->ApplyModifications(L"VisualActor/FoundationActor", m_BaseActorName, GetEntityId());
else
newActorName = cmpValueModificationManager->ApplyModifications(L"VisualActor/Actor", m_BaseActorName, GetEntityId());
if (newActorName != m_ActorName)
{
- m_ActorName = newActorName;
+ ParseActorName(newActorName);
ReloadActor();
}
break;
}
case MT_InterpolatedPositionChanged:
{
const CMessageInterpolatedPositionChanged& msgData = static_cast (msg);
if (m_ModelTag.valid())
{
CmpPtr cmpModelRenderer(GetSystemEntity());
cmpModelRenderer->UpdateUnitPos(m_ModelTag, msgData.inWorld, msgData.pos0, msgData.pos1);
}
break;
}
+ case MT_Create:
+ {
+ InitModel();
+
+ SelectAnimation("idle");
+ break;
+ }
case MT_Destroy:
{
if (m_ModelTag.valid())
{
CmpPtr cmpModelRenderer(GetSystemEntity());
cmpModelRenderer->RemoveUnit(m_ModelTag);
m_ModelTag = ICmpUnitRenderer::tag_t();
}
break;
}
}
}
virtual CBoundingBoxAligned GetBounds() const
{
if (!m_Unit)
return CBoundingBoxAligned::EMPTY;
return m_Unit->GetModel().GetWorldBounds();
}
virtual CUnit* GetUnit()
{
return m_Unit;
}
virtual CBoundingBoxOriented GetSelectionBox() const
{
if (!m_Unit)
return CBoundingBoxOriented::EMPTY;
return m_Unit->GetModel().GetSelectionBox();
}
virtual CVector3D GetPosition() const
{
if (!m_Unit)
return CVector3D(0, 0, 0);
return m_Unit->GetModel().GetTransform().GetTranslation();
}
virtual std::wstring GetActorShortName() const
{
if (!m_Unit)
return L"";
return m_Unit->GetObject().m_Base->m_ShortName;
}
virtual std::wstring GetProjectileActor() const
{
if (!m_Unit)
return L"";
return m_Unit->GetObject().m_ProjectileModelName;
}
virtual CFixedVector3D GetProjectileLaunchPoint() const
{
if (!m_Unit)
return CFixedVector3D();
if (m_Unit->GetModel().ToCModel())
{
// Ensure the prop transforms are correct
CmpPtr cmpUnitRenderer(GetSystemEntity());
CmpPtr cmpPosition(GetEntityHandle());
if (cmpUnitRenderer && cmpPosition)
{
float frameOffset = cmpUnitRenderer->GetFrameOffset();
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset));
m_Unit->GetModel().SetTransform(transform);
m_Unit->GetModel().ValidatePosition();
}
CModelAbstract* ammo = m_Unit->GetModel().ToCModel()->FindFirstAmmoProp();
if (ammo)
{
CVector3D vector = ammo->GetTransform().GetTranslation();
return CFixedVector3D(fixed::FromFloat(vector.X), fixed::FromFloat(vector.Y), fixed::FromFloat(vector.Z));
}
}
return CFixedVector3D();
}
virtual void SetVariant(const CStr& key, const CStr& selection)
{
if (m_VariantSelections[key] == selection)
return;
m_VariantSelections[key] = selection;
if (m_Unit)
{
m_Unit->SetEntitySelection(key, selection);
if (m_Unit->GetAnimation())
m_Unit->GetAnimation()->ReloadAnimation();
}
}
virtual std::string GetAnimationName() const
{
return m_AnimName;
}
virtual void SelectAnimation(const std::string& name, bool once = false, fixed speed = fixed::FromInt(1))
{
m_AnimName = name;
m_AnimOnce = once;
m_AnimSpeed = speed;
m_SoundGroup = L"";
m_AnimDesync = fixed::FromInt(1)/20; // TODO: make this an argument
m_AnimSyncRepeatTime = fixed::Zero();
m_AnimSyncOffsetTime = fixed::Zero();
SetVariant("animation", m_AnimName);
CmpPtr cmpSound(GetEntityHandle());
if (cmpSound)
m_SoundGroup = cmpSound->GetSoundGroup(wstring_from_utf8(m_AnimName));
if (!m_Unit || !m_Unit->GetAnimation() || !m_Unit->GetID())
return;
m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
}
virtual void SelectMovementAnimation(const std::string& name, fixed speed)
{
ENSURE(name == "idle" || name == "walk" || name == "run");
if (m_AnimName != "idle" && m_AnimName != "walk" && m_AnimName != "run")
return;
SelectAnimation(name, false, speed);
}
virtual void SetAnimationSyncRepeat(fixed repeattime)
{
m_AnimSyncRepeatTime = repeattime;
if (m_Unit && m_Unit->GetAnimation())
m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat());
}
virtual void SetAnimationSyncOffset(fixed actiontime)
{
m_AnimSyncOffsetTime = actiontime;
if (m_Unit && m_Unit->GetAnimation())
m_Unit->GetAnimation()->SetAnimationSyncOffset(m_AnimSyncOffsetTime.ToFloat());
}
virtual void SetShadingColor(fixed r, fixed g, fixed b, fixed a)
{
m_R = r;
m_G = g;
m_B = b;
UNUSED2(a); // TODO: why is this even an argument?
if (m_Unit)
{
CModelAbstract& model = m_Unit->GetModel();
model.SetShadingColor(CColor(m_R.ToFloat(), m_G.ToFloat(), m_B.ToFloat(), 1.0f));
}
}
virtual void SetVariable(const std::string& name, float value)
{
if (m_Unit)
m_Unit->GetModel().SetEntityVariable(name, value);
}
virtual u32 GetActorSeed() const
{
return m_Seed;
}
virtual void SetActorSeed(u32 seed)
{
if (seed == m_Seed)
return;
m_Seed = seed;
ReloadActor();
}
virtual bool HasConstructionPreview() const
{
return m_ConstructionPreview;
}
virtual void Hotload(const VfsPath& name)
{
if (!m_Unit)
return;
if (name != m_ActorName)
return;
ReloadActor();
}
private:
+ // Replace {phenotype} with the correct value in m_ActorName
+ void ParseActorName(std::wstring base);
+
/// Helper function shared by component init and actor reloading
- void InitModel(const CParamNode& paramNode);
+ void InitModel();
/// Helper method; initializes the model selection shape descriptor from XML. Factored out for readability of @ref Init.
void InitSelectionShapeDescriptor(const CParamNode& paramNode);
// ReloadActor is used when the actor or seed changes.
void ReloadActor();
// ReloadUnitAnimation is used for a minimal reloading upon deserialization, when the actor and seed are identical.
// It is also used by ReloadActor.
void ReloadUnitAnimation();
};
REGISTER_COMPONENT_TYPE(VisualActor)
// ------------------------------------------------------------------------------------------------------------------
-void CCmpVisualActor::InitModel(const CParamNode& paramNode)
+void CCmpVisualActor::ParseActorName(std::wstring base)
+{
+ CmpPtr cmpIdentity(GetEntityHandle());
+ const std::wstring pattern = L"{phenotype}";
+ if (cmpIdentity)
+ {
+ size_t pos = base.find(pattern);
+ while (pos != std::string::npos)
+ {
+ base.replace(pos, pattern.size(), cmpIdentity->GetPhenotype());
+ pos = base.find(pattern, pos + pattern.size());
+ }
+ }
+
+ m_ActorName = base;
+}
+
+void CCmpVisualActor::InitModel()
{
if (!GetSimContext().HasUnitManager())
return;
std::set selections;
std::wstring actorName = m_ActorName;
if (actorName.find(L".xml") == std::wstring::npos)
actorName += L".xml";
m_Unit = GetSimContext().GetUnitManager().CreateUnit(actorName, GetActorSeed(), selections);
if (!m_Unit)
return;
CModelAbstract& model = m_Unit->GetModel();
if (model.ToCModel())
{
u32 modelFlags = 0;
- if (paramNode.GetChild("SilhouetteDisplay").ToBool())
+ if (m_SilhouetteDisplay)
modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
- if (paramNode.GetChild("SilhouetteOccluder").ToBool())
+ if (m_SilhouetteOccluder)
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
CmpPtr cmpVisibility(GetEntityHandle());
if (cmpVisibility && cmpVisibility->GetAlwaysVisible())
modelFlags |= MODELFLAG_IGNORE_LOS;
model.ToCModel()->AddFlagsRec(modelFlags);
}
- if (paramNode.GetChild("DisableShadows").IsOk())
+ if (m_DisableShadows)
{
if (model.ToCModel())
model.ToCModel()->RemoveShadowsRec();
else if (model.ToCModelDecal())
model.ToCModelDecal()->RemoveShadows();
}
- // Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
- // Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
- // shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
- // which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
- // initialize the selection shape descriptor on-demand.
- InitSelectionShapeDescriptor(paramNode);
-
m_Unit->SetID(GetEntityId());
bool floating = m_Unit->GetObject().m_Base->m_Properties.m_FloatOnWater;
CmpPtr cmpPosition(GetEntityHandle());
if (cmpPosition)
cmpPosition->SetActorFloating(floating);
if (!m_ModelTag.valid())
{
CmpPtr cmpModelRenderer(GetSystemEntity());
if (cmpModelRenderer)
{
// TODO: this should account for all possible props, animations, etc,
// else we might accidentally cull the unit when it should be visible
CBoundingBoxAligned bounds = m_Unit->GetModel().GetWorldBoundsRec();
CBoundingSphere boundSphere = CBoundingSphere::FromSweptBox(bounds);
int flags = 0;
if (m_IsActorOnly)
flags |= ICmpUnitRenderer::ACTOR_ONLY;
if (m_VisibleInAtlasOnly)
flags |= ICmpUnitRenderer::VISIBLE_IN_ATLAS_ONLY;
m_ModelTag = cmpModelRenderer->AddUnit(GetEntityHandle(), m_Unit, boundSphere, flags);
}
}
+
+ // the model is now responsible for cleaning up the descriptor
+ if (m_ShapeDescriptor != nullptr)
+ m_Unit->GetModel().SetCustomSelectionShape(m_ShapeDescriptor);
}
void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode)
{
// by default, we don't need a custom selection shape and we can just keep the default behaviour
- CModelAbstract::CustomSelectionShape* shapeDescriptor = NULL;
+ m_ShapeDescriptor = nullptr;
const CParamNode& shapeNode = paramNode.GetChild("SelectionShape");
if (shapeNode.IsOk())
{
if (shapeNode.GetChild("Bounds").IsOk())
{
// default; no need to take action
}
else if (shapeNode.GetChild("Footprint").IsOk())
{
CmpPtr cmpFootprint(GetEntityHandle());
if (cmpFootprint)
{
ICmpFootprint::EShape fpShape; // fp stands for "footprint"
entity_pos_t fpSize0, fpSize1, fpHeight; // fp stands for "footprint"
cmpFootprint->GetShape(fpShape, fpSize0, fpSize1, fpHeight);
float size0 = fpSize0.ToFloat();
float size1 = fpSize1.ToFloat();
// TODO: we should properly distinguish between CIRCLE and SQUARE footprint shapes here, but since cylinders
// aren't implemented yet and are almost indistinguishable from boxes for small enough sizes anyway,
// we'll just use boxes for either case. However, for circular footprints the size0 and size1 values both
// represent the radius, so we do have to adjust them to match the size1 and size0's of square footprints
// (which represent the full width and depth).
if (fpShape == ICmpFootprint::CIRCLE)
{
size0 *= 2;
size1 *= 2;
}
- shapeDescriptor = new CModelAbstract::CustomSelectionShape;
- shapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
- shapeDescriptor->m_Size0 = size0;
- shapeDescriptor->m_Size1 = size1;
- shapeDescriptor->m_Height = fpHeight.ToFloat();
+ m_ShapeDescriptor = new CModelAbstract::CustomSelectionShape;
+ m_ShapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
+ m_ShapeDescriptor->m_Size0 = size0;
+ m_ShapeDescriptor->m_Size1 = size1;
+ m_ShapeDescriptor->m_Height = fpHeight.ToFloat();
}
else
{
LOGERROR("[VisualActor] Cannot apply footprint-based SelectionShape; Footprint component not initialized.");
}
}
else if (shapeNode.GetChild("Box").IsOk())
{
// TODO: we might need to support the ability to specify a different box center in the future
- shapeDescriptor = new CModelAbstract::CustomSelectionShape;
- shapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
- shapeDescriptor->m_Size0 = shapeNode.GetChild("Box").GetChild("@width").ToFixed().ToFloat();
- shapeDescriptor->m_Size1 = shapeNode.GetChild("Box").GetChild("@depth").ToFixed().ToFloat();
- shapeDescriptor->m_Height = shapeNode.GetChild("Box").GetChild("@height").ToFixed().ToFloat();
+ m_ShapeDescriptor = new CModelAbstract::CustomSelectionShape;
+ m_ShapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
+ m_ShapeDescriptor->m_Size0 = shapeNode.GetChild("Box").GetChild("@width").ToFixed().ToFloat();
+ m_ShapeDescriptor->m_Size1 = shapeNode.GetChild("Box").GetChild("@depth").ToFixed().ToFloat();
+ m_ShapeDescriptor->m_Height = shapeNode.GetChild("Box").GetChild("@height").ToFixed().ToFloat();
}
else if (shapeNode.GetChild("Cylinder").IsOk())
{
LOGWARNING("[VisualActor] TODO: Cylinder selection shapes are not yet implemented; defaulting to recursive bounding boxes");
}
else
{
// shouldn't happen by virtue of validation against schema
LOGERROR("[VisualActor] No selection shape specified");
}
}
-
- ENSURE(m_Unit);
- // the model is now responsible for cleaning up the descriptor
- m_Unit->GetModel().SetCustomSelectionShape(shapeDescriptor);
}
void CCmpVisualActor::ReloadActor()
{
if (!m_Unit)
return;
// Save some data from the old unit
CColor shading = m_Unit->GetModel().GetShadingColor();
player_id_t playerID = m_Unit->GetModel().GetPlayerID();
// Replace with the new unit
GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
// HACK: selection shape needs template data, but rather than storing all that data
// in the component, we load the template here and pass it into a helper function
CmpPtr cmpTemplateManager(GetSystemEntity());
const CParamNode* node = cmpTemplateManager->LoadLatestTemplate(GetEntityId());
ENSURE(node && node->GetChild("VisualActor").IsOk());
- InitModel(node->GetChild("VisualActor"));
+ InitSelectionShapeDescriptor(node->GetChild("VisualActor"));
+
+ InitModel();
ReloadUnitAnimation();
m_Unit->GetModel().SetShadingColor(shading);
m_Unit->GetModel().SetPlayerID(playerID);
if (m_ModelTag.valid())
{
CmpPtr cmpModelRenderer(GetSystemEntity());
CBoundingBoxAligned bounds = m_Unit->GetModel().GetWorldBoundsRec();
CBoundingSphere boundSphere = CBoundingSphere::FromSweptBox(bounds);
cmpModelRenderer->UpdateUnit(m_ModelTag, m_Unit, boundSphere);
}
}
void CCmpVisualActor::ReloadUnitAnimation()
{
if (!m_Unit)
return;
m_Unit->SetEntitySelection(m_VariantSelections);
if (!m_Unit->GetAnimation())
return;
m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
// We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate
if (!m_AnimSyncRepeatTime.IsZero())
m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat());
if (!m_AnimSyncOffsetTime.IsZero())
m_Unit->GetAnimation()->SetAnimationSyncOffset(m_AnimSyncOffsetTime.ToFloat());
}
Index: ps/trunk/source/simulation2/components/ICmpIdentity.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpIdentity.cpp (revision 22585)
+++ ps/trunk/source/simulation2/components/ICmpIdentity.cpp (revision 22586)
@@ -1,40 +1,45 @@
-/* Copyright (C) 2011 Wildfire Games.
+/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "ICmpIdentity.h"
#include "simulation2/system/InterfaceScripted.h"
#include "simulation2/scripting/ScriptComponent.h"
BEGIN_INTERFACE_WRAPPER(Identity)
END_INTERFACE_WRAPPER(Identity)
class CCmpIdentityScripted : public ICmpIdentity
{
public:
DEFAULT_SCRIPT_WRAPPER(IdentityScripted)
virtual std::string GetSelectionGroupName()
{
return m_Script.Call("GetSelectionGroupName");
}
+
+ virtual std::wstring GetPhenotype()
+ {
+ return m_Script.Call("GetPhenotype");
+ }
};
REGISTER_COMPONENT_SCRIPT_WRAPPER(IdentityScripted)