use strict; use warnings; use File::Find; use XML::Simple; use XML::LibXML; use Data::Dumper; my $vfsroot = '../../../binaries/data/mods'; my (%dot_actor, %dot_inherit); my %xml = ('template_entity_full.xml' => 1, 'template_entity_quasi.xml' => 1); my $tro = `trang oldformat.rnc oldformat.rng 2>&1`; die $tro if $tro; my $rngschema = XML::LibXML::RelaxNG->new(location => 'oldformat.rng'); convert_all("$vfsroot/public"); convert_all("$vfsroot/internal") if -e "$vfsroot/internal"; sub convert_all { my ($root) = @_; my $dir = "$root/entities"; my $dir2 = "$root/simulation/templates"; my @xml; find({ wanted => sub { push @xml, $_ if /\.xml$/ and !/template_entity(|_full|_quasi)\.xml$/; }, no_chdir => 1 }, $dir); s~\Q$dir/~~ for @xml; $xml{$_} = 1 for @xml; for my $xml (@xml) { print " $xml\n"; my $name = $xml; $name =~ s/\.xml$//; next if $name =~ /^(template_foundation|foundation_)/; next if $name =~ /^(template_corpse)$/; my $doc = XML::LibXML->new->parse_file("$dir/$xml"); eval { $rngschema->validate($doc); }; if ($@) { warn $@; open my $f, "$dir/$xml"; print <$f>; } my %opt = (KeyAttr => []); my $data = XMLin("$dir/$xml", %opt, ForceArray => 1); my $c = convert($name, $data); my $out = $c; open my $fo, "> $dir2/$xml" or die $!; binmode $fo, ':utf8'; print $fo $out; } } sub convert { my ($name, $data) = @_; #print Dumper $data if $name eq 'template_unit_infantry'; my $out = qq{\n}; my $i = " "; $out .= qq{{Parent}) { my $p = $data->{Parent}; $p = "units/$p" if $p =~ /^(celt|cart|hele|iber|pers|rome)_(cavalry|infantry|support)/; $p = "structures/$p" if $p =~ /^(celt|cart|hele|iber|pers|rome)_scout_tower/; warn "Unknown parent $p\n" unless $xml{"$p.xml"}; $out .= qq{ parent="$p"}; $dot_inherit{$name}{$p} = 1; } $out .= qq{>\n}; my $civ; $civ = 'hele' if $name =~ /^other\/camp_(mace_hypaspist|sparta_phalangite)$/; $civ = 'gaia' if $name =~ /^(template_gaia|template_structure_gaia_settlement|fence_(long|short))$/ or ($name =~ /^gaia\/fauna_/ and $data->{Parent} !~ /^template_gaia/); $civ = $1 if $name =~ /^(?:units|structures)\/([a-z]{4})_/; $civ = $1 if $name =~ /^other\/camp_(pers|rome)_/; my $needs_explicit_civ = ($civ and $data->{Parent} !~ /^${civ}_/); if ($data->{Traits}[0]{Id} or $needs_explicit_civ) { if (not $civ and $name !~ /^template/ and $data->{Parent} =~ /^template/ and $data->{Traits}[0]{Id}[0]{Civ}) { $civ = ({ Hellenes => 'hele', Objects => 'gaia', Romans => 'rome'})->{ $data->{Traits}[0]{Id}[0]{Civ}[0] } or die; $needs_explicit_civ = 1; } $out .= qq{$i\n}; $out .= qq{$i$i$civ\n} if $needs_explicit_civ; my @map = ( [Generic => 'GenericName'], [Specific => 'SpecificName'], [Icon => 'IconSheet'], [Icon_Cell => 'IconCell'], [Rollover => 'Rollover'], [Tooltip => 'Tooltip'], [History => 'History'], ); for my $m (@map) { $out .= qq{$i$i<$m->[1]>$data->{Traits}[0]{Id}[0]{$m->[0]}[0][1]>\n} if $data->{Traits}[0]{Id}[0]{$m->[0]}; } my @classes = split /,\s*/, ($data->{Traits}[0]{Id}[0]{Classes}[0] || ''); @classes = grep /^(Foot|Mounted|Mechanical|Organic|Civic|Economic|City|Village|Town|Bow|Javelin|Spear|Sword|Hero)$/, @classes; # most of the others are useless so don't bother with them if (@classes) { $out .= qq{$i$i@classes\n}; } $out .= qq{$i\n}; } if ($data->{Traits}[0]{Promotion}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Traits}[0]{Promotion}[0]{Entity}[0]\n} if $data->{Traits}[0]{Promotion}[0]{Entity}; $out .= qq{$i$i$data->{Traits}[0]{Promotion}[0]{Req}[0]\n} if $data->{Traits}[0]{Promotion}[0]{Req}; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Anchor}) { $out .= qq{$i\n}; $out .= qq{$i$itrue\n} if $data->{Traits}[0]{Anchor}[0]{Type} and $data->{Traits}[0]{Anchor}[0]{Type}[0] eq 'Water'; $out .= qq{$i$ipitch-roll\n} if $data->{Traits}[0]{Anchor}[0]{ConformX} and $name ne 'template_unit_cavalry'; $out .= qq{$i$ipitch\n} if $name eq 'template_unit_cavalry'; $out .= qq{$i\n}; } if ($name eq 'template_structure_gaia_settlement') { $out .= qq{$i\n}; } if ($data->{Traits}[0]{Creation}[0]{BuildingLimitCategory} or $data->{Traits}[0]{Creation}[0]{TerritoryRestriction}) { $out .= qq{$i\n}; if ($name eq 'template_structure') { $out .= qq{$i$istandard\n}; } if ($data->{Traits}[0]{Creation}[0]{Socket}) { $out .= qq{$i$isettlement\n}; } if ($data->{Traits}[0]{Creation}[0]{TerritoryRestriction}) { $out .= qq{$i$i\L$data->{Traits}[0]{Creation}[0]{TerritoryRestriction}[0]\E\n}; } if ($data->{Traits}[0]{Creation}[0]{BuildingLimitCategory}) { $out .= qq{$i$i$data->{Traits}[0]{Creation}[0]{BuildingLimitCategory}[0]\n}; } $out .= qq{$i\n}; } if ($data->{Traits}[0]{MiniMap}) { $out .= qq{$i\n}; $out .= qq{$i$i}.lc($data->{Traits}[0]{MiniMap}[0]{Type}[0]).qq{\n} if $data->{Traits}[0]{MiniMap}[0]{Type}; $out .= qq{$i$i\n} #" if $data->{Traits}[0]{MiniMap}[0]{Red}; $out .= qq{$i\n}; } if ($name eq 'template_structure') { $out .= qq{$i\n}; $out .= qq{$i$istructure\n}; $out .= qq{$i\n}; } if ($name eq 'template_unit') { $out .= qq{$i\n}; $out .= qq{$i$ipassive\n}; $out .= qq{$i\n}; $out .= qq{$i\n}; $out .= qq{$i$i1\n}; $out .= qq{$i$i\n}; $out .= qq{$i$i$i0\n}; $out .= qq{$i$i$i0\n}; $out .= qq{$i$i$i0\n}; $out .= qq{$i$i$i0\n}; $out .= qq{$i$i\n}; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Ai}) { $out .= qq{$i\n}; $out .= qq{$i$i\L$data->{Traits}[0]{Ai}[0]{Behaviour}[0]\E\n}; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Population} or $data->{Traits}[0]{Creation}[0]{Resource} or $data->{Traits}[0]{Creation}[0]{Time}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Traits}[0]{Population}[0]{Rem}[0]\n} if defined $data->{Traits}[0]{Population}[0]{Rem} and $data->{Traits}[0]{Population}[0]{Rem}[0] != 1; $out .= qq{$i$i$data->{Traits}[0]{Population}[0]{Add}[0]\n} if defined $data->{Traits}[0]{Population}[0]{Add}; $out .= qq{$i$i$data->{Traits}[0]{Creation}[0]{Time}[0]\n} if defined $data->{Traits}[0]{Creation}[0]{Time}; if ($data->{Traits}[0]{Creation}[0]{Resource}) { $out .= qq{$i$i\n}; for (qw(Food Wood Stone Metal)) { $out .= qq{$i$i$i<\l$_>$data->{Traits}[0]{Creation}[0]{Resource}[0]{$_}[0]\n} if defined $data->{Traits}[0]{Creation}[0]{Resource}[0]{$_}[0]; } $out .= qq{$i$i\n}; } $out .= qq{$i\n}; } if ($data->{Traits}[0]{Loot}) { $out .= qq{$i\n}; for (qw(Xp Food Wood Stone Metal)) { $out .= qq{$i$i<\L$_\E>}.int($data->{Traits}[0]{Loot}[0]{$_}[0]).qq{\n} if $data->{Traits}[0]{Loot}[0]{$_}; } $out .= qq{$i\n}; } if ($data->{Actions}[0]{Loot}) { $out .= qq{$i\n}; } if ($data->{Traits}[0]{Supply} and $name =~ /template_gaia/) { $out .= qq{$i\n}; } if ($data->{Traits}[0]{Supply}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Traits}[0]{Supply}[0]{Max}[0]\n}; my $t = $data->{Traits}[0]{Supply}[0]{Type}[0]; $t .= '.'.$data->{Traits}[0]{Supply}[0]{SubType}[0] if $data->{Traits}[0]{Supply}[0]{SubType}; $out .= qq{$i$i$t\n}; $out .= qq{$i\n}; } if ($data->{Actions}[0]{Gather}) { $out .= qq{$i\n}; my $speed = sprintf '%.1f', $data->{Actions}[0]{Gather}[0]{Speed}[0]/1000; $out .= qq{$i$i$speed\n}; if ($data->{Actions}[0]{Gather}[0]{Resource}) { $out .= qq{$i$i\n}; my $r = $data->{Actions}[0]{Gather}[0]{Resource}[0]; for my $t (sort keys %$r) { if (ref $r->{$t}[0]) { for my $s (sort keys %{$r->{$t}[0]}) { $out .= qq{$i$i$i<\L$t.$s>$r->{$t}[0]{$s}[0]\n}; } } else { $out .= qq{$i$i$i<\L$t>$r->{$t}[0]\n}; } } $out .= qq{$i$i\n}; } $out .= qq{$i\n}; } if ($data->{Traits}[0]{Health} or $name eq 'template_unit_mechanical') { $out .= qq{$i\n}; $out .= qq{$i$icorpse\n} if $name eq 'template_unit'; $out .= qq{$i$ivanish\n} if $name =~ /^(template_structure|other\/fence_(long|short))$/; $out .= qq{$i$i$data->{Traits}[0]{Health}[0]{Max}[0]\n} if $data->{Traits}[0]{Health}[0]{Max}; $out .= qq{$i$i$data->{Traits}[0]{Health}[0]{RegenRate}[0]\n} if $data->{Traits}[0]{Health}[0]{RegenRate}; $out .= qq{$i$itrue\n} if $data->{Traits}[0]{Health}[0]{Healable}; $out .= qq{$i$itrue\n} if $data->{Traits}[0]{Health}[0]{Repairable}; $out .= qq{$i$ifalse\n$i$itrue\n} if $name eq 'template_unit_mechanical'; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Stamina} and $name !~ /^(template_entity|template_unit|template_unit_mechanical_ship)$/) { $out .= qq{$i\n}; $out .= qq{$i$i}.int($data->{Traits}[0]{Stamina}[0]{Max}[0]).qq{\n}; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Armour}) { $out .= qq{$i\n}; for my $n (qw(Hack Pierce Crush)) { $out .= qq{$i$i<$n>$data->{Traits}[0]{Armour}[0]{$n}[0]\n} if defined $data->{Traits}[0]{Armour}[0]{$n}; } $out .= qq{$i\n}; } if ($data->{Actions}[0]{Move}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Actions}[0]{Move}[0]{Speed}[0]\n} if $data->{Actions}[0]{Move}[0]{Speed}; if ($data->{Actions}[0]{Move}[0]{Run}) { $out .= qq{$i$i\n}; $out .= qq{$i$i$i$data->{Actions}[0]{Move}[0]{Run}[0]{Speed}[0]\n} if $data->{Actions}[0]{Move}[0]{Run}[0]{Speed}; $out .= qq{$i$i$i$data->{Actions}[0]{Move}[0]{Run}[0]{Range}[0]\n} if $data->{Actions}[0]{Move}[0]{Run}[0]{Range}; $out .= qq{$i$i$i$data->{Actions}[0]{Move}[0]{Run}[0]{RangeMin}[0]\n} if $data->{Actions}[0]{Move}[0]{Run}[0]{RangeMin}; $out .= qq{$i$i$i$data->{Actions}[0]{Move}[0]{Run}[0]{RegenRate}[0]\n} if $data->{Actions}[0]{Move}[0]{Run}[0]{RegenRate}; $out .= qq{$i$i$i$data->{Actions}[0]{Move}[0]{Run}[0]{DecayRate}[0]\n} if $data->{Actions}[0]{Move}[0]{Run}[0]{DecayRate}; $out .= qq{$i$i\n}; } $out .= qq{$i\n}; } if ($data->{Actions}[0]{Attack}) { $out .= qq{$i\n}; if ($data->{Actions}[0]{Attack}[0]{Melee}) { $out .= qq{$i$i\n}; for my $n (qw(Hack Pierce Crush Range MinRange ProjectileSpeed)) { my $e = $n; $e = 'MaxRange' if $e eq 'Range'; $out .= qq{$i$i$i<$e>$data->{Actions}[0]{Attack}[0]{Melee}[0]{$n}[0]\n} if $data->{Actions}[0]{Attack}[0]{Melee}[0]{$n}; #$out .= qq{$i$i$i<$e>0.0\n} if $n eq 'MinRange' and # $name =~ /^(template_unit_(cavalry_melee|hero|infantry_melee|mechanical_siege_(ballista|onager|ram)|super_(cavalry|infantry))|units\/celt_support_female_citizen)$/; } if ($data->{Actions}[0]{Attack}[0]{Melee}[0]{Speed}) { my $s = $data->{Actions}[0]{Attack}[0]{Melee}[0]{Speed}[0]; if ($s eq '1000') { $out .= qq{$i$i$i1000\n}; } elsif ($s eq '1500') { $out .= qq{$i$i$i1500\n}; } else { die $s; } } $out .= qq{$i$i\n}; } if ($data->{Actions}[0]{Attack}[0]{Ranged}) { $out .= qq{$i$i\n}; for my $n (qw(Hack Pierce Crush Range MinRange ProjectileSpeed)) { my $e = $n; $e = 'MaxRange' if $e eq 'Range'; $out .= qq{$i$i$i<$e>$data->{Actions}[0]{Attack}[0]{Ranged}[0]{$n}[0]\n} if $data->{Actions}[0]{Attack}[0]{Ranged}[0]{$n}; $out .= qq{$i$i$i<$e>0.0\n} if $n eq 'MinRange' and $name =~ /^(template_unit_(mechanical_siege_(ballista|onager)))$/; } if ($data->{Actions}[0]{Attack}[0]{Ranged}[0]{Speed}) { my $s = $data->{Actions}[0]{Attack}[0]{Ranged}[0]{Speed}[0]; if ($s eq '1000') { $out .= qq{$i$i$i600\n}; $out .= qq{$i$i$i1000\n}; } elsif ($s eq '1500' or $s eq '1520' or $s eq '1510') { $out .= qq{$i$i$i900\n}; $out .= qq{$i$i$i1500\n}; } elsif ($s eq '2000') { $out .= qq{$i$i$i1200\n}; $out .= qq{$i$i$i2000\n}; } else { die $s; } } $out .= qq{$i$i\n}; } if ($data->{Actions}[0]{Attack}[0]{Charge}) { $out .= qq{$i$i\n}; for my $n (qw(Hack Pierce Crush Range MinRange ProjectileSpeed)) { my $e = $n; $e = 'MaxRange' if $e eq 'Range'; $out .= qq{$i$i$i<$e>$data->{Actions}[0]{Attack}[0]{Charge}[0]{$n}[0]\n} if $data->{Actions}[0]{Attack}[0]{Charge}[0]{$n}; $out .= qq{$i$i$i<$e>0.0\n} if $n eq 'MinRange' and $name =~ /^(template_unit_(cavalry_melee|hero|infantry_melee|super_cavalry|super_infantry|mechanical_ship|mechanical_siege_ram))$/; } $out .= qq{$i$i\n}; } $out .= qq{$i\n}; } $dot_actor{$name} = $data->{Actor}; if ($data->{Actor} or $data->{Traits}[0]{Creation}[0]{Foundation}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Actor}[0]\n} if $data->{Actor}; if ($data->{Traits}[0]{Creation}[0]{Foundation}) { $data->{Traits}[0]{Creation}[0]{Foundation}[0] =~ /^foundation_(\d+x\d+|theatron|field)$/ or die $data->{Traits}[0]{Creation}[0]{Foundation}[0]; my $actor = ($1 eq 'field' ? 'structures/plot_field_found.xml' : "structures/fndn_$1.xml"); $out .= qq{$i$i$actor\n}; } $out .= qq{$i\n}; } if ($data->{Traits}[0]{Display}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Traits}[0]{Display}[0]{Bars}[0]{Offset}[0]\n}; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Footprint}) { my $r = ''; $r = ' replace=""' if $data->{Parent} and (($data->{Traits}[0]{Footprint}[0]{Width} and $data->{Parent} =~ /^template_(unit|unit_mechanical|unit_mechanical_siege|unit_mechanical_siege_onager|unit_(super|hero)_ranged)$/) or ($data->{Traits}[0]{Footprint}[0]{Radius} and $data->{Parent} =~ /^template_structure_(special|military_fortress)$/)); $out .= qq{$i\n}; if ($data->{Traits}[0]{Footprint}[0]{Radius}) { $out .= qq{$i$i\n}; } if ($data->{Traits}[0]{Footprint}[0]{Width}) { $out .= qq{$i$i\n}; #" } if ($data->{Traits}[0]{Footprint}[0]{Height}) { $out .= qq{$i$i$data->{Traits}[0]{Footprint}[0]{Height}[0]\n}; } $out .= qq{$i\n}; if ($name =~ /^(template_(structure|gaia)_|structures\/)/ and $name !~ /^template_structure_resource_field$/) { my ($w, $d); if ($data->{Traits}[0]{Footprint}[0]{Radius}) { $w = $d = sprintf '%.1f', 2*$data->{Traits}[0]{Footprint}[0]{Radius}[0]; } if ($data->{Traits}[0]{Footprint}[0]{Width}) { $w = $data->{Traits}[0]{Footprint}[0]{Width}[0]; $d = $data->{Traits}[0]{Footprint}[0]{Depth}[0]; } $out .= qq{$i\n}; $out .= qq{$i$i\n}; $out .= qq{$i\n}; } } if ($name eq 'template_unit') { $out .= qq{$i\n}; $out .= qq{$i$i\n}; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Vision} and $name ne 'template_entity') { $out .= qq{$i\n}; $out .= qq{$i$i}.int($data->{Traits}[0]{Vision}[0]{Los}[0]).qq{\n} if $data->{Traits}[0]{Vision}[0]{Los}; $out .= qq{$i$itrue\n} if $data->{Traits}[0]{Vision}[0]{Permanent}; $out .= qq{$i$itrue\n} if $name =~ /^other\/fence_(long|short)$/; $out .= qq{$i$ifalse\n} if $name =~ /^(template_unit)$/; $out .= qq{$i\n}; } if ($data->{Actions}[0]{Create}[0]{List}[0]{StructCiv} or $data->{Actions}[0]{Create}[0]{List}[0]{StructMil}) { $out .= qq{$i\n}; $out .= qq{$i$i1.0\n} if $data->{Actions}[0]{Build}; # $out .= qq{$i$i\n}; $out .= qq{$i$i\n}; for (sort (keys %{$data->{Actions}[0]{Create}[0]{List}[0]{StructCiv}[0]}, keys %{$data->{Actions}[0]{Create}[0]{List}[0]{StructMil}[0]})) { my $n = "structures/" . ($civ || "{civ}") . "_" . (lc $_); $out .= qq{$i$i$i$n\n}; } $out .= qq{$i$i\n}; $out .= qq{$i\n}; } if ($data->{Actions}[0]{Create}[0]{List}[0]{Train}) { $out .= qq{$i\n}; # $out .= qq{$i$i\n}; $out .= qq{$i$i\n}; for (sort (keys %{$data->{Actions}[0]{Create}[0]{List}[0]{Train}[0]})) { my $n = "units/" . ($civ || "{civ}") . "_" . (lc $_); $out .= qq{$i$i$i$n\n}; } $out .= qq{$i$i\n}; $out .= qq{$i\n}; } if ($data->{Traits}[0]{Garrison}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Traits}[0]{Garrison}[0]{Max}[0]\n}; $out .= qq{$i\n}; } # Auras: # Trample -> ignore # Dropsite -> special # Infidelity -> ok # Allure -> ok # Heal -> ok # Fear -> ok # Courage -> ok if ($data->{Traits}[0]{Auras}[0]{Dropsite}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Traits}[0]{Auras}[0]{Dropsite}[0]{Radius}[0]\n}; my @r; for (qw(Food Wood Stone Metal)) { push @r, lc $_ if $data->{Traits}[0]{Auras}[0]{Dropsite}[0]{Types}[0]{$_}; } $out .= qq{$i$i@r\n}; $out .= qq{$i\n}; } elsif ($data->{Traits}[0]{Auras}) { my @as = keys %{$data->{Traits}[0]{Auras}[0]}; @as = grep $_ ne 'Trample', @as; if (@as) { $out .= qq{$i\n}; for my $a (@as) { $out .= qq{$i$i<$a>\n}; $out .= qq{$i$i$i$data->{Traits}[0]{Auras}[0]{$a}[0]{Radius}[0]\n}; $out .= qq{$i$i$i$data->{Traits}[0]{Auras}[0]{$a}[0]{Bonus}[0]\n} if $data->{Traits}[0]{Auras}[0]{$a}[0]{Bonus}; $out .= qq{$i$i$i\n} if $data->{Traits}[0]{Auras}[0]{$a}[0]{Time}; $out .= qq{$i$i$i$data->{Traits}[0]{Auras}[0]{$a}[0]{Speed}[0]\n} if $data->{Traits}[0]{Auras}[0]{$a}[0]{Speed}; $out .= qq{$i$i\n}; } $out .= qq{$i\n}; } } if ($data->{SoundGroups}) { $out .= qq{$i\n}; $out .= qq{$i$i\n}; for my $n (qw(Walk Run Melee Death Build Gather_Fruit Gather_Grain Gather_Wood Gather_Stone Gather_Metal)) { my $n2 = lc $n; if ($n2 eq 'melee') { $n2 = 'attack'; } if ($data->{SoundGroups}[0]{$n}) { my $f = $data->{SoundGroups}[0]{$n}[0]; $f =~ s~^audio/~~ or die; $out .= qq{$i$i$i<$n2>$f\n}; } } $out .= qq{$i$i\n}; $out .= qq{$i\n}; } $out .= qq{\n}; return $out; } open my $dot, '> entities.dot' or die $!; print $dot < "$_";\n}; #" } } for my $p (sort keys %dot_inherit) { for my $c (sort keys %{$dot_inherit{$p}}) { print $dot qq{"$p" -> "$c";\n}; } } print $dot "}\n";