forked from 0ad/0ad
Replace {gender} by {phenotype} and support this tag in VisualActor.
A random phenotype can be chosen by giving multiple tokens to the template. This allows giving different looks to the same template. Comments By: stan, vladislav, elexis Patch By: Freagarach Reviewed By: wraitii Differential Revision: https://code.wildfiregames.com/D1955 This was SVN commit r22586.
This commit is contained in:
parent
d7a93c3b35
commit
eab4f9fdde
@ -17,7 +17,10 @@ Identity.prototype.Schema =
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<optional>" +
|
||||
"<element name='Gender' a:help='Unit gender for voices. Choices includes male or female.'>" +
|
||||
"<element name='Phenotype' a:help='Unit phenotype for voices and visual. If more than one is specified a random one will be chosen.'>" +
|
||||
"<attribute name='datatype'>" +
|
||||
"<value>tokens</value>" +
|
||||
"</attribute>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
@ -95,15 +98,12 @@ 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;
|
||||
@ -114,9 +114,22 @@ 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.Gender || "male"; // ugly default
|
||||
return this.template.Phenotype._string.split(/\s+/);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current Phenotype.
|
||||
* @return {string} The current phenotype.
|
||||
*/
|
||||
Identity.prototype.GetPhenotype = function()
|
||||
{
|
||||
return this.phenotype;
|
||||
};
|
||||
|
||||
Identity.prototype.GetRank = function()
|
||||
|
@ -35,16 +35,16 @@ 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();
|
||||
let lang = cmpIdentity.GetLang();
|
||||
// Replace the "{phenotype}" codes with this entity's phenotype ID
|
||||
let phenotype = cmpIdentity.GetPhenotype();
|
||||
|
||||
var soundName = this.template.SoundGroups[name].replace(/\{lang\}/g, lang)
|
||||
.replace(/\{gender\}/g, gender);
|
||||
var cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager);
|
||||
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);
|
||||
}
|
||||
|
@ -2,12 +2,13 @@ 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(), []);
|
||||
@ -20,7 +21,7 @@ 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",
|
||||
@ -39,7 +40,7 @@ cmpIdentity = ConstructComponent(6, "Identity", {
|
||||
|
||||
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"]);
|
||||
@ -50,3 +51,12 @@ TS_ASSERT_EQUALS(cmpIdentity.CanUseFormation("special/formations/skirmish"), tru
|
||||
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");
|
||||
|
@ -51,6 +51,7 @@
|
||||
special/formations/flank
|
||||
special/formations/battle_line
|
||||
</Formations>
|
||||
<Phenotype datatype="tokens">male</Phenotype>
|
||||
<Undeletable>false</Undeletable>
|
||||
</Identity>
|
||||
<Looter/>
|
||||
|
@ -80,11 +80,11 @@
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<walk>actor/mounted/movement/walk.xml</walk>
|
||||
<run>actor/mounted/movement/walk.xml</run>
|
||||
<attack_impact_ranged>attack/impact/arrow_metal.xml</attack_impact_ranged>
|
||||
|
@ -42,11 +42,11 @@
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<walk>actor/mounted/movement/walk.xml</walk>
|
||||
<run>actor/mounted/movement/walk.xml</run>
|
||||
<attack_melee>attack/weapon/sword.xml</attack_melee>
|
||||
|
@ -32,16 +32,16 @@
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<trained>interface/alarm/alarm_create_infantry.xml</trained>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{gender}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{phenotype}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<walk>actor/human/movement/walk.xml</walk>
|
||||
<run>actor/human/movement/walk.xml</run>
|
||||
<attack_melee>attack/weapon/sword.xml</attack_melee>
|
||||
<death>actor/human/death/{gender}_death.xml</death>
|
||||
<death>actor/human/death/{phenotype}_death.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<Vision>
|
||||
|
@ -54,20 +54,20 @@
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<trained>interface/alarm/alarm_create_infantry.xml</trained>
|
||||
<order_heal>voice/{lang}/civ/civ_{gender}_heal.xml</order_heal>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{gender}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<order_heal>voice/{lang}/civ/civ_{phenotype}_heal.xml</order_heal>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{phenotype}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<attack_melee>attack/weapon/sword.xml</attack_melee>
|
||||
<attack_impact_ranged>attack/impact/arrow_metal.xml</attack_impact_ranged>
|
||||
<attack_ranged>attack/weapon/arrowfly.xml</attack_ranged>
|
||||
<walk>actor/human/movement/walk.xml</walk>
|
||||
<run>actor/human/movement/walk.xml</run>
|
||||
<death>actor/human/death/{gender}_death.xml</death>
|
||||
<death>actor/human/death/{phenotype}_death.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingRestrictions>
|
||||
|
@ -100,18 +100,18 @@
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{gender}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{phenotype}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<walk>actor/human/movement/walk.xml</walk>
|
||||
<run>actor/human/movement/run.xml</run>
|
||||
<attack_impact_ranged>attack/impact/arrow_metal.xml</attack_impact_ranged>
|
||||
<attack_melee>attack/weapon/sword.xml</attack_melee>
|
||||
<attack_ranged>attack/weapon/arrowfly.xml</attack_ranged>
|
||||
<death>actor/human/death/{gender}_death.xml</death>
|
||||
<death>actor/human/death/{phenotype}_death.xml</death>
|
||||
<build>resource/construction/con_wood.xml</build>
|
||||
<gather_fruit>resource/foraging/forage_leaves.xml</gather_fruit>
|
||||
<gather_grain>resource/farming/farm.xml</gather_grain>
|
||||
|
@ -48,7 +48,7 @@
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Female Citizen</GenericName>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<Classes datatype="tokens">FemaleCitizen</Classes>
|
||||
<VisibleClasses datatype="tokens">Citizen Worker</VisibleClasses>
|
||||
<Formations disable=""/>
|
||||
@ -70,15 +70,15 @@
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<trained>interface/alarm/alarm_create_female.xml</trained>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_build>voice/{lang}/civ/civ_{gender}_build.xml</order_build>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{gender}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_build>voice/{lang}/civ/civ_{phenotype}_build.xml</order_build>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{phenotype}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<attack_melee>attack/weapon/sword.xml</attack_melee>
|
||||
<death>actor/human/death/{gender}_death.xml</death>
|
||||
<death>actor/human/death/{phenotype}_death.xml</death>
|
||||
<build>resource/construction/con_wood.xml</build>
|
||||
<gather_fruit>resource/foraging/forage_leaves.xml</gather_fruit>
|
||||
<gather_grain>resource/farming/farm.xml</gather_grain>
|
||||
|
@ -42,16 +42,16 @@
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<trained>interface/alarm/alarm_create_infantry.xml</trained>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{gender}_repair.xml</order_repair>
|
||||
<order_heal>voice/{lang}/civ/civ_{gender}_heal.xml</order_heal>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{phenotype}_repair.xml</order_repair>
|
||||
<order_heal>voice/{lang}/civ/civ_{phenotype}_heal.xml</order_heal>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<walk>actor/human/movement/walk.xml</walk>
|
||||
<run>actor/human/movement/run.xml</run>
|
||||
<death>actor/human/death/{gender}_death.xml</death>
|
||||
<death>actor/human/death/{phenotype}_death.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<Vision>
|
||||
|
@ -65,14 +65,14 @@
|
||||
</ResourceGatherer>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{gender}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{gender}_garrison.xml</order_garrison>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{phenotype}_repair.xml</order_repair>
|
||||
<order_garrison>voice/{lang}/civ/civ_{phenotype}_garrison.xml</order_garrison>
|
||||
<walk>actor/human/movement/walk.xml</walk>
|
||||
<run>actor/human/movement/run.xml</run>
|
||||
<death>actor/human/death/{gender}_death.xml</death>
|
||||
<death>actor/human/death/{phenotype}_death.xml</death>
|
||||
<build>resource/construction/con_wood.xml</build>
|
||||
<gather_fruit>resource/foraging/forage_leaves.xml</gather_fruit>
|
||||
<gather_grain>resource/farming/farm.xml</gather_grain>
|
||||
|
@ -19,16 +19,16 @@
|
||||
</Identity>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>voice/{lang}/civ/civ_{gender}_select.xml</select>
|
||||
<order_trade>voice/{lang}/civ/civ_{gender}_trade.xml</order_trade>
|
||||
<order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{gender}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{gender}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{gender}_repair.xml</order_repair>
|
||||
<select>voice/{lang}/civ/civ_{phenotype}_select.xml</select>
|
||||
<order_trade>voice/{lang}/civ/civ_{phenotype}_trade.xml</order_trade>
|
||||
<order_walk>voice/{lang}/civ/civ_{phenotype}_walk.xml</order_walk>
|
||||
<order_attack>voice/{lang}/civ/civ_{phenotype}_attack.xml</order_attack>
|
||||
<order_gather>voice/{lang}/civ/civ_{phenotype}_gather.xml</order_gather>
|
||||
<order_repair>voice/{lang}/civ/civ_{phenotype}_repair.xml</order_repair>
|
||||
<walk>actor/human/movement/walk.xml</walk>
|
||||
<run>actor/human/movement/run.xml</run>
|
||||
<attack_melee>attack/weapon/sword.xml</attack_melee>
|
||||
<death>actor/human/death/{gender}_death.xml</death>
|
||||
<death>actor/human/death/{phenotype}_death.xml</death>
|
||||
<build>resource/construction/con_wood.xml</build>
|
||||
<gather_fruit>resource/foraging/forage_leaves.xml</gather_fruit>
|
||||
<gather_grain>resource/farming/farm.xml</gather_grain>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<VisibleClasses datatype="tokens">Chariot</VisibleClasses>
|
||||
<GenericName>Boudicca (Chariot)</GenericName>
|
||||
<SpecificName>Boadicea</SpecificName>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<Icon>units/brit_hero_boudicca.png</Icon>
|
||||
</Identity>
|
||||
<VisualActor>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<Civ>brit</Civ>
|
||||
<GenericName>Boudicca (Sword)</GenericName>
|
||||
<SpecificName>Boadicea</SpecificName>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<Icon>units/brit_hero_boudicca.png</Icon>
|
||||
</Identity>
|
||||
<VisualActor>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<Civ>brit</Civ>
|
||||
<GenericName>Boudicca (Sword)</GenericName>
|
||||
<SpecificName>Boadicea</SpecificName>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<Icon>units/brit_hero_boudicca.png</Icon>
|
||||
</Identity>
|
||||
<VisualActor>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Entity parent="template_unit_support_healer">
|
||||
<Identity>
|
||||
<Civ>cart</Civ>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<SelectionGroupName>units/cart_support_healer_b</SelectionGroupName>
|
||||
<SpecificName>Kehinit</SpecificName>
|
||||
<Icon>units/cart_support_healer.png</Icon>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Entity parent="template_unit_support_healer">
|
||||
<Identity>
|
||||
<Civ>iber</Civ>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<SelectionGroupName>units/iber_support_healer_b</SelectionGroupName>
|
||||
<GenericName>Priestess of Ataekina</GenericName>
|
||||
<SpecificName>Emakumezko Apaiz de Ataekina</SpecificName>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<Civ>kush</Civ>
|
||||
<GenericName>Amanirenas</GenericName>
|
||||
<SpecificName>Amnirense qore li kdwe li</SpecificName>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<Icon>units/kush_hero_amanirenas.png</Icon>
|
||||
</Identity>
|
||||
<VisualActor>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<Civ>kush</Civ>
|
||||
<GenericName>Amanirenas</GenericName>
|
||||
<SpecificName>Amnirense qore li kdwe li</SpecificName>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<Icon>units/kush_hero_amanirenas.png</Icon>
|
||||
</Identity>
|
||||
<VisualActor>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Entity parent="template_unit_champion_infantry_swordsman">
|
||||
<Identity>
|
||||
<Civ>maur</Civ>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<GenericName>Maiden Guard</GenericName>
|
||||
<SpecificName>Visha Kanya</SpecificName>
|
||||
<SelectionGroupName>units/maur_champion_maiden</SelectionGroupName>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Entity parent="template_unit_champion_infantry_archer">
|
||||
<Identity>
|
||||
<Civ>maur</Civ>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<GenericName>Maiden Guard Archer</GenericName>
|
||||
<SpecificName>Visha Kanya</SpecificName>
|
||||
<Icon>units/maur_champion_maiden_archer.png</Icon>
|
||||
|
@ -7,7 +7,7 @@
|
||||
</Auras>
|
||||
<Identity>
|
||||
<Civ>ptol</Civ>
|
||||
<Gender>female</Gender>
|
||||
<Phenotype datatype="tokens">female</Phenotype>
|
||||
<GenericName>Cleopatra VII</GenericName>
|
||||
<SpecificName>Kleopatra H' Philopator</SpecificName>
|
||||
<Icon>units/ptol_hero_cleopatra.png</Icon>
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "simulation2/MessageTypes.h"
|
||||
|
||||
#include "ICmpFootprint.h"
|
||||
#include "ICmpIdentity.h"
|
||||
#include "ICmpUnitRenderer.h"
|
||||
#include "ICmpOwnership.h"
|
||||
#include "ICmpPosition.h"
|
||||
@ -59,6 +60,7 @@ public:
|
||||
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
|
||||
componentManager.SubscribeToMessageType(MT_ValueModification);
|
||||
componentManager.SubscribeToMessageType(MT_TerrainChanged);
|
||||
componentManager.SubscribeToMessageType(MT_Create);
|
||||
componentManager.SubscribeToMessageType(MT_Destroy);
|
||||
}
|
||||
|
||||
@ -70,6 +72,7 @@ private:
|
||||
|
||||
// Not initialized in non-visual mode
|
||||
CUnit* m_Unit;
|
||||
CModelAbstract::CustomSelectionShape* m_ShapeDescriptor = nullptr;
|
||||
|
||||
fixed m_R, m_G, m_B; // shading color
|
||||
|
||||
@ -91,6 +94,10 @@ private:
|
||||
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:
|
||||
@ -195,17 +202,23 @@ public:
|
||||
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);
|
||||
m_SilhouetteDisplay = paramNode.GetChild("SilhouetteDisplay").ToBool();
|
||||
m_SilhouetteOccluder = paramNode.GetChild("SilhouetteOccluder").ToBool();
|
||||
m_DisableShadows = paramNode.GetChild("DisableShadows").ToBool();
|
||||
|
||||
SelectAnimation("idle");
|
||||
// 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()
|
||||
@ -260,6 +273,9 @@ public:
|
||||
|
||||
SerializeCommon(deserialize);
|
||||
|
||||
InitModel();
|
||||
SelectAnimation("idle");
|
||||
|
||||
// If we serialized a different seed or different actor, reload actor
|
||||
if (oldSeed != GetActorSeed() || m_BaseActorName != m_ActorName)
|
||||
ReloadActor();
|
||||
@ -309,7 +325,7 @@ public:
|
||||
newActorName = cmpValueModificationManager->ApplyModifications(L"VisualActor/Actor", m_BaseActorName, GetEntityId());
|
||||
if (newActorName != m_ActorName)
|
||||
{
|
||||
m_ActorName = newActorName;
|
||||
ParseActorName(newActorName);
|
||||
ReloadActor();
|
||||
}
|
||||
break;
|
||||
@ -324,6 +340,13 @@ public:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_Create:
|
||||
{
|
||||
InitModel();
|
||||
|
||||
SelectAnimation("idle");
|
||||
break;
|
||||
}
|
||||
case MT_Destroy:
|
||||
{
|
||||
if (m_ModelTag.valid())
|
||||
@ -523,8 +546,11 @@ public:
|
||||
}
|
||||
|
||||
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);
|
||||
@ -540,7 +566,24 @@ REGISTER_COMPONENT_TYPE(VisualActor)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
||||
void CCmpVisualActor::ParseActorName(std::wstring base)
|
||||
{
|
||||
CmpPtr<ICmpIdentity> 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;
|
||||
@ -558,10 +601,10 @@ void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
||||
{
|
||||
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<ICmpVisibility> cmpVisibility(GetEntityHandle());
|
||||
@ -571,7 +614,7 @@ void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
||||
model.ToCModel()->AddFlagsRec(modelFlags);
|
||||
}
|
||||
|
||||
if (paramNode.GetChild("DisableShadows").IsOk())
|
||||
if (m_DisableShadows)
|
||||
{
|
||||
if (model.ToCModel())
|
||||
model.ToCModel()->RemoveShadowsRec();
|
||||
@ -579,13 +622,6 @@ void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
||||
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;
|
||||
@ -612,12 +648,16 @@ void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
||||
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())
|
||||
@ -649,11 +689,11 @@ void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode)
|
||||
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
|
||||
{
|
||||
@ -663,11 +703,11 @@ void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode)
|
||||
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())
|
||||
{
|
||||
@ -679,10 +719,6 @@ void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode)
|
||||
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()
|
||||
@ -703,7 +739,9 @@ void CCmpVisualActor::ReloadActor()
|
||||
const CParamNode* node = cmpTemplateManager->LoadLatestTemplate(GetEntityId());
|
||||
ENSURE(node && node->GetChild("VisualActor").IsOk());
|
||||
|
||||
InitModel(node->GetChild("VisualActor"));
|
||||
InitSelectionShapeDescriptor(node->GetChild("VisualActor"));
|
||||
|
||||
InitModel();
|
||||
|
||||
ReloadUnitAnimation();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* 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
|
||||
@ -35,6 +35,11 @@ public:
|
||||
{
|
||||
return m_Script.Call<std::string>("GetSelectionGroupName");
|
||||
}
|
||||
|
||||
virtual std::wstring GetPhenotype()
|
||||
{
|
||||
return m_Script.Call<std::wstring>("GetPhenotype");
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_SCRIPT_WRAPPER(IdentityScripted)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* 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
|
||||
@ -29,6 +29,8 @@ class ICmpIdentity : public IComponent
|
||||
public:
|
||||
virtual std::string GetSelectionGroupName() = 0;
|
||||
|
||||
virtual std::wstring GetPhenotype() = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Identity)
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user