1
0
forked from 0ad/0ad

Allow variants to be defined in external files. Fixes #3286

This was SVN commit r18054.
This commit is contained in:
sanderd17 2016-04-19 07:51:59 +00:00
parent 5d8e5edbfb
commit 1e6c12de7b
13 changed files with 548 additions and 218 deletions

View File

@ -9,6 +9,7 @@ element actor {
element group {
element variant {
attribute name { text }? &
attribute file { text }? &
attribute frequency { xsd:nonNegativeInteger }? &
element mesh {
text

View File

@ -18,6 +18,9 @@
<optional>
<attribute name="name"/>
</optional>
<optional>
<attribute name="file"/>
</optional>
<optional>
<attribute name="frequency">
<data type="nonNegativeInteger"/>

View File

@ -6,9 +6,6 @@
<animations>
<animation file="biped/inf_sword_ready_a.dae" name="Idle" speed="100"/>
<animation file="biped/inf_sword_ready_a.dae" name="Idle" speed="97"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_01.psa" name="attack_capture" speed="100"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_06.psa" name="attack_capture" speed="100"/>
<animation event="0.2" file="infantry/sword/attack/isw_s_em_04.psa" name="attack_capture" speed="100"/>
<animation event="0.5" file="infantry/sword/attack/isw_s_off_05.psa" name="attack_slaughter" speed="100"/>
<animation event="0.84" file="biped/inf_arch_atk_a.psa" load="0.16" name="attack_ranged" speed="90"/>
<animation file="infantry/general/dude/dudewalk.psa" name="Walk" speed="100"/>
@ -44,11 +41,7 @@
</group>
<group>
<variant frequency="100" name="Idle"/>
<variant name="attack_capture">
<props>
<prop actor="props/units/tools/pitchfork.xml" attachpoint="r_hand"/>
</props>
</variant>
<variant file="biped/attack_capture.xml"/>
<variant name="attack_ranged">
<props>
<prop actor="props/units/weapons/bow_short.xml" attachpoint="l_hand"/>

View File

@ -5,9 +5,6 @@
<variant frequency="100" name="Base">
<animations>
<animation file="infantry/general/dude/dudeidle.psa" name="Idle" speed="100"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_01.psa" name="attack_capture" speed="100"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_06.psa" name="attack_capture" speed="100"/>
<animation event="0.2" file="infantry/sword/attack/isw_s_em_04.psa" name="attack_capture" speed="100"/>
<animation event="0.5" file="infantry/javelin/attack/ijv_off_01.psa" load="0" name="attack_ranged" speed="75"/>
<animation event="0.5" file="infantry/sword/attack/isw_s_off_05.psa" name="attack_slaughter" speed="100"/>
<animation file="biped/walk_spearshield.psa" name="Walk" speed="120"/>
@ -48,11 +45,7 @@
</group>
<group>
<variant frequency="100" name="Idle"/>
<variant name="attack_capture">
<props>
<prop actor="props/units/tools/pitchfork.xml" attachpoint="r_hand"/>
</props>
</variant>
<variant file="biped/attack_capture.xml"/>
<variant name="attack_ranged">
<props>
<prop attachpoint="r_hand"/>

View File

@ -11,9 +11,6 @@
<animation file="biped/walk_spearshield.psa" name="carry_wood" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_stone" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_metal" speed="120"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_01.psa" name="attack_capture" speed="100"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_06.psa" name="attack_capture" speed="100"/>
<animation event="0.2" file="infantry/sword/attack/isw_s_em_04.psa" name="attack_capture" speed="100"/>
<animation event="0.5" file="biped/inf_sling_atk_a.psa" name="attack_ranged" speed="30"/>
<animation event="0.5" file="infantry/sword/attack/isw_s_off_05.psa" name="attack_slaughter" speed="100"/>
@ -46,11 +43,7 @@
</group>
<group>
<variant frequency="1" name="Idle"/>
<variant name="attack_capture">
<props>
<prop actor="props/units/tools/pitchfork.xml" attachpoint="r_hand"/>
</props>
</variant>
<variant file="biped/attack_capture.xml"/>
<variant name="attack_slaughter">
<props>
<prop actor="props/units/weapons/knife.xml" attachpoint="r_hand"/>

View File

@ -12,9 +12,6 @@
<animation file="biped/walk_spearshield.psa" name="carry_wood" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_stone" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_metal" speed="120"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_01.psa" name="attack_capture" speed="100"/>
<animation event="0.4" file="infantry/sword/attack/isw_s_def_06.psa" name="attack_capture" speed="100"/>
<animation event="0.2" file="infantry/sword/attack/isw_s_em_04.psa" name="attack_capture" speed="100"/>
<animation event="0.5" file="biped/inf_hoplite_atk_a.psa" name="attack_melee" speed="200"/>
<animation event="0.5" file="biped/inf_hoplite_atk_a.psa" name="attack_slaughter" speed="200"/>
<animation event="0.23" file="infantry/general/chop.psa" name="gather_tree" speed="250"/>
@ -55,11 +52,7 @@
</group>
<group>
<variant frequency="1" name="Idle"/>
<variant name="attack_capture">
<props>
<prop actor="props/units/tools/pitchfork.xml" attachpoint="r_hand"/>
</props>
</variant>
<variant file="biped/attack_capture.xml"/>
<variant name="attack_melee">
<props>
<prop actor="props/units/weapons/spear_hoplite.xml" attachpoint="r_hand"/>

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<actor version="1">
<castshadow/>
<group>
<variant frequency="1" name="Base">
<animations>
<animation file="infantry/spear/idle/isp_01.psa" name="Idle" speed="200"/>
<animation file="biped/walk_spearshield.psa" name="Walk" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_food" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_meat" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_wood" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_stone" speed="120"/>
<animation file="biped/walk_spearshield.psa" name="carry_metal" speed="120"/>
<animation event="0.5" file="biped/inf_sling_atk_a.psa" name="attack_ranged" speed="30"/>
<animation event="0.5" file="infantry/sword/attack/isw_s_off_05.psa" name="attack_slaughter" speed="100"/>
<animation file="infantry/sword/move/run/isw_s_off_01.psa" name="Run" speed="38"/>
<animation file="infantry/sword/move/run/isw_s_def_02.psa" name="Run" speed="35"/>
<animation event="0.23" file="infantry/general/chop.psa" name="gather_tree" speed="250"/>
<animation file="biped/hoe.psa" name="gather_grain" speed="300"/>
<animation file="infantry/general/forage.psa" name="gather_fruit" speed="125"/>
<animation file="infantry/general/forage.psa" name="gather_meat" speed="125"/>
<animation event="0.43" file="infantry/general/mine.psa" name="gather_rock" speed="250"/>
<animation event="0.43" file="infantry/general/mine.psa" name="gather_ore" speed="250"/>
<animation event="0.43" file="infantry/general/mine.psa" name="gather_ruins" speed="250"/>
<animation event="0.6" file="infantry/general/dude/dudebuild.psa" name="Build" speed="300"/>
<animation file="infantry/general/death/inf_01.psa" name="Death" speed="400"/>
<animation file="infantry/general/death/inf_02.psa" name="Death" speed="700"/>
<animation file="infantry/general/death/inf_03.psa" name="Death" speed="500"/>
<animation file="infantry/general/death/inf_04.psa" name="Death" speed="400"/>
<animation file="infantry/general/death/inf_06.psa" name="Death" speed="500"/>
<animation file="infantry/general/death/inf_07.psa" name="Death" speed="400"/>
<animation file="biped/inf_salute_c.psa" name="Promotion" speed="450"/>
</animations>
<mesh>skeletal/m_tunic_short.dae</mesh>
<props>
<prop actor="props/units/heads/head_hele_b.xml" attachpoint="head"/>
<prop actor="props/units/weapons/sling.xml" attachpoint="r_hand"/>
<prop actor="props/units/weapons/slingrock.xml" attachpoint="projectile"/>
</props>
<textures>
<texture file="skeletal/hele_tunic.dds" name="baseTex"/>
</textures>
</variant>
</group>
<group>
<variant frequency="1" name="Idle"/>
<variant file="biped/attack_capture.xml"/>
<variant name="attack_slaughter">
<props>
<prop actor="props/units/weapons/knife.xml" attachpoint="r_hand"/>
</props>
</variant>
<variant name="gather_tree">
<props>
<prop actor="props/units/tools/axe.xml" attachpoint="l_hand"/>
<prop attachpoint="r_hand"/>
</props>
</variant>
<variant name="gather_grain">
<props>
<prop actor="props/units/tools/hoe.xml" attachpoint="l_hand"/>
<prop attachpoint="r_hand"/>
</props>
</variant>
<variant name="gather_fruit">
<props>
<prop actor="props/units/tools/basket.xml" attachpoint="l_leg"/>
<prop attachpoint="r_hand"/>
</props>
</variant>
<variant name="gather_meat">
<props>
<prop attachpoint="r_hand"/>
<prop actor="props/units/tools/basket.xml" attachpoint="l_leg"/>
</props>
</variant>
<variant name="gather_rock">
<props>
<prop actor="props/units/tools/pick.xml" attachpoint="l_hand"/>
<prop attachpoint="r_hand"/>
</props>
</variant>
<variant name="gather_ore">
<props>
<prop actor="props/units/tools/pick.xml" attachpoint="l_hand"/>
<prop attachpoint="r_hand"/>
</props>
</variant>
<variant name="gather_ruins">
<props>
<prop actor="props/units/tools/pick.xml" attachpoint="l_hand"/>
<prop attachpoint="r_hand"/>
</props>
</variant>
<variant name="Build">
<props>
<prop actor="props/units/tools/mallet.xml" attachpoint="r_hand"/>
<prop attachpoint="l_hand"/>
</props>
</variant>
<variant name="carry_food">
<props>
<prop actor="props/units/shuttle_basket.xml" attachpoint="r_hand"/>
<prop attachpoint="l_hand"/>
</props>
</variant>
<variant name="carry_meat">
<props>
<prop actor="props/units/shuttle_meat.xml" attachpoint="r_hand"/>
<prop attachpoint="l_hand"/>
</props>
</variant>
<variant name="carry_wood">
<props>
<prop actor="props/units/shuttle_wood.xml" attachpoint="r_hand"/>
<prop actor="" attachpoint="l_hand"/>
<prop attachpoint="shield"/>
</props>
</variant>
<variant name="carry_stone">
<props>
<prop actor="props/units/shuttle_stone.xml" attachpoint="r_hand"/>
<prop actor="" attachpoint="l_hand"/>
</props>
</variant>
<variant name="carry_metal">
<props>
<prop actor="props/units/shuttle_metal.xml" attachpoint="r_hand"/>
<prop actor="" attachpoint="l_hand"/>
</props>
</variant>
</group>
<material>player_trans.xml</material>
</actor>

View File

@ -0,0 +1,54 @@
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
##
# NOTE: To modify this Relax NG grammar, edit the Relax NG Compact (.rnc) file
# and use a converter tool like trang to generate the Relax NG XML (.rng) file
##
element variant {
attribute name { text }? &
attribute frequency { xsd:nonNegativeInteger }? &
element mesh {
text
}? &
element textures {
element texture {
attribute file { text }? &
attribute name { text }
}*
}? &
element decal {
attribute width { xsd:float } & # X
attribute depth { xsd:float } & # Z
attribute angle { xsd:float } &
attribute offsetx { xsd:float } &
attribute offsetz { xsd:float }
}? &
element particles {
attribute file { text }
}? &
element color { list {
xsd:nonNegativeInteger, # R
xsd:nonNegativeInteger, # G
xsd:nonNegativeInteger # B
} }? &
element animations {
element animation {
attribute name { text } &
attribute file { text }? &
attribute speed { xsd:nonNegativeInteger } &
attribute event { xsd:decimal { minInclusive = "0" maxInclusive = "1" } }? &
attribute load { xsd:decimal { minInclusive = "0" maxInclusive = "1" } }? &
attribute sound { xsd:decimal { minInclusive = "0" maxInclusive = "1" } }?
}*
}? &
element props {
element prop {
(attribute actor { text }? &
attribute attachpoint { text } &
attribute minheight { xsd:float }? &
attribute maxheight { xsd:float }? &
attribute selectable { "true" | "false" }?)
}*
}?
}

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<element name="variant" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<a:documentation/>
<!--
NOTE: To modify this Relax NG grammar, edit the Relax NG Compact (.rnc) file
and use a converter tool like trang to generate the Relax NG XML (.rng) file
-->
<a:documentation/>
<interleave>
<optional>
<attribute name="name"/>
</optional>
<optional>
<attribute name="frequency">
<data type="nonNegativeInteger"/>
</attribute>
</optional>
<optional>
<element name="mesh">
<text/>
</element>
</optional>
<optional>
<element name="textures">
<zeroOrMore>
<element name="texture">
<interleave>
<optional>
<attribute name="file"/>
</optional>
<attribute name="name"/>
</interleave>
</element>
</zeroOrMore>
</element>
</optional>
<optional>
<element name="decal">
<interleave>
<attribute name="width">
<data type="float"/>
</attribute>
<!-- X -->
<attribute name="depth">
<data type="float"/>
</attribute>
<!-- Z -->
<attribute name="angle">
<data type="float"/>
</attribute>
<attribute name="offsetx">
<data type="float"/>
</attribute>
<attribute name="offsetz">
<data type="float"/>
</attribute>
</interleave>
</element>
</optional>
<optional>
<element name="particles">
<attribute name="file"/>
</element>
</optional>
<optional>
<element name="color">
<list>
<group>
<data type="nonNegativeInteger"/>
<!-- R -->
<data type="nonNegativeInteger"/>
<!-- G -->
<data type="nonNegativeInteger"/>
</group>
<!-- B -->
</list>
</element>
</optional>
<optional>
<element name="animations">
<zeroOrMore>
<element name="animation">
<interleave>
<attribute name="name"/>
<optional>
<attribute name="file"/>
</optional>
<attribute name="speed">
<data type="nonNegativeInteger"/>
</attribute>
<optional>
<attribute name="event">
<data type="decimal">
<param name="minInclusive">0</param>
<param name="maxInclusive">1</param>
</data>
</attribute>
</optional>
<optional>
<attribute name="load">
<data type="decimal">
<param name="minInclusive">0</param>
<param name="maxInclusive">1</param>
</data>
</attribute>
</optional>
<optional>
<attribute name="sound">
<data type="decimal">
<param name="minInclusive">0</param>
<param name="maxInclusive">1</param>
</data>
</attribute>
</optional>
</interleave>
</element>
</zeroOrMore>
</element>
</optional>
<optional>
<element name="props">
<zeroOrMore>
<element name="prop">
<interleave>
<optional>
<attribute name="actor"/>
</optional>
<attribute name="attachpoint"/>
<optional>
<attribute name="minheight">
<data type="float"/>
</attribute>
</optional>
<optional>
<attribute name="maxheight">
<data type="float"/>
</attribute>
</optional>
<optional>
<attribute name="selectable">
<choice>
<value>true</value>
<value>false</value>
</choice>
</attribute>
</optional>
</interleave>
</element>
</zeroOrMore>
</element>
</optional>
</interleave>
</element>

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -38,6 +38,171 @@ CObjectBase::CObjectBase(CObjectManager& objectManager)
m_Properties.m_FloatOnWater = false;
}
void CObjectBase::LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant)
{
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(animation);
EL(animations);
EL(color);
EL(decal);
EL(mesh);
EL(particles);
EL(prop);
EL(props);
EL(texture);
EL(textures);
EL(variant);
AT(actor);
AT(angle);
AT(attachpoint);
AT(depth);
AT(event);
AT(file);
AT(frequency);
AT(load);
AT(maxheight);
AT(minheight);
AT(name);
AT(offsetx);
AT(offsetz);
AT(selectable);
AT(sound);
AT(speed);
AT(width);
#undef AT
#undef EL
if (variant.GetNodeName() != el_variant)
{
LOGERROR("Invalid variant format (unrecognised root element '%s')", XeroFile.GetElementString(variant.GetNodeName()).c_str());
return;
}
XERO_ITER_ATTR(variant, attr)
{
if (attr.Name == at_file)
{
// Open up an external file to load.
// Don't crash hard when failures happen, but log them and continue
m_UsedFiles.insert(attr.Value);
CXeromyces XeroVariant;
if (XeroVariant.Load(g_VFS, "art/variants/" + attr.Value) == PSRETURN_OK)
{
XMBElement variantRoot = XeroVariant.GetRoot();
LoadVariant(XeroVariant, variantRoot, currentVariant);
}
else
LOGERROR("Could not open path %s", attr.Value);
// Continue loading extra definitions in this variant to allow nested files
}
else if (attr.Name == at_name)
currentVariant.m_VariantName = attr.Value.LowerCase();
else if (attr.Name == at_frequency)
currentVariant.m_Frequency = attr.Value.ToInt();
}
XERO_ITER_EL(variant, option)
{
int option_name = option.GetNodeName();
if (option_name == el_mesh)
{
currentVariant.m_ModelFilename = VfsPath("art/meshes") / option.GetText().FromUTF8();
}
else if (option_name == el_textures)
{
XERO_ITER_EL(option, textures_element)
{
ENSURE(textures_element.GetNodeName() == el_texture);
Samp samp;
XERO_ITER_ATTR(textures_element, se)
{
if (se.Name == at_file)
samp.m_SamplerFile = VfsPath("art/textures/skins") / se.Value.FromUTF8();
else if (se.Name == at_name)
samp.m_SamplerName = CStrIntern(se.Value);
}
currentVariant.m_Samplers.push_back(samp);
}
}
else if (option_name == el_decal)
{
XMBAttributeList attrs = option.GetAttributes();
Decal decal;
decal.m_SizeX = attrs.GetNamedItem(at_width).ToFloat();
decal.m_SizeZ = attrs.GetNamedItem(at_depth).ToFloat();
decal.m_Angle = DEGTORAD(attrs.GetNamedItem(at_angle).ToFloat());
decal.m_OffsetX = attrs.GetNamedItem(at_offsetx).ToFloat();
decal.m_OffsetZ = attrs.GetNamedItem(at_offsetz).ToFloat();
currentVariant.m_Decal = decal;
}
else if (option_name == el_particles)
{
XMBAttributeList attrs = option.GetAttributes();
VfsPath file = VfsPath("art/particles") / attrs.GetNamedItem(at_file).FromUTF8();
currentVariant.m_Particles = file;
// For particle hotloading, it's easiest to reload the entire actor,
// so remember the relevant particle file as a dependency for this actor
m_UsedFiles.insert(file);
}
else if (option_name == el_color)
{
currentVariant.m_Color = option.GetText();
}
else if (option_name == el_animations)
{
XERO_ITER_EL(option, anim_element)
{
ENSURE(anim_element.GetNodeName() == el_animation);
Anim anim;
XERO_ITER_ATTR(anim_element, ae)
{
if (ae.Name == at_name)
anim.m_AnimName = ae.Value;
else if (ae.Name == at_file)
anim.m_FileName = VfsPath("art/animation") / ae.Value.FromUTF8();
else if (ae.Name == at_speed)
anim.m_Speed = ae.Value.ToInt() > 0 ? ae.Value.ToInt() / 100.f : 1.f;
else if (ae.Name == at_event)
anim.m_ActionPos = clamp(ae.Value.ToFloat(), 0.f, 1.f);
else if (ae.Name == at_load)
anim.m_ActionPos2 = clamp(ae.Value.ToFloat(), 0.f, 1.f);
else if (ae.Name == at_sound)
anim.m_SoundPos = clamp(ae.Value.ToFloat(), 0.f, 1.f);
}
currentVariant.m_Anims.push_back(anim);
}
}
else if (option_name == el_props)
{
XERO_ITER_EL(option, prop_element)
{
ENSURE(prop_element.GetNodeName() == el_prop);
Prop prop;
XERO_ITER_ATTR(prop_element, pe)
{
if (pe.Name == at_attachpoint)
prop.m_PropPointName = pe.Value;
else if (pe.Name == at_actor)
prop.m_ModelName = pe.Value.FromUTF8();
else if (pe.Name == at_minheight)
prop.m_minHeight = pe.Value.ToFloat();
else if (pe.Name == at_maxheight)
prop.m_maxHeight = pe.Value.ToFloat();
else if (pe.Name == at_selectable)
prop.m_selectable = pe.Value != "false";
}
currentVariant.m_Props.push_back(prop);
}
}
}
}
bool CObjectBase::Load(const VfsPath& pathname)
{
m_UsedFiles.clear();
@ -53,36 +218,8 @@ bool CObjectBase::Load(const VfsPath& pathname)
EL(actor);
EL(castshadow);
EL(float);
EL(material);
EL(group);
EL(variant);
EL(animations);
EL(animation);
EL(props);
EL(prop);
EL(mesh);
EL(texture);
EL(textures);
EL(color);
EL(decal);
EL(particles);
AT(file);
AT(name);
AT(speed);
AT(event);
AT(load);
AT(sound);
AT(attachpoint);
AT(actor);
AT(frequency);
AT(width);
AT(depth);
AT(angle);
AT(offsetx);
AT(offsetz);
AT(minheight);
AT(maxheight);
AT(selectable);
EL(material);
#undef AT
#undef EL
@ -94,7 +231,6 @@ bool CObjectBase::Load(const VfsPath& pathname)
return false;
}
m_VariantGroups.clear();
m_Pathname = pathname;
@ -132,155 +268,21 @@ bool CObjectBase::Load(const VfsPath& pathname)
std::vector<Variant>::iterator currentVariant = currentGroup->begin();
XERO_ITER_EL(child, variant)
{
ENSURE(variant.GetNodeName() == el_variant);
XERO_ITER_ATTR(variant, attr)
{
if (attr.Name == at_name)
currentVariant->m_VariantName = attr.Value.LowerCase();
else if (attr.Name == at_frequency)
currentVariant->m_Frequency = attr.Value.ToInt();
}
XERO_ITER_EL(variant, option)
{
int option_name = option.GetNodeName();
if (option_name == el_mesh)
{
currentVariant->m_ModelFilename = VfsPath("art/meshes") / option.GetText().FromUTF8();
}
else if (option_name == el_textures)
{
XERO_ITER_EL(option, textures_element)
{
ENSURE(textures_element.GetNodeName() == el_texture);
Samp samp;
XERO_ITER_ATTR(textures_element, se)
{
if (se.Name == at_file)
samp.m_SamplerFile = VfsPath("art/textures/skins") / se.Value.FromUTF8();
else if (se.Name == at_name)
samp.m_SamplerName = CStrIntern(se.Value);
}
currentVariant->m_Samplers.push_back(samp);
}
}
else if (option_name == el_decal)
{
XMBAttributeList attrs = option.GetAttributes();
Decal decal;
decal.m_SizeX = attrs.GetNamedItem(at_width).ToFloat();
decal.m_SizeZ = attrs.GetNamedItem(at_depth).ToFloat();
decal.m_Angle = DEGTORAD(attrs.GetNamedItem(at_angle).ToFloat());
decal.m_OffsetX = attrs.GetNamedItem(at_offsetx).ToFloat();
decal.m_OffsetZ = attrs.GetNamedItem(at_offsetz).ToFloat();
currentVariant->m_Decal = decal;
}
else if (option_name == el_particles)
{
XMBAttributeList attrs = option.GetAttributes();
VfsPath file = VfsPath("art/particles") / attrs.GetNamedItem(at_file).FromUTF8();
currentVariant->m_Particles = file;
// For particle hotloading, it's easiest to reload the entire actor,
// so remember the relevant particle file as a dependency for this actor
m_UsedFiles.insert(file);
}
else if (option_name == el_color)
{
currentVariant->m_Color = option.GetText();
}
else if (option_name == el_animations)
{
XERO_ITER_EL(option, anim_element)
{
ENSURE(anim_element.GetNodeName() == el_animation);
Anim anim;
XERO_ITER_ATTR(anim_element, ae)
{
if (ae.Name == at_name)
{
anim.m_AnimName = ae.Value;
}
else if (ae.Name == at_file)
{
anim.m_FileName = VfsPath("art/animation") / ae.Value.FromUTF8();
}
else if (ae.Name == at_speed)
{
anim.m_Speed = ae.Value.ToInt() / 100.f;
if (anim.m_Speed <= 0.0) anim.m_Speed = 1.0f;
}
else if (ae.Name == at_event)
{
float pos = ae.Value.ToFloat();
anim.m_ActionPos = clamp(pos, 0.f, 1.f);
}
else if (ae.Name == at_load)
{
float pos = ae.Value.ToFloat();
anim.m_ActionPos2 = clamp(pos, 0.f, 1.f);
}
else if (ae.Name == at_sound)
{
float pos = ae.Value.ToFloat();
anim.m_SoundPos = clamp(pos, 0.f, 1.f);
}
}
currentVariant->m_Anims.push_back(anim);
}
}
else if (option_name == el_props)
{
XERO_ITER_EL(option, prop_element)
{
ENSURE(prop_element.GetNodeName() == el_prop);
Prop prop;
XERO_ITER_ATTR(prop_element, pe)
{
if (pe.Name == at_attachpoint)
prop.m_PropPointName = pe.Value;
else if (pe.Name == at_actor)
prop.m_ModelName = pe.Value.FromUTF8();
else if (pe.Name == at_minheight)
prop.m_minHeight = pe.Value.ToFloat();
else if (pe.Name == at_maxheight)
prop.m_maxHeight = pe.Value.ToFloat();
else if (pe.Name == at_selectable)
prop.m_selectable = pe.Value != "false";
}
currentVariant->m_Props.push_back(prop);
}
}
}
LoadVariant(XeroFile, variant, *currentVariant);
++currentVariant;
}
if (currentGroup->size() == 0)
{
LOGERROR("Actor group has zero variants ('%s')", pathname.string8());
}
++currentGroup;
}
else if (child_name == el_castshadow)
{
m_Properties.m_CastShadows = true;
}
else if (child_name == el_float)
{
m_Properties.m_FloatOnWater = true;
}
else if (child_name == el_material)
{
m_Material = VfsPath("art/materials") / child.GetText().FromUTF8();
}
}
if (m_Material.empty())

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2016 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,7 @@ class CObjectManager;
#include "lib/file/vfs/vfs_path.h"
#include "ps/CStr.h"
#include "ps/CStrIntern.h"
#include "ps/XML/Xeromyces.h"
#include <boost/random/mersenne_twister.hpp>
@ -187,6 +188,8 @@ private:
CObjectManager& m_ObjectManager;
boost::unordered_set<VfsPath> m_UsedFiles;
void LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant);
};
#endif

View File

@ -46,13 +46,14 @@ ActorEditorListCtrl::ActorEditorListCtrl(wxWindow* parent)
#undef COLOR
AddColumnType(_("Variant"), 90, "@name", new FieldEditCtrl_Text());
AddColumnType(_("Ratio"), 50, "@frequency", new FieldEditCtrl_Text());
AddColumnType(_("Model"), 140, "mesh", new FieldEditCtrl_File(_T("art/meshes/"), _("Mesh files (*.pmd, *.dae)|*.pmd;*.dae|All files (*.*)|*.*")));
AddColumnType(_("Textures"), 250, "textures", new FieldEditCtrl_Dialog(&TexListEditor::Create));
AddColumnType(_("Animations"), 250, "animations", new FieldEditCtrl_Dialog(&AnimListEditor::Create));
AddColumnType(_("Props"), 220, "props", new FieldEditCtrl_Dialog(&PropListEditor::Create));
AddColumnType(_("Color"), 80, "color", new FieldEditCtrl_Color());
AddColumnType(_("Variant"), 90, "@name", new FieldEditCtrl_Text());
AddColumnType(_("Base File"), 90, "@file", new FieldEditCtrl_File(_T("art/variants/"), _("Variants (*.xml)|*.xml|All files (*.*)|*.*")));
AddColumnType(_("Ratio"), 50, "@frequency", new FieldEditCtrl_Text());
AddColumnType(_("Model"), 140, "mesh", new FieldEditCtrl_File(_T("art/meshes/"), _("Mesh files (*.pmd, *.dae)|*.pmd;*.dae|All files (*.*)|*.*")));
AddColumnType(_("Textures"), 250, "textures", new FieldEditCtrl_Dialog(&TexListEditor::Create));
AddColumnType(_("Animations"), 250, "animations", new FieldEditCtrl_Dialog(&AnimListEditor::Create));
AddColumnType(_("Props"), 220, "props", new FieldEditCtrl_Dialog(&PropListEditor::Create));
AddColumnType(_("Color"), 80, "color", new FieldEditCtrl_Color());
}
void ActorEditorListCtrl::DoImport(AtObj& in)

View File

@ -77,12 +77,18 @@ sub validate
sub validate_actors
{
my @files = find_files('art/actors', 'xml');
validate('actor', \@files, 'art/actors/actor.rng');
}
sub validate_guis
{
# there are two different gui XML schemas depending on path
validate('actor', \@files, 'art/actors/actor.rng');
}
sub validate_variants
{
my @files = find_files('art/variants', 'xml');
validate('variant', \@files, 'art/variants/variant.rng');
}
sub validate_guis
{
# there are two different gui XML schemas depending on path
my @files = find_files('gui', 'xml');
my (@guipages, @guixmls);
for my $f (@files)
@ -164,12 +170,13 @@ sub validate_textures
push @files, $f if $f =~ /textures.xml/;
}
validate('texture', \@files, 'art/textures/texture.rng');
}
validate_actors();
validate_guis();
validate_maps();
validate_materials();
}
validate_actors();
validate_variants();
validate_guis();
validate_maps();
validate_materials();
validate_particles();
validate_simulation();
validate_soundgroups();