use strict; use warnings; use File::Find; use XML::Simple; use Data::Dumper; my $dir = '../../../binaries/data/mods/public/entities'; my $dir2 = '../../../binaries/data/mods/public/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; my %xml = ('template_entity_full.xml' => 1, 'template_entity_quasi.xml' => 1); $xml{$_} = 1 for @xml; my (%dot_actor, %dot_inherit); for my $xml (@xml) { print "$xml\n"; my $name = $xml; $name =~ s/\.xml$//; my %opt = (KeyAttr => []); my $data = XMLin("$dir/$xml", %opt, ForceArray => 1); my $c = convert($name, $data); my $out = $c; # print "$out\n\n"; open my $fo, "> $dir2/$xml" or die $!; 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)/; warn "Unknown parent $p\n" unless $xml{"$p.xml"}; $out .= qq{ parent="$p"}; $dot_inherit{$name}{$p} = 1; } $out .= qq{>\n}; my $civ; $civ = $1 if $name =~ /^units\/([a-z]{4})_/; my $needs_explicit_civ = ($civ and $data->{Parent} !~ /^${civ}_/); if ($data->{Traits}[0]{Id} or $needs_explicit_civ) { $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'], ); 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]}; } for my $k (keys %{$data->{Traits}[0]{Id}[0]}) { next if $k =~ /^(Civ)$/; # we do civ based on the filename instead, since it's more reliable next if $k =~ /^(Tooltip|History|Internal_Only|Classes|Rollover|Civ_Code)$/; # TODO: convert these somehow warn "Unrecognised field <$k>" unless grep $_->[0] eq $k, @map; } $out .= qq{$i\n}; } if ($name eq 'template_unit') { $out .= qq{$i\n}; $out .= qq{$i\n}; $out .= qq{$i$i1\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 $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 $data->{Traits}[0]{Population}[0]{Add}; $out .= qq{$i$i$data->{Traits}[0]{Creation}[0]{Time}[0]\n} if $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 $data->{Traits}[0]{Creation}[0]{Resource}[0]{$_}[0]; } $out .= qq{$i$i\n}; } $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}; $out .= qq{$i$i$data->{Traits}[0]{Supply}[0]{Type}[0]\n}; $out .= qq{$i$i$data->{Traits}[0]{Supply}[0]{SubType}[0]\n} if $data->{Traits}[0]{Supply}[0]{SubType}; $out .= qq{$i\n}; } if ($data->{Actions}[0]{Gather}) { $out .= qq{$i\n}; $out .= qq{$i$i$data->{Actions}[0]{Gather}[0]{Speed}[0]\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}) { $out .= qq{$i\n}; $out .= qq{$i$icorpse\n} if $name eq 'template_unit'; $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\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 $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}; $out .= qq{$i\n}; } die if $data->{Actions}[0]{Attack}[0]{Melee} and $data->{Actions}[0]{Attack}[0]{Ranged}; # only allow one at once my $attack = $data->{Actions}[0]{Attack}[0]{Melee} || $data->{Actions}[0]{Attack}[0]{Ranged}; if ($attack) { $out .= qq{$i\n}; for my $n (qw(Hack Pierce Crush Range MinRange ProjectileSpeed)) { $out .= qq{$i$i<$n>$attack->[0]{$n}[0]\n} if $attack->[0]{$n}; } if ($attack->[0]{Speed}) { my $s = $attack->[0]{Speed}[0]; # TODO: are these values sane? if ($s eq '1000') { $out .= qq{$i$i600\n}; $out .= qq{$i$i1000\n}; } elsif ($s eq '1500' or $s eq '1520' or $s eq '1510') { $out .= qq{$i$i900\n}; $out .= qq{$i$i1500\n}; } elsif ($s eq '2000') { $out .= qq{$i$i1200\n}; $out .= qq{$i$i2000\n}; } else { die $s; } } $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]{Footprint}) { $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)$/) { $out .= qq{$i\n}; } if ($name =~ /^template_structure_resource_field$/) { $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}; 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->{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";