#!/usr/local/bin/perl -w # -*- perl -*- ###################################################################### # fixupmodule.pl -- Do all fixup operations to module # Copyright (c) 2006 Tero Kivinen # All Rights Reserved. ###################################################################### # Program: fixupmodule.pl # $Source: /home/cereacvs/cvsroot/cerea2/perllib/fixupmodule.pl,v $ # Author : $Author: kivinen $ # # (C) Tero Kivinen 2006 # # Creation : 21:57 Nov 1 2006 kivinen # Last Modification : 17:31 touko 3 2007 smbkivinen # Last check in : $Date: 2007/05/03 21:41:16 $ # Revision number : $Revision: 1.28 $ # State : $State: Exp $ # Version : 1.1000 # Edit time : 639 min # # Description : Do all fixup operations to module # # $Log: fixupmodule.pl,v $ # Revision 1.28 2007/05/03 21:41:16 kivinen # Fixed deity list. # # Revision 1.27 2007/04/20 01:28:03 kivinen # Added setting Appearance / ScaleX / ScaleY / ScaleZ / correct # script / flags to chairs. Added checking of door targets. # Added Scale* to creatures. Initial non working version of # environmental -> placeable conversion. # # Revision 1.26 2007/03/15 00:03:49 kivinen # Fixed check for required variables. # # Revision 1.25 2007/02/05 02:03:28 kivinen # Added checking of worldmap transition waypoints, names, and # triggers. Renamed Opt::warnings_filter to match the option # name, so the config.pl variable name is same as option name. # # Revision 1.24 2007/01/14 03:05:11 kivinen # Fixed a regexp... # # Revision 1.23 2007/01/12 00:16:43 kivinen # Added printing of leve. # # Revision 1.22 2007/01/10 21:21:44 kivinen # Added -q option to list all quests at the end of run. # # Revision 1.21 2007/01/08 00:38:06 kivinen # Added setting of MinX, MinY, MaxX, MaxY to area. # # Revision 1.20 2007/01/06 02:45:37 kivinen # Added checking of the creature scripts. Added counting of # warnings. # # Revision 1.19 2007/01/04 21:57:51 kivinen # Fix the Mod_CustomTlk by removing the .tlk from it. # # Revision 1.18 2007/01/03 21:58:31 kivinen # Added creation of the level 0 quest item for non-public # quests. Added also NUM_SUCCESSES_xx, NUM_FAILS_xx, and # NUM_STEPS_xx integer variables on the quest item (xx is the # number of quest (i.e. matching QUEST_xx)). # # Revision 1.17 2007/01/01 21:06:10 kivinen # Add "guild_list" variable to the module having list of all # valid guild identities. # # Revision 1.16 2006/12/21 20:32:35 kivinen # Added warning filter. Added checking of conversations for # incorrect use of parameters for the scripts. # # Revision 1.15 2006/12/21 00:42:49 kivinen # Lots of new checks. Verifies guild names, guild # objects/creatures etc, shop objects/keepers/triggers/items, # checks all items etc. # # Revision 1.14 2006/12/19 21:27:05 kivinen # Added better extracting of the version from path. Added # stricter check for shop tags, so we can have shop keeper # waypoints. # # Revision 1.13 2006/11/25 22:47:56 kivinen # Added warnings about invalid or missing shop variables. # # Revision 1.12 2006/11/25 13:43:51 kivinen # New version, that will put area waypoints in, fix area tags if # not found, added OnEnter and OnExit scripts to areas, and add # newline variable to module. # # Revision 1.11 2006/11/21 09:48:06 kivinen # Added flushing output when getting warnings. # # Revision 1.10 2006/11/20 21:13:43 kivinen # Fixed duplicate door tag detection. # # Revision 1.9 2006/11/20 20:58:28 kivinen # Changed fixupmodule script to fix doors in areas. Also add # warnings if some area is missing any of the files required. # Added code to print warnings for placed creatures without # conversations. Added warnings for areas without tag or name. # Added warning about duplicate door tags, or nondefault # scripts. # # Revision 1.8 2006/11/20 16:28:49 kivinen # Added replacing \n with real newline in the journal texts. # # Revision 1.7 2006/11/16 22:17:20 kivinen # Added support of updating the module description and version # number in the module.ifo. Version number is either taken from # command line (--cereaversion or -c option), or if no options # given, then it is taken from the input module path by taking # the last component of the path name and removing all # non-numeric characters. # # Revision 1.6 2006/11/13 22:31:36 kivinen # Added also PUBLIC 2 quests to the adv guild helper list. # # Revision 1.5 2006/11/03 23:32:50 kivinen # Changed journal to have QUEST_ prefix on categories. # # Revision 1.4 2006/11/02 22:25:33 kivinen # Added support for generating ci_g_adv_guild_quest_list_ # item templates which contains QUEST_ variables listing # tags of the quests for level . The number of quests on # level is in QUESTS variable. # # Revision 1.3 2006/11/01 23:24:27 kivinen # Added Picture field. Changed TEXT_FAIL to be optional. # # Revision 1.2 2006/11/01 23:08:59 kivinen # Fixed couple of typos in the file type and structure name of # the journal. # # Revision 1.1 2006/11/01 22:48:23 kivinen # Created. # # $EndLog$ # # # ###################################################################### # initialization require 5.6.0; package FixupModule; use strict; use Gff; use GffRead; use GffWrite; use Erf; use ErfRead; use ErfWrite; use CereaUtils; use IO::Handle; use Digest::MD5 qw(md5_hex); $FixupModule::class_regexp = '^(Aberration|Agent|Animal|Arcanetrickster|Arcane Archer|Assassin|Barbarian|Bard|Beast|Blackguard|Cavalier|Cleric|Commoner|Construct|Contemplative|Divinechampion|Dragon|Dragondisciple|Druid|Duelist|Dwarvendefender|Eldritch Knight|Elemental|Fey|Fighter|Frenziedberserker|Giant|Harper|Humanoid|Magical Beast|Magus|Monk|Monstrous|Mystictheurge|Ooze|Outsider|Paladin|Palemaster|Ranger|Rogue|Sacredfist|Shadowdancer|Shadowthiefofamn|Shapechanger|Shifter|Sorcerer|Undead|Vermin|Warder|Warlock|Warpriest|Weapon Master|Wizard)$'; $FixupModule::domain_regexp = '^(Air|Animal|Death|Destruction|Earth|Evil|Fire|Good|Healing|Knowledge|Luck|Magic|Plant|Protection|Strength|Sun|Travel|Trickery|War|Water)$'; $FixupModule::deity_regexp = '^(Adamus|Air|Am|Annalina|Belwar|Boink|Borg|Bort|Chondra|Cidosoby|Crog|De\'Amon|Dragonlord|Dural|Earth|Fire|Greyre|GugluGug|Iorak|Iz|Jaysek|Keo|Kieron|Krao|Kuor|Lagnar|Life|Marcus|Neda|Nehaya|Neowen|None|Numa|Oribasus|Ra|Ragnarok|Reann|Robin|Saffron|Sagath|Shandril|Storm|Suttus|Tara|Teris|The Oracle|Ulane|Unknown|Unlife|Valris|Vindir|Water|Waukya|Yugal)$'; $FixupModule::one_prop_regexp = '([EUXGLPORC](' . '(D[FCWELUSPBMD](\d+D\d+|\d+D\d+[+-]\d+|[+-]?\d+))|' . '(V\d+!(I|P|\d+(\.\d+)|\.\d+))|' . '(L\d!\d+!(I|P|\d+(\.\d+)|\.\d+))|' . '(N[CMQUH]!\d+)|' . '(E)|' . '(F\d+)|' . '(S([SU]|C[1-5]|R[1-5])\d+)|' . '(U((EAT|DRINK);[-]?[0123]|(CONVO|SUMMON|CREATE[PIC]);[a-zA-Z0-9_]|' . 'QUEST;QUEST_[A-Z_0-9]+;\d+|HERB;\d+|HEAL|MONEYPOUCH|EXAMINER|' . 'PARFUME;\d|SPELL;\d+;\d+))|' . '(B([DEA][-]?\d+|[ST]\d+;[-]?\d+))|' . '(T[0-4][1-9][01]?)))'; # Not include yet: [AGWCXI] $FixupModule::prop_regexp = '[!#%$=@]' . $FixupModule::one_prop_regexp . '(,' . $FixupModule::one_prop_regexp . ')*'; ###################################################################### # Main function sub fixupmodule { my($top) = @_; my($erf, $i, $j, $k, $journal_id, $module_ifo_id, $dst); $FixupModule::warnings = 0; $FixupModule::shown_warnings = 0; $Opt::dump_quests = 0; CereaUtils::get_version(); CereaUtils::read_config($top . "/config.pl", ".fixupmodulerc"); CereaUtils::parse_options("output|o=s" => \$Opt::output_module, "cereaversion|c=s", \$Opt::cerea_version, "quests|q", \$Opt::dump_quests, "warningsskip|w=s", \$Opt::warningsskip); if (!defined($Opt::output_module) || $Opt::output_module eq "") { die "Mandatory output argument missing"; } if (!defined($Opt::cerea_version)) { $Opt::cerea_version = $Opt::module; $Opt::cerea_version =~ s/\///g; $Opt::cerea_version =~ s/.*\///g; $Opt::cerea_version =~ tr/0-9//cd; } # Read module $erf = CereaUtils::read_module($Opt::module); # Loop through all resources for($i = 0; $i < $erf->resource_count; $i++) { if ($erf->resource_extension($i) eq 'jrl') { $journal_id = $i; } if ($erf->resource_extension($i) eq 'ifo') { $module_ifo_id = $i; } if ($erf->resource_extension($i) eq 'uti') { $FixupModule::uti{lc($erf->resource_reference($i))} = 1; } if ($erf->resource_extension($i) eq 'are') { read_area_info($erf, $i, $erf->resource_reference($i)); } } # Read information from resources for($i = 0; $i < $erf->resource_count; $i++) { read_info($erf, $i, $erf->resource_reference($i), $erf->resource_extension($i)); } # Check area files foreach $i (keys %FixupModule::areas) { if ($FixupModule::areas{$i} != 31) { warning("AMF: Area $i is missing some files"); } } # Check guilds foreach $i (keys %FixupModule::guilds_seen) { if (!defined($FixupModule::guild{$i})) { warning("GOM: Guild $i object is missing, used in " . $FixupModule::guilds_seen{$i}); } } # Check quests foreach $i (keys %FixupModule::quest_used) { if (!defined($FixupModule::quests{$i})) { warning("QOM: Quest $i object is missing, used in " . $FixupModule::quest_used{$i}); } } # Check worldmap information foreach $i (keys %FixupModule::wm_trigger_area) { if (!defined($FixupModule::wm_wp{$i})) { warning("WMMW: Worldmap transition ($i) in area " . $FixupModule::wm_trigger_area{$i} . " has does not have any WP_$i waypoints"); } elsif ($#{$FixupModule::wm_wp{$i}} != 0) { warning("WMMW: Worldmap transition ($i) in area " . $FixupModule::wm_trigger_area{$i} . " has more than one WP_$i waypoints: " . join(", ", @{$FixupModule::wm_wp{$i}})); } for($j = 0; $j <= $#{$FixupModule::wm_trigger{$i}}; $j++) { $dst = $FixupModule::wm_trigger{$i}[$j]; if (!defined($FixupModule::wm_trigger_area{$dst})) { warning("WMMW: Worldmap transition ($i) in area " . $FixupModule::wm_trigger_area{$i} . " has transition to $dst, which does not exist"); } else { for($k = 0; $k <= $#{$FixupModule::wm_trigger{$dst}}; $k++) { if ($FixupModule::wm_trigger{$dst}[$k] eq $i) { last; } } if ($k > $#{$FixupModule::wm_trigger{$dst}}) { warning("WMMW: Worldmap transition ($i) in area " . $FixupModule::wm_trigger_area{$i} . " has transition to $dst, which does not " . "have transtion back: " . join(", ", @{$FixupModule::wm_trigger{$dst}})); } } } } # Check door targets foreach $i (keys %FixupModule::door_target) { next if (defined($FixupModule::door_tags{$i}) || defined($FixupModule::waypoint_tags{$i})); warning("DTM: Door target door / waypoint missing, tag = $i, door = " . $FixupModule::door_target{$i}); } # Check journal if (!defined($journal_id)) { die "Journal missing"; } # Check module ifo if (!defined($module_ifo_id)) { die "module.ifo missing"; } # Generate journal, adventure guild list and update ifo. $erf->resource_data($journal_id, generate_journal()); generate_adv_guild_lists($erf); update_module_ifo($erf, $module_ifo_id); # Write module $erf = CereaUtils::write_module($Opt::output_module, $erf); # Dump quests if ($Opt::dump_quests) { foreach $i (keys %FixupModule::quests) { printf("%d %-25s %-25s %s\n", $FixupModule::quests{$i}{LEVEL}, $i, $FixupModule::quests{$i}{Area}, $FixupModule::quests{$i}{Name}); } } printf("Total of %d warnings (%d warnings skipped because of filters)\n", $FixupModule::warnings, $FixupModule::warnings - $FixupModule::shown_warnings) if ($Opt::verbose > 0); } %FixupModule::area_resources = ( trx => 1, trn => 2, are => 4, git => 8, gic => 16 ); ###################################################################### # Reading area info phase sub read_area_info { my($erf, $index, $name) = @_; my($gff); $gff = GffRead::read(data => $erf->resource_data($index), return_errors => 1); if (!defined($gff)) { die "Cannot read $FixupModule::resource"; } $FixupModule::area{$name}{Width} = $$gff{Width}; $FixupModule::area{$name}{Height} = $$gff{Height}; $FixupModule::area{$name}{HasTerrain} = $$gff{HasTerrain}; } ###################################################################### # Reading phase sub read_info { my($erf, $index, $name, $type) = @_; print("Processing resource: $name\n") if ($Opt::verbose > 6); $FixupModule::resource = $name; if ($type eq 'git') { my($gff, $i, $minx, $miny, $maxx, $maxy); undef %FixupModule::shops; undef %FixupModule::shop_keep; undef %FixupModule::shop_locker; undef %FixupModule::shop_trigger; $FixupModule::modified = 0; $FixupModule::area_waypoint = 0; print("Found area: $name\n") if ($Opt::verbose > 4); $gff = GffRead::read(data => $erf->resource_data($index), return_errors => 1); if (!defined($gff)) { die "Cannot read $FixupModule::resource"; } $gff->find(find_label => '^/WaypointList\[\d+\]/$', proc => \&find_waypoint_variables); if (!$FixupModule::area_waypoint) { add_waypoint($gff, 'cw_area_waypoint', $name); $FixupModule::modified = 1; } # Fix door scripts. $gff->find(find_label => '^/Door List\[\d+\]/$', proc => \&fix_door); $gff->find(find_label => '^/Door List\[\d+\]/$', proc => \&rename_door); $gff->find(find_label => '^/Creature List\[\d+\]/$', proc => \&check_creatures); $gff->find(find_label => '^/Placeable List\[\d+\]/$', proc => \&check_placeables); $gff->find(find_label => '^/TriggerList\[\d+\]/$', proc => \&check_triggers); $gff->find(find_label => '^/WaypointList\[\d+\]/$', proc => \&check_waypoints); foreach $i (keys %FixupModule::shops) { if (!defined($FixupModule::shop_keep{$i})) { warning("SKM: Shop $i in area " . $FixupModule::resource . " does not have keeper"); } else { delete $FixupModule::shop_keep{$i}; } if (!defined($FixupModule::shop_locker{$i})) { warning("SLM: Shop $i in area " . $FixupModule::resource . " does not have any lockers"); } else { delete $FixupModule::shop_locker{$i}; } if (!defined($FixupModule::shop_trigger{$i})) { warning("STM: Shop $i in area " . $FixupModule::resource . " does not have trigger"); } else { delete $FixupModule::shop_trigger{$i}; } } foreach $i (keys %FixupModule::shop_keep) { warning("SWMK: Shop $i in area " . $FixupModule::resource . " has keeper, but does not have shop waypoint"); } foreach $i (keys %FixupModule::shop_locker) { warning("SWML: Shop $i in area " . $FixupModule::resource . " has locker, but does not have shop waypoint"); } foreach $i (keys %FixupModule::shop_trigger) { warning("SWMT: Shop $i in area " . $FixupModule::resource . " has trigger, but does not have shop waypoint"); } if ($FixupModule::area{$name}{HasTerrain}) { $minx = 80; $maxx = $FixupModule::area{$name}{Width} * 10 + 80; $miny = 80; $maxy = $FixupModule::area{$name}{Height} * 10 + 80; } else { $minx = 0; $maxx = $FixupModule::area{$name}{Width} * 10; $miny = 0; $maxy = $FixupModule::area{$name}{Height} * 10; } if (!defined($gff->variable("MinX")) || $gff->variable("MinX")->varvalue != $minx) { $gff->variable("MinX", $minx); $FixupModule::modified = 1; } if (!defined($gff->variable("MinY")) || $gff->variable("MinY")->varvalue != $miny) { $gff->variable("MinY", $miny); $FixupModule::modified = 1; } if (!defined($gff->variable("MaxX")) || $gff->variable("MaxX")->varvalue != $maxx) { $gff->variable("MaxX", $maxx); $FixupModule::modified = 1; } if (!defined($gff->variable("MaxY")) || $gff->variable("MaxY")->varvalue != $maxy) { $gff->variable("MaxY", $maxy); $FixupModule::modified = 1; } if ($FixupModule::modified) { $erf->resource_data($index, GffWrite::write($gff)); } } if ($type eq 'are') { my($gff); $FixupModule::modified = 0; $gff = GffRead::read(data => $erf->resource_data($index), return_errors => 1); if (!defined($gff)) { die "Cannot read $FixupModule::resource"; } if ($$gff{Tag} eq '') { warning("ANT: No tag for area $name"); $$gff{Tag} = $name; $FixupModule::modified = 1; } elsif ($$gff{Tag} ne $name) { warning("ATN: Area tag $$gff{Tag} does not match $name"); } if (!defined($$gff{Name}) || !defined($$gff{Name}{0}) || $$gff{Name}{0} eq '') { warning("AMN: No name for area $name"); } if (!defined($$gff{OnEnter}) || $$gff{OnEnter} eq '') { $$gff{OnEnter} = "ce_g_a_areaentry"; $FixupModule::modified = 1; } elsif ($$gff{OnEnter} ne "ce_g_a_areaentry") { warning("ANO: Non standard OnEnter script on area $name: " . $$gff{OnEnter}); } if (!defined($$gff{OnExit}) || $$gff{OnExit} eq '') { $$gff{OnExit} = "ce_g_a_areaexit"; $FixupModule::modified = 1; } elsif ($$gff{OnExit} ne "ce_g_a_areaexit") { warning("ANOX: Non standard OnExit script on area $name: " . $$gff{OnExit}); } if ($FixupModule::modified) { $erf->resource_data($index, GffWrite::write($gff)); } } if ($type eq 'uti') { my($gff); $gff = GffRead::read(data => $erf->resource_data($index), return_errors => 1); if (!defined($gff)) { die "Cannot read $FixupModule::resource"; } check_item($gff, $name . ".uti", $name); } if ($type eq 'dlg') { my($gff); $gff = GffRead::read(data => $erf->resource_data($index), return_errors => 1); if (!defined($gff)) { die "Cannot read $FixupModule::resource"; } $gff->find(find_label => '/ActiveConditional\[\d+\]/$', proc => \&check_dialog_script); $gff->find(find_label => '/ScriptList\[\d+\]/$', proc => \&check_dialog_script); } if (defined($FixupModule::area_resources{$type})) { $FixupModule::areas{$name} = 0 if (!defined($FixupModule::areas{$name})); $FixupModule::areas{$name} |= $FixupModule::area_resources{$type}; } } %FixupModule::door_scripts = ( 'OnClick' => 'cp_g_d_click', 'OnClosed' => 'cp_g_d_closed', 'OnDamaged' => 'cp_g_d_damaged', 'OnFailToOpen' => 'cp_g_d_failtoopen', 'OnLock' => 'cp_g_d_lock', 'OnOpen' => 'cp_g_d_open', 'OnUnlock' => 'cp_g_d_unlock' ); # gffmodify.pl -v -s '^/Door List\[\d+\]/$' -f 'OnClick:=:^(_gen_door_are|)$' -f 'OnClosed:=:^(_gen_door_clo|_gen_lockdoornow|_gen_lockdoor|lockdoornow|)$' -f 'OnDamaged:=:^(_gen_door_dam|_gen_doordamage|)$' -f 'OnFailToOpen:=:^(_gen_door_fai|_gen_brokendoor|)$' -f 'OnOpen:=:^(_gen_door_ope|_gen_closedoor|_gen_onopen|doorautolock001|sc_vindbardguild|sc_vindfghtsckool|_gen_lockdoor|doorcloser|door_close|)$' -f 'OnUnlock:=:^(_gen_door_unl|_gen_lockdoor|)$' -f 'OnDeath:=:^(x2_door_death|)$' -f 'OnDisarm:=:^(_gen_disarmed|)$' -f 'OnHeartbeat:=:^$' -f 'OnMeleeAttacked:=:^$' -f 'OnSpellCastAt:=:^(_gen_closedoor|)$' -f 'OnTrapTriggered:=:^$' -f 'OnUserDefined:=:^$' -p '/(Tag|OnClick|OnClosed|OnDamaged|OnFailToOpen|OnOpen|OnUnlock|OnDeath|OnDisarm|OnHeartbeat|OnMeleeAttacked|OnSpellCastAt|OnTrapTriggered|OnUserDefined)$' -m 'OnClick=_gen_door_are' -m 'OnClosed=_gen_door_clo' -m 'OnDamaged=_gen_door_dam' -m 'OnFailToOpen=_gen_door_fai' -m 'OnOpen=_gen_door_ope' -m 'OnUnlock=_gen_door_unl' -m 'OnDeath=' --variable 'app=@appereance@@substr(000@generictype@,-3,3)@' -P 'appereance=/Appearance' -P 'generictype=/GenericType' *.git # gffmodify.pl -v -s '^/Door List\[\d+\]/$' -f '/Locked$:=:^0$' -f '/Lockable$:=:^0$' -f '/KeyRequired$:=:^0$' -f '/KeyName$:=:^$' -m '/KeyRequired=1' -m 'KeyName=dm_quest_key' *.git ###################################################################### # Fix doors sub fix_door { my($gff, $full_label, $label, $value, $parent_gffs) = @_; my($i, $name, $vargff); if (defined($$gff{LocName}{0})) { $name = $$gff{LocName}{0} . " (" . $$gff{Tag} . ")" . " in area " . $FixupModule::resource; } else { $name = " (" . $$gff{Tag} . ")" . " in area " . $FixupModule::resource; } foreach $i (keys %FixupModule::door_scripts) { if (!defined($$gff{$i})) { warning("DMS: Door $name does not have script $i"); } elsif ($$gff{$i} ne '' && $$gff{$i} ne $FixupModule::door_scripts{$i}) { warning("DNS: Door $name has nonstandard script $i: " . $$gff{$i}); } elsif ($$gff{$i} eq '') { $$gff{$i} = $FixupModule::door_scripts{$i}; $FixupModule::modified = 1; } } if ($$gff{OnDeath} eq 'x2_door_death') { $$gff{OnDeath} = ''; $FixupModule::modified = 1; } elsif ($$gff{OnDeath} ne '') { warning("DNSD: Door $name has nonstandard script OnDeath: " . $$gff{OnDeath}); } # Search for non lockable door, non locked door and add require key if ($$gff{Locked} == 0 && $$gff{Lockable} == 0 && $$gff{KeyRequired} == 0 && $$gff{KeyName} eq '') { $$gff{KeyRequired} = 1; $$gff{KeyName} = 'dm_quest_key'; $FixupModule::modified = 1; } # Set HP and plot if not already set. if ($$gff{HP} != 10000 || $$gff{CurrentHP} != 10000 || $$gff{Plot} != 0) { $$gff{HP} = 10000; $$gff{CurrentHP} = 10000; $$gff{Plot} = 0; $FixupModule::modified = 1; } $vargff = $gff->variable("Appearance"); if (!defined($vargff) || $vargff->varvalue ne $$gff{Appearance}) { $gff->variable("Appearance", $$gff{Appearance}); $FixupModule::modified = 1; } $vargff = $gff->variable("GenericType"); if (!defined($vargff) || $vargff->varvalue ne $$gff{GenericType}) { $gff->variable("GenericType", $$gff{GenericType}); $FixupModule::modified = 1; } if ($$gff{Tag} !~ /^(plc_d|cwall1)/i) { if (defined($FixupModule::door_tags{$$gff{Tag}})) { warning("DDT: Duplicate tag for door $name"); } else { $FixupModule::door_tags{$$gff{Tag}} = $name; } } if (defined($$gff{LinkedTo}) && $$gff{LinkedTo} ne '') { $FixupModule::door_target{$$gff{LinkedTo}} = $name; } } ###################################################################### # Rename doors sub rename_door { my($gff, $full_label, $label, $value, $parent_gffs) = @_; if ($$gff{Tag} =~ /^(plc_d|cwall1)/i) { my($tag, $i); for($i = 0; $i < 10000; $i++) { $tag = sprintf("%s%04d", $FixupModule::resource, $i); if (!defined($FixupModule::door_tags{$tag})) { $$gff{Tag} = $tag; $FixupModule::door_tags{$tag} = 1; $FixupModule::modified = 1; return; } } warning("AMU: Could not rename door, couldn't find " . "unique number on area " . $FixupModule::resource); } } %FixupModule::creature_scripts_ok = ( 'ScriptAttacked' => 'cn_g_onattacked', 'ScriptDamaged' => 'cn_g_ondamaged', 'ScriptDeath' => 'cn_g_onkilled', 'ScriptDialogue' => 'cn_g_onconversation', 'ScriptDisturbed' => 'cn_g_ondisturbed', 'ScriptEndRound' => 'cn_g_oncombatroundend', 'ScriptHeartbeat' => 'cn_g_onheartbeat', 'ScriptOnBlocked' => 'cn_g_onblocked', 'ScriptOnNotice' => 'cn_g_onperception', 'ScriptRested' => 'cn_g_onrested', 'ScriptSpawn' => 'cn_g_onspawn', 'ScriptSpellAt' => 'cn_g_onspellcastat', 'ScriptUserDefine' => 'cn_g_onuserdefined' ); %FixupModule::creature_scripts_x2 = ( 'ScriptAttacked' => 'x2_def_attacked', 'ScriptDamaged' => 'x2_def_ondamage', 'ScriptDeath' => 'x2_def_ondeath', 'ScriptDialogue' => 'x2_def_onconv', 'ScriptDisturbed' => 'x2_def_ondisturb', 'ScriptEndRound' => 'x2_def_endcombat', 'ScriptHeartbeat' => 'x2_def_heartbeat', 'ScriptOnBlocked' => 'x2_def_onblocked', 'ScriptOnNotice' => 'x2_def_percept', 'ScriptRested' => 'x2_def_rested', 'ScriptSpawn' => 'x2_def_spawn', 'ScriptSpellAt' => 'x2_def_spellcast', 'ScriptUserDefine' => 'x2_def_userdef', ); %FixupModule::creature_scripts_c2 = ( 'ScriptAttacked' => 'nw_c2_default5', 'ScriptDamaged' => 'nw_c2_default6', 'ScriptDeath' => 'nw_c2_default7', 'ScriptDialogue' => 'nw_c2_default4', 'ScriptDisturbed' => 'nw_c2_default8', 'ScriptEndRound' => 'nw_c2_default3', 'ScriptHeartbeat' => 'nw_c2_default1', 'ScriptOnBlocked' => 'nw_c2_defaulte', 'ScriptOnNotice' => 'nw_c2_default2', 'ScriptRested' => 'nw_c2_defaulta', 'ScriptSpawn' => 'nw_c2_default9', 'ScriptSpellAt' => 'nw_c2_defaultb', 'ScriptUserDefine' => 'nw_c2_defaultd' ); ###################################################################### # Check creatures sub check_creatures { my($gff, $full_label, $label, $value, $parent_gffs) = @_; my($name, $i, $ok, $x2, $c2, $vargff); $name = ''; $name .= $$gff{FirstName}{0} if (defined($$gff{FirstName}{0})); $name .= ' '; $name .= $$gff{LastName}{0} if (defined($$gff{LastName}{0})); if ($$gff{Conversation} eq '') { warning("CMC: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " has no conversation"); } if ($$gff{Deity} ne "") { if ($$gff{Deity} !~ /$FixupModule::deity_regexp/i) { warning("CID: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " has invalid Deity " . $$gff{Deity}); } } foreach $i ('ScriptAttacked', 'ScriptDamaged', 'ScriptDeath', 'ScriptDialogue', 'ScriptDisturbed', 'ScriptEndRound', 'ScriptHeartbeat', 'ScriptOnBlocked', 'ScriptOnNotice', 'ScriptRested', 'ScriptSpawn', 'ScriptSpellAt', 'ScriptUserDefine') { $ok = $FixupModule::creature_scripts_ok{$i}; $x2 = $FixupModule::creature_scripts_x2{$i}; $c2 = $FixupModule::creature_scripts_c2{$i}; if (!defined($$gff{$i}) || $$gff{$i} eq '') { warning("CMS: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " is missing script " . $i); } elsif ($$gff{$i} eq $x2) { warning("CISX: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " has invalid x2_def_* script " . $i . " = " . $$gff{$i}); } elsif ($$gff{$i} eq $c2) { warning("CISC: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " has invalid nw_c2_default* script " . $i . " = " . $$gff{$i}); } elsif ($$gff{$i} ne $ok) { warning("CIS: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " has invalid script " . $i . " = " . $$gff{$i}); } } if ($$gff{Deity} eq '') { warning("CED: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " has empty Deity (fixed)"); $$gff{Deity} = "None"; $FixupModule::modified = 1; } elsif ($$gff{Deity} !~ /$FixupModule::deity_regexp/i) { warning("CID: Creature $name ($$gff{Tag}) in area " . $FixupModule::resource . " has invalid Deity " . $$gff{Deity}); } if ($$gff{Tag} =~ /^(shop_[^_]*_[^_]*)_keep$/) { $FixupModule::shop_keep{$1} = 1; } $vargff = $gff->variable("ScaleX"); if (!defined($vargff) || $vargff->varvalue ne $gff->value("ModelScale/x") || $vargff->vartype() != 2) { $gff->variable("ScaleX", $gff->value("ModelScale/x"), 2); $FixupModule::modified = 1; } $vargff = $gff->variable("ScaleY"); if (!defined($vargff) || $vargff->varvalue ne $gff->value("ModelScale/y") || $vargff->vartype() != 2) { $gff->variable("ScaleY", $gff->value("ModelScale/y"), 2); $FixupModule::modified = 1; } $vargff = $gff->variable("ScaleZ"); if (!defined($vargff) || $vargff->varvalue ne $gff->value("ModelScale/z") || $vargff->vartype() != 2) { $gff->variable("ScaleZ", $gff->value("ModelScale/z"), 2); $FixupModule::modified = 1; } } ###################################################################### # Check item sub check_item { my($gff, $name, $resref) = @_; if ($$gff{Tag} eq '') { warning("IMT: Item $name does not have tag"); } elsif (defined($resref) && lc($$gff{Tag}) ne lc($resref)) { warning("IRT: Item $resref and the tag $$gff{Tag} do not match"); } if ((!defined($$gff{LocalizedName}{0}) || $$gff{LocalizedName}{0} eq '') && (!defined($$gff{'LocalizedName. ____string_ref'}) || $$gff{'LocalizedName. ____string_ref'} == 4294967295)) { warning("IMN: Item $name $$gff{Tag} does not have name"); } match_variable($gff, "Price", 1, '\d+', 1); match_variable($gff, "Base", 1, '\d+', 1); match_variable($gff, "Heat", 1, '-?\d+', 0); match_variable($gff, "Cold", 1, '-?\d+', 0); match_variable($gff, "Prop", 3, $FixupModule::prop_regexp, 0); } ###################################################################### # Check conditional or action script in the dialog sub check_dialog_script { my($gff, $full_label, $label, $value, $parent_gffs) = @_; my($script, $param, $type, $location); $script = $$gff{Script}; return if (!defined($script)); $type = $gff->value('Parameters[0]/ParameterType'); $param = $gff->value('Parameters[0]/Parameter'); $location = $full_label . " at " . $FixupModule::resource; if ($script eq 'cc_g_q_canstart' || $script eq 'cc_g_q_getfail' || $script eq 'cc_g_q_getstate' || $script eq 'cc_g_q_nocreatures' || $script eq 'cc_g_q_running' || $script eq 'cc_g_q_success' || $script eq 'ca_g_q_incstate' || $script eq 'ca_g_q_setfail' || $script eq 'ca_g_q_setstate' || $script eq 'ca_g_q_setsucces') { if ($type != 2) { warning("CQSP: Script $script has wrong parameter 1 " . "type $type in $location"); } if ($param !~ /^QUEST_/) { warning("CQSQ: Quest script $script does not have QUEST_ " . "as param $param in $location"); } else { if (defined($FixupModule::quest_used{$param})) { $FixupModule::quest_used{$param} .= ", " . $location; } else { $FixupModule::quest_used{$param} = $location; } } } elsif ($script eq 'cc_g_d_hasdeity' || $script eq 'ca_g_d_setdeity') { if ($type != 2) { warning("CDSP: Script $script has wrong parameter 1 " . "type $type in $location"); } if ($param ne "" && $param =~ $FixupModule::deity_regexp) { warning("CDSD: Deity script $script does not have proper deity_ " . "as param $param in $location"); } } elsif ($script eq 'cc_g_g_member' || $script eq 'cc_g_g_npcrecommendation' || $script eq 'cc_g_g_requirements' || $script eq 'ca_g_g_advance' || $script eq 'ca_g_g_npcrecommendation') { if ($type != 2) { warning("CGSP: Script $script has wrong parameter 1 " . "type $type in $location"); } if ($param =~ /^guild_/) { warning("CDSG: Guild script $script does have extra guild_ " . "as param $param in $location"); } else { if ($param ne "") { if (defined($FixupModule::guilds_seen{$param})) { $FixupModule::guilds_seen{$param} .= ", " . $location; } else { $FixupModule::guilds_seen{$param} = $location; } } } } } %FixupModule::chairs = ( 644 => 1, # plc_mc_bench01 citymbench1 645 => 1, # plc_mc_bench02 citymbench2 1140 => 1, # plc_mc_bench03 citymbench04 1141 => 1, # plc_mc_bench04 citymbench05 1259 => 1, # plc_mc_bench05 citymbench5 1260 => 1, # plc_mc_bench06 citymbench6 1261 => 1, # plc_mc_bench07 citymbench7 654 => 1, # plc_mc_chair01 citymchair1 655 => 1, # plc_mc_chair02 citymchair2 656 => 1, # plc_mc_chair03 citymchair3 1262 => 1, # plc_mc_chair04 citymchair4 1352 => 1, # plc_mc_chair05 citymchair5 1472 => 1, # plc_mc_chair06 citymchair06 663 => 1, # plc_mc_stool01 citymfootstool1 1380 => 1, # plc_mc_stool02 citymfootstool2 1381 => 1, # plc_mc_stool03 citymfootstool3 1382 => 1, # plc_mc_stool04 citymfootstool4 927 => 1, # plc_mr_stool ruralmstool 1157 => 1, # plc_mr_stool2 ruralmstool2 715 => 1, # plc_mc_throne citymthrone 1496 => 1, # plc_mc_throne2 citymthroneblackgarius 1495 => 1 # plc_mc_throne1 citymthronelordnasher ); ###################################################################### # Check placeables sub check_placeables { my($gff, $full_label, $label, $value, $parent_gffs) = @_; my($name, $vargff, $i); $name = ''; $name = $$gff{LocName}{0} if (defined($$gff{LocName}{0})); if ($$gff{Tag} =~ /^(shop_[^_]*_[^_]*)_([A-Z]|\d+)$/) { $FixupModule::shop_locker{$1} = 1; if ($$gff{OnOpen} ne "cp_g_shopopen") { if ($$gff{OnOpen} eq "") { warning("SPOS: Shop placeable $name ($$gff{Tag}) in area " . $FixupModule::resource . " does not have OnOpen script (fixed)"); $$gff{OnOpen} = "cp_g_shopopen"; $FixupModule::modified = 1; } else { warning("SPO: Shop placeable $name ($$gff{Tag}) in area " . $FixupModule::resource . " has wrong OnOpen script: " . $$gff{OnOpen}); } } if ($$gff{OnUsed} eq "cp_g_shopopen") { warning("SPUS: Shop placeable $name ($$gff{Tag}) in area " . $FixupModule::resource . " has cp_g_shopopen as OnUsed script (removed)"); $$gff{OnUsed} = ""; $FixupModule::modified = 1; } if (!$$gff{HasInventory}) { warning("SPMI: Shop placeable $name ($$gff{Tag}) in area " . $FixupModule::resource . " does not have inventory"); $$gff{HasInventory} = 1; $FixupModule::modified = 1; } if (!$$gff{Plot}) { warning("SPNP: Shop placeable $name ($$gff{Tag}) in area " . $FixupModule::resource . " are not plot (fixed)"); $$gff{Plot} = 1; $FixupModule::modified = 1; } if ($$gff{Static}) { warning("SPS: Shop placeable $name ($$gff{Tag}) in area " . $FixupModule::resource . " is static (fixed)"); $$gff{Static} = 0; $FixupModule::modified = 1; } match_item_variable_list($gff, "Restock", 0, "Time"); match_item_variable_list($gff, "Fill", 0); foreach $i (@{$$gff{ItemList}}) { check_item($i, $$i{''} . ' of area ' . $FixupModule::resource); } } if ($$gff{Tag} =~ /^guild_(.*)$/i) { $FixupModule::guild{$1} = 1; $vargff = $gff->variable("Name"); if (!defined($vargff)) { warning("GNM: Guild $$gff{Tag} is missing variable Name on \"" . $FixupModule::resource . "\""); } match_variable($gff, "MemberType", 1, "[0-5]", 0); match_variable_list($gff, "PriClass", 3, $FixupModule::class_regexp, 0); match_variable_list($gff, "SecClass", 3, $FixupModule::class_regexp, 0); match_variable_list($gff, "Alignments", 3, "[CL][GNE]|N[GE]|TN", 0); match_guild_variable_list($gff, "ConflictingGuilds", 0); match_guild_variable_list($gff, "RequiredGuilds", 0); match_variable($gff, "MinLevel", 1, '\d+', 0); match_variable($gff, "RequiredVariable1", 3, '^[^\s,]*,[^\s,]*,\d+,.*$', 0); match_variable($gff, "RequiredVariable2", 3, '^[^\s,]*,[^\s,]*,\d+,.*$', 0); match_variable($gff, "RequiredVariable3", 3, '^[^\s,]*,[^\s,]*,\d+,.*$', 0); match_variable($gff, "RequiredVariable4", 3, '^[^\s,]*,[^\s,]*,\d+,.*$', 0); match_variable($gff, "RequiredVariable5", 3, '^[^\s,]*,[^\s,]*,\d+,.*$', 0); match_variable($gff, "RequiredVariable6", 3, '^[^\s,]*,[^\s,]*,\d+,.*$', 0); match_variable_list($gff, "RequiredDomainds", 3, $FixupModule::domain_regexp, 0); match_variable_list($gff, "RequiredDeity", 3, $FixupModule::deity_regexp, 0); } $vargff = $gff->variable("Appearance"); if (!defined($vargff) || $vargff->varvalue ne $$gff{Appearance}) { $gff->variable("Appearance", $$gff{Appearance}); $FixupModule::modified = 1; } $vargff = $gff->variable("ScaleX"); if (!defined($vargff) || $vargff->varvalue ne $gff->value("ModelScale/x") || $vargff->vartype() != 2) { $gff->variable("ScaleX", $gff->value("ModelScale/x"), 2); $FixupModule::modified = 1; } $vargff = $gff->variable("ScaleY"); if (!defined($vargff) || $vargff->varvalue ne $gff->value("ModelScale/y") || $vargff->vartype() != 2) { $gff->variable("ScaleY", $gff->value("ModelScale/y"), 2); $FixupModule::modified = 1; } $vargff = $gff->variable("ScaleZ"); if (!defined($vargff) || $vargff->varvalue ne $gff->value("ModelScale/z") || $vargff->vartype() != 2) { $gff->variable("ScaleZ", $gff->value("ModelScale/z"), 2); $FixupModule::modified = 1; } if (defined($FixupModule::chairs{$$gff{Appearance}})) { # Chair, make sure it is not special if ($$gff{OnUsed} =~ /^pl_sitonchair$/i || $$gff{OnUsed} =~ /^cp_g_sitonchair$/i || $$gff{OnUsed} eq '') { my($skip); $skip = 0; # Static = false # Usable = True # Default action = use foreach $i ('OnClosed', 'OnDamaged', 'OnDeath', 'OnDialog', 'OnDisarm', 'OnHeartbeat', 'OnInvDisturbed', 'OnLock', 'OnMeleeAttacked', 'OnOpen', 'OnSpellCastAt', 'OnTrapTriggered', 'OnUnlock', 'OnUserDefined') { if ($$gff{$i} ne '') { warning("PCIS: Chair placeable $name ($$gff{Tag}) in area " . $FixupModule::resource . " is has special scripts for $i : " . $$gff{$i}); $skip = 1; } } if (!$skip) { if ($$gff{OnUsed} ne "cp_g_sitonchair") { warning("PCWU: Chair Placelabe $name ($$gff{Tag}) in area " . $FixupModule::resource . " has wrong as OnUsed script (fixed)"); $$gff{OnUsed} = "cp_g_sitonchair"; $FixupModule::modified = 1; } if ($$gff{Static}) { warning("PCS: Chair Placelabe $name ($$gff{Tag}) in area " . $FixupModule::resource . " is static (fixed)"); $$gff{Static} = 0; $FixupModule::modified = 1; } if (!$$gff{Plot}) { warning("PCNP: Chair Placelabe $name ($$gff{Tag}) in area " . $FixupModule::resource . " is not plot (fixed)"); $$gff{Plot} = 1; $FixupModule::modified = 1; } if (!$$gff{Useable}) { warning("PCNU: Chair Placelabe $name ($$gff{Tag}) in area " . $FixupModule::resource . " is not useable (fixed)"); $$gff{Useable} = 1; $FixupModule::modified = 1; } if (!$$gff{DefAction}) { warning("PCDA: Chair Placelabe $name ($$gff{Tag}) in area " . $FixupModule::resource . " Default Action is not Use (fixed)"); $$gff{DefAction} = 1; $FixupModule::modified = 1; } } } } } ###################################################################### # Check triggers sub check_triggers { my($gff, $full_label, $label, $value, $parent_gffs) = @_; my($name, $i); $name = $$gff{LocalizedName}{0}; if ($$gff{Tag} =~ /^(shop_[^_]*_[^_]*)_exit$/) { my($i); $FixupModule::shop_trigger{$1} = 1; if ($$gff{ScriptOnEnter} ne "ce_g_i_leaveshop") { if ($$gff{ScriptOnEnter} eq "") { warning("STMS: Shop trigger $name ($$gff{Tag}) in area " . $FixupModule::resource . " does not have ScriptOnEnter script (fixed)"); $$gff{ScriptOnEnter} = "ce_g_i_leaveshop"; $FixupModule::modified = 1; } else { warning("STWS: Shop trigger $name ($$gff{Tag}) in area " . $FixupModule::resource . " has wrong ScriptOnEnter script: " . $$gff{ScriptOnEnter}); } } } if ($$gff{ScriptOnEnter} eq 'ce_g_t_worldmap') { if ($$gff{OnClick} ne 'ce_g_t_worldmap') { warning("WMIC: Worldmap transition $name ($$gff{Tag}) in area " . $FixupModule::resource . " does not have OnClick script (fixed)"); $$gff{OnClick} = "ce_g_t_worldmap"; $FixupModule::modified = 1; } $FixupModule::wm_trigger_area{$$gff{Tag}} = $FixupModule::resource; for($i = 0; $i <= $#{$$gff{VarTable}}; $i++) { $FixupModule::wm_trigger{$$gff{Tag}}[$i] = $$gff{VarTable}[$i]{Name}; } if ($name !~ /^Travel/) { warning("WMIN: Worldmap transition $name ($$gff{Tag}) in area " . $FixupModule::resource . " has invalid name (not starting with Travel)"); } elsif ($name !~ /^Travel$/) { warning("WMIN: Worldmap transition $name ($$gff{Tag}) in area " . $FixupModule::resource . " has does not have area name (fixed)"); $$gff{LocalizedName}{0} = "Travel {" . $FixupModule::resource . "}"; $FixupModule::modified = 1; } } } ###################################################################### # Check waypoints sub check_waypoints { my($gff, $full_label, $label, $value, $parent_gffs) = @_; my($name); if (defined($$gff{LocalizedName}{0})) { $name = $$gff{LocalizedName}{0} . " (" . $$gff{Tag} . ")" . " in area " . $FixupModule::resource; } else { $name = " (" . $$gff{Tag} . ")" . " in area " . $FixupModule::resource; } if ($$gff{Tag} =~ /^WP_(.*)$/) { push(@{$FixupModule::wm_wp{$1}}, $FixupModule::resource); } $FixupModule::waypoint_tags{$$gff{Tag}} = $name; } ###################################################################### # Match variable with type and regexp sub match_variable { my($gff, $var, $type, $regexp, $mandatory) = @_; my($vargff); $vargff = $gff->variable($var); if (!defined($vargff)) { if ($mandatory) { warning("IMV: Item $$gff{Tag} is missing variable $var on \"" . $FixupModule::resource . "\""); } return; } if ($vargff->vartype != $type) { warning("IVT: Item $$gff{Tag} variable $var has wrong type " . $vargff->vartype . " != " . $type . " on \"" . $FixupModule::resource . "\""); } if ($vargff->varvalue !~ /$regexp/i) { warning("IVVW: Item $$gff{Tag} variable $var value " . $vargff->varvalue . " does not match regexp " . $regexp . " on \"" . $FixupModule::resource . "\""); } } ###################################################################### # Match variable with list of items with type and regexp sub match_variable_list { my($gff, $var, $type, $regexp, $mandatory) = @_; my($vargff, @items, $i); $vargff = $gff->variable($var); if (!defined($vargff)) { if ($mandatory) { warning("ILMV: Item $$gff{Tag} is missing variable $var on \"" . $FixupModule::resource . "\""); } return; } if ($vargff->vartype != $type) { warning("ILVT: Item $$gff{Tag} variable $var has wrong type " . $vargff->vartype . " != " . $type . " on \"" . $FixupModule::resource . "\""); } @items = split(/\s*,\s*/, $vargff->varvalue); foreach $i (@items) { if ($i !~ /$regexp/i) { warning("ILVVW: Item $$gff{Tag} variable $var list value " . $vargff->varvalue . " item " . $i . " does not match regexp " . $regexp . " on \"" . $FixupModule::resource . "\""); } } } ###################################################################### # Match guild variable with list of guild names sub match_guild_variable_list { my($gff, $var, $mandatory) = @_; my($vargff, @items, $i); $vargff = $gff->variable($var); if (!defined($vargff)) { if ($mandatory) { warning("IGMV: Item $$gff{Tag} is missing variable $var on \"" . $FixupModule::resource . "\""); } return; } if ($vargff->vartype != 3) { warning("IGVT: Item $$gff{Tag} variable $var has wrong type " . $vargff->vartype . " != 3 on \"" . $FixupModule::resource . "\""); } @items = split(/\s*,\s*/, $vargff->varvalue); foreach $i (@items) { if ($i =~ /^guild_/i) { warning("IGGP: Item $$gff{Tag} variable $var list value " . $vargff->varvalue . " item " . $i . " has guild_ prefix, should have that on \"" . $FixupModule::resource . "\""); } if (defined($FixupModule::guilds_seen{$i})) { $FixupModule::guilds_seen{$i} .= ", " . $$gff{Tag}; } else { $FixupModule::guilds_seen{$i} = $$gff{Tag}; } } } ###################################################################### # Match item variable with list of items resrefs sub match_item_variable_list { my($gff, $var, $mandatory, $timevar) = @_; my($vargff, @items, $i); $vargff = $gff->variable($var); if (!defined($vargff)) { if ($mandatory) { warning("SIMV: Shop $$gff{Tag} is missing variable $var on \"" . $FixupModule::resource . "\""); } return; } if ($vargff->vartype != 3) { warning("SIVT: Shop $$gff{Tag} variable $var has wrong type " . $vargff->vartype . " != 3 on \"" . $FixupModule::resource . "\""); } @items = split(/\s*,\s*/, $vargff->varvalue); foreach $i (@items) { if (!defined($FixupModule::uti{lc($i)})) { warning("SIMR: Shop $$gff{Tag} variable $var list value " . $vargff->varvalue . " item " . $i . " has resref that is not found from module on \"" . $FixupModule::resource . "\""); } } if (defined($timevar)) { my($timevargff); $i = $#items; $timevargff = $gff->variable($timevar); @items = split(/\s*,\s*/, $timevargff->varvalue); if ($#items != $i) { warning("SIWC: Shop $$gff{Tag} variable $var list value " . $vargff->varvalue . " and timelist $timevar " . $timevargff->varvalue . " item count does not match on \"" . $FixupModule::resource . "\""); } } } ###################################################################### # Convert environmental object to placeable. # Unfinished, waiting for Quarterion processing to get bearing... sub XXX_convert_env_to_placeable { my($gff, $env_gff) = @_; my($plc, $i); $plc = Gff->new(); $plc->value('', '/Placeable List[x]'); $plc->value(' ____struct_type', 9); # Bytes foreach $i ('AnimationState', 'AutoRemoveKey', 'BodyBag', 'CloseLockDC', 'ContainerUI', 'DefAction', 'DisarmDC', 'DynamicCl', 'Fort', 'Hardness', 'HasInventory', 'Interruptable', 'IsWalkable', 'KeyRequired', 'Lockable', 'Locked', 'OpenLockDC', 'Plot') { $plc->value($i, 0, 0); } # Resrefs foreach $i ('AppearanceSEF', 'Conversation', 'OnClosed', 'OnDamaged', 'OnDeath', 'OnDialog', 'OnDisarm', 'OnHeartbeat', 'OnInvDisturbed', 'OnLock', 'OnMeleeAttacked', 'OnOpen', 'OnSpellCastAt', 'OnTrapTriggered', 'OnUnlock', 'OnUsed', 'OnUserDefined') { $plc->value($i, '', 11); } # Strings foreach $i ('KeyName', '') { $plc->value($i, '', 11); } # Floats foreach $i ('Bearing') { $plc->value($i, 0.0, 8); } # Shorts foreach $i ('CurrentHP', 'HP') { $plc->value($i, 0, 3); } $plc->value('InventorySize', 32, 3); $plc->value('ItemList. ____type', 15); # DWords foreach $i ('Faction') { $plc->value($i, 0, 4); } # Copy values $plc->value('Appearance', $env_gff->value('Appearance'), 4); $plc->value('PlcblCastsShadow', $env_gff->value('EnvCastsShadow'), 0); $plc->value('PlcblRcvShadow', $env_gff->value('EnvRcvShadow'), 0); $plc->value('ModelScale/', 'Placeable List[x]/ModelScale'); $plc->value('ModelScale/ ____struct_type', 0); $plc->value('ModelScale/', 'Placeable List[x]/ModelScale'); foreach $i ('Classification', 'Description', 'GUID', 'HeightLock', 'LocName', 'ModelScale/x', 'ModelScale/y', 'ModelScale/z') { if ($env_gff->type($i) == 12) { if (defined($$env_gff{$i} . "/0")) { $plc->value($i . "/0", $env_gff->value($i . "/0")); } if (defined($$env_gff{$i . ". ____string_ref"})) { $plc->value($i . ". ____string_ref", $env_gff->value($i . ". ____string_ref")); } $plc->value($i . ". ____type", 12); } else { $plc->value($i, $env_gff->value($i), $env_gff->type($i)); } } $plc->value('ModelScale. ____type', 14); $plc->value("Tintable/ ____struct_type", 0); add_tintable($plc, "Tintable/Tint"); $plc->value("Tintable. ____type", 14); $plc->value("/VarTable. ____type", 15); # $plc->print(print_types => 1); push(@{$$gff{'Placeable List'}}, $plc); } ###################################################################### # Add waypoint with tag sub add_waypoint { my($gff, $tag, $name) = @_; my($wp); $wp = Gff->new(); $wp->value('', '/WaypointList[x]'); $wp->value(' ____struct_type', 5); $wp->value('Appearance', 0, 0); $wp->value('Classification', "", 10); $wp->value("Description/0", "Area Waypoint"); $wp->value("Description. ____string_ref", 4294967295); $wp->value("Description. ____type", 12); $wp->value("GUID", md5_hex($name), 13); $wp->value('HasMapNote', 0, 0); $wp->value('HeightLock', 0, 0); $wp->value('LinkedTo', "", 10); $wp->value("LocalizedName/0", $name); $wp->value("LocalizedName. ____string_ref", 4294967295); $wp->value("LocalizedName. ____type", 12); $wp->value("MapNote/0", "Area Waypoint"); $wp->value("MapNote. ____string_ref", 4294967295); $wp->value("MapNote. ____type", 12); $wp->value('MapNoteEnabled', 0, 0); $wp->value('PositionLock', 0, 0); $wp->value('Stackable', 0, 0); $wp->value('Tag', $tag, 10); $wp->value('TemplateResRef', $tag, 11); $wp->value("Tintable/ ____struct_type", 0); add_tintable($wp, "Tintable/Tint"); $wp->value("Tintable. ____type", 14); $wp->value("/VarTable. ____type", 15); $wp->value('XOrientation', 0, 8); $wp->value('XPosition', 0, 8); $wp->value('YOrientation', 0, 8); $wp->value('YPosition', 0, 8); $wp->value('ZOrientation', 0, 8); $wp->value('ZPosition', 0, 8); # $wp->print(print_types => 1); push(@{$$gff{'WaypointList'}}, $wp); # $gff->print(print_types => 1); } ###################################################################### # Find waypoint variables sub find_waypoint_variables { my($gff, $full_label, $label, $value, $parent_gffs) = @_; if ($$gff{Tag} eq 'cw_area_waypoint') { $FixupModule::area_waypoint = 1; } if ($$gff{Tag} =~ /^(shop_[^_]*_[^_]*)$/) { my($i); $FixupModule::shops{$1} = 1; foreach $i ("BuyRate", "SellRate", "Appraise", "Money", "MaxBuy") { my($vargff); $vargff = $gff->variable($i); if (!defined($vargff)) { warning("SVM: Shop $$gff{Tag} is missing variable $i on \"" . $FixupModule::resource . "\""); } elsif ($vargff->vartype != 1) { warning("SVT: Shop $$gff{Tag} variable $i has " . "wrong type on \"" . $FixupModule::resource . "\""); } } if ($gff->variable("BuyRate")->varvalue > $gff->variable("SellRate")->varvalue) { warning("SBSW: Shop $$gff{Tag} has BuyRate (" . $gff->variable("BuyRate")->varvalue . ") > SellRate (" . $gff->variable("SellRate")->varvalue . ") on \"" . $FixupModule::resource . "\""); } } if ($$gff{Tag} =~ /^(QUEST_.*)$/i) { my($quest, $i, $tmp, $var); $quest = $1; print("Found quest: $quest from $FixupModule::resource\n") if ($Opt::verbose > 3); if ($quest eq 'QUEST_TAG_UNIQUEID') { warning("QTUF: Found QUEST_TAG_UNIQUEID from \"" . $FixupModule::resource . "\" with waypoint name of \"" . $$gff{LocalizedName}{0} . "\""); } if (defined($FixupModule::quests{$quest})) { warning("QDQT: Found duplicate quest tag \"$quest\" from \"" . $FixupModule::resource . "\" with waypoint name of \"" . $$gff{LocalizedName}{0} . "\" previous quest had name of \"" . $FixupModule::quests{$quest}{Name} . "\" in the area of \"" . $FixupModule::quests{$quest}{Area} . "\""); return; } $FixupModule::quests{$quest}{Name} = $$gff{LocalizedName}{0}; $FixupModule::quests{$quest}{Area} = $FixupModule::resource; foreach $i ("LEVEL", "PUBLIC", "TEXT_SUCCESS") { get_var($gff, $quest, $i, 1); } foreach $i ("QUEST_DESCRIPTION", "PRIORITY", "TEXT_FAIL") { get_var($gff, $quest, $i); } if (defined($FixupModule::quests{$quest}{PUBLIC}) && $FixupModule::quests{$quest}{PUBLIC} >= 1) { foreach $i ("LIST_TITLE", "LIST_TEXT", "LIST_WHERE") { get_var($gff, $quest, $i, 1); } if ($FixupModule::quests{$quest}{PUBLIC} >= 2) { get_var($gff, $quest, "PRE_QUEST", 1); } } else { get_var($gff, $quest, "LIST_TITLE", 0); } if (!defined($FixupModule::quests{$quest}{LIST_TITLE}) || $FixupModule::quests{$quest}{LIST_TITLE} eq '') { $FixupModule::quests{$quest}{LIST_TITLE} = $FixupModule::quests{$quest}{Name}; if ($FixupModule::quests{$quest}{LIST_TITLE} eq '') { $FixupModule::quests{$quest}{LIST_TITLE} = ""; } } for($i = 2; ; $i++) { if (!get_var($gff, $quest, "TEXT_SUCCESS$i")) { last; } } $FixupModule::quests{$quest}{NUM_SUCCESSES} = $i - 1; for($i = 2; ; $i++) { if (!get_var($gff, $quest, "TEXT_FAIL$i")) { last; } } $FixupModule::quests{$quest}{NUM_FAILS} = $i - 1; for($i = 1; ; $i++) { if (!get_var($gff, $quest, "TEXT_$i")) { last; } } $FixupModule::quests{$quest}{NUM_STEPS} = $i - 1; # Id mapping of the numbers above to the IDs in the journal. # success = 1001 => 2001 # success2 = 1002 => 2002 # text_1 => 1 => 1001 # text_2 => 2 => 1002 # fail = -1 => 999 # fail2 = -2 => 998 } } ###################################################################### # Generate journal sub generate_journal { my($journal); my($quest, $cat, $entry, $tmp, $i); $journal = Gff->new(); $journal->file_type('JRL '); $journal->file_version('V3.2'); $journal->value("/ ____struct_type", 4294967295); $cat = 0; foreach $quest (keys %FixupModule::quests) { $journal->value("/Categories[$cat]/ ____struct_type", $cat); $tmp = $FixupModule::quests{$quest}{QUEST_DESCRIPTION}; $tmp = fixup_string($tmp); $journal->value("/Categories[$cat]/Comment", $tmp, 10); $tmp = $FixupModule::quests{$quest}{Name}; $tmp = fixup_string($tmp); $journal->value("/Categories[$cat]/Name/0", $tmp); $journal->value("/Categories[$cat]/Name. ____string_ref", 4294967295); $journal->value("/Categories[$cat]/Name. ____type", 12); $tmp = $FixupModule::quests{$quest}{PRIORITY}; if (!defined($tmp)) { $tmp = 2; } $journal->value("/Categories[$cat]/Picture", 65535, 2); $journal->value("/Categories[$cat]/Priority", $tmp, 4); $journal->value("/Categories[$cat]/Tag", $quest, 10); $journal->value("/Categories[$cat]/XP", 0, 4); $entry = 0; add_entry($journal, $quest, $cat, $entry++, "TEXT_SUCCESS", 2001, 1); for($i = 2; defined($FixupModule::quests{$quest}{"TEXT_SUCCESS$i"}); $i++) { add_entry($journal, $quest, $cat, $entry++, "TEXT_SUCCESS$i", 2000 + $i, 1); } if (defined($FixupModule::quests{$quest}{"TEXT_FAIL"})) { add_entry($journal, $quest, $cat, $entry++, "TEXT_FAIL", 999, 1); } for($i = 2; defined($FixupModule::quests{$quest}{"TEXT_FAIL$i"}); $i++) { add_entry($journal, $quest, $cat, $entry++, "TEXT_FAIL$i", 1000 - $i, 1); } for($i = 1; defined($FixupModule::quests{$quest}{"TEXT_$i"}); $i++) { add_entry($journal, $quest, $cat, $entry++, "TEXT_$i", 1000 + $i, 0); } $cat++; } $journal->value("/Categories. ____type", 15); # $journal->print(print_types => 1); return GffWrite::write($journal); } ###################################################################### # add_entry sub add_entry { my($journal, $quest, $cat, $entry, $var, $id, $end) = @_; my($txt); $txt = $FixupModule::quests{$quest}{$var}; $txt = fixup_string($txt); $journal->value("/Categories[$cat]/EntryList[$entry]/ ____struct_type", $entry); $journal->value("/Categories[$cat]/EntryList[$entry]/End", $end, 2); $journal->value("/Categories[$cat]/EntryList[$entry]/ID", $id, 4); $journal->value("/Categories[$cat]/EntryList[$entry]/Text/0", $txt); $journal->value("/Categories[$cat]/EntryList[$entry]/Text. ____string_ref", 4294967295); $journal->value("/Categories[$cat]/EntryList[$entry]/Text. ____type", 12); $journal->value("/Categories[$cat]/EntryList. ____type", 15); } ###################################################################### # Get variable, return true if found sub get_var { my($gff, $quest, $var, $warn_if_not_found) = @_; my($vargff); $vargff = $gff->variable($var); if (!defined($vargff)) { if (defined($warn_if_not_found) && $warn_if_not_found) { warning("QMV: Quest \"$quest\" in " . $FixupModule::resource . "\" with waypoint name of \"" . $$gff{LocalizedName}{0} . " does not have variable $var"); } return 0; } $FixupModule::quests{$quest}{$var} = $vargff->varvalue; return 1; } ###################################################################### # Generate adventure guild list items sub generate_adv_guild_lists { my($erf) = @_; my($quest, %quests, $level, $gff, $i); # Generate quest lists per level foreach $quest (keys %FixupModule::quests) { if (!defined($FixupModule::quests{$quest}{PUBLIC}) || $FixupModule::quests{$quest}{PUBLIC} < 1) { push(@{$quests{0}}, $quest); } else { push(@{$quests{$FixupModule::quests{$quest}{LEVEL}}}, $quest); } } foreach $level (sort { $a <=> $b } keys %quests) { $gff = generate_base_item($level); $i = 0; print("Generating level $level item\n") if ($Opt::verbose > 4); foreach $quest (sort { $FixupModule::quests{$a}{LIST_TITLE} cmp $FixupModule::quests{$b}{LIST_TITLE}; } @{$quests{$level}}) { print("Processing quest $quest\n") if ($Opt::verbose > 5); $gff->variable("QUEST_$i", $quest); $gff->variable("NUM_SUCCESSES_$i", $FixupModule::quests{$quest}{NUM_SUCCESSES}); $gff->variable("NUM_FAILS_$i", $FixupModule::quests{$quest}{NUM_FAILS}); $gff->variable("NUM_STEPS_$i", $FixupModule::quests{$quest}{NUM_STEPS}); $i++; } $gff->variable("QUESTS", $i); # $gff->print(print_types => 1); $i = $erf->new_resource("ci_g_adv_guild_quest_list_$level", $Erf::extensions2types{'uti'}); $erf->resource_data($i, GffWrite::write($gff)); } } ###################################################################### # Generate adventure guild base item sub generate_base_item { my($level) = @_; my($gff); my($i); $gff = Gff->new(); $gff->file_type('UTI '); $gff->file_version('V3.2'); $gff->value('', ''); $gff->value("/ ____struct_type", 4294967295); foreach $i (qw/ACBkHip ACBkHip ACFtHip ACLtAnkle ACLtArm ACLtBracer ACLtElbow ACLtFoot ACLtHip ACLtKnee ACLtLeg ACLtShin ACLtShoulder ACRtAnkle ACRtArm ACRtBracer ACRtElbow ACRtFoot ACRtHip ACRtKnee ACRtLeg ACRtShin ACRtShoulder/) { $gff->value("/$i/ ____struct_type", 0); $gff->value("/$i/Accessory", 0, 0); $gff->value("/$i/Tintable/ ____struct_type", 0); add_tintable($gff, "/$i/Tintable/Tint"); $gff->value("/$i/Tintable. ____type", 14); add_uvscroll($gff, "/$i/UVScroll"); $gff->value("/$i. ____type", 14); } add_tintable($gff, "/ArmorTint"); $gff->value("/Tintable/ ____struct_type", 0); add_tintable($gff, "/Tintable/Tint"); $gff->value("/Tintable. ____type", 14); $gff->value("/AppearanceSEF", "", 11); $gff->value("/ArmorRulesType", 0, 0); $gff->value("/ArmorVisualType", 0, 0); $gff->value("/BaseItem", 24, 5); $gff->value("/Charges", 0, 0); $gff->value("/Classification", "", 10); $gff->value("/Comment", "", 10); $gff->value("/ContainerPref", 0, 0); $gff->value("/ContainerUI", 0, 0); $gff->value("/Cost", 0, 4); $gff->value("/Cursed", 0, 0); $gff->value("/DescIdentified/0", ""); $gff->value("/DescIdentified. ____string_ref", 4294967295); $gff->value("/DescIdentified. ____type", 12); $gff->value("/Description/0", ""); $gff->value("/Description. ____string_ref", 4294967295); $gff->value("/Description. ____type", 12); $gff->value("/DmgReduction. ____type", 15); $gff->value("/Dropable", 0, 0); $gff->value("/ForceContainer", 0, 0); $gff->value("/GMaterial", 0, 3); $gff->value("/IPActPref", 0, 0); $gff->value("/Icon", 1267, 4); $gff->value("/Identified", 1, 0); $gff->value("/ItemCastsShadow", 14, 0); $gff->value("/ItemRcvShadow", 6, 0); $gff->value("/LocalizedName/0", "ci_g_adv_guild_quest_list_$level"); $gff->value("/LocalizedName. ____string_ref", 4294967295); $gff->value("/LocalizedName. ____type", 12); $gff->value("/ModelPart1", 1, 0); $gff->value("/ModelPart2", 1, 0); $gff->value("/ModelPart3", 1, 0); $gff->value("/ModifyCost", 0, 5); $gff->value("/PaletteID", 0, 0); $gff->value("/Pickpocketable", 0, 0); $gff->value("/Plot", 0, 0); $gff->value("/PropertiesList. ____type", 15); $gff->value("/StackSize", 1, 2); $gff->value("/Stolen", 0, 0); $gff->value("/Tag", "ci_g_adv_guild_quest_list_$level", 10); $gff->value("/TemplateResRef", "ci_g_adv_guild_quest_list_$level", 11); add_uvscroll($gff, "/UVScroll"); $gff->value("/VarTable. ____type", 15); $gff->value("/Variation", 0, 0); $gff->variable("Price", 0); $gff->variable("Base", 0); return $gff; } ###################################################################### # Add tintable structure sub add_tintable { my($gff, $base) = @_; my($i, $j); $gff->value("$base/ ____struct_type", 0); foreach $i ("1", "2", "3") { $gff->value("$base/$i/ ____struct_type", 0); foreach $j ("a", "b", "g", "r") { $gff->value("$base/$i/$j", 255, 0); } $gff->value("$base/$i. ____type", 14); } $gff->value("$base. ____type", 14); } ###################################################################### # Add uvscroll structure sub add_uvscroll { my($gff, $base) = @_; my($i, $j); $gff->value("$base/ ____struct_type", 0); $gff->value("$base/Scroll", 0, 5); $gff->value("$base/U", 0, 8); $gff->value("$base/V", 0, 8); $gff->value("$base. ____type", 14); } ###################################################################### # Update module ifo file sub update_module_ifo { my($erf, $module_ifo_id) = @_; my($gff, $tlk); $gff = GffRead::read(data => $erf->resource_data($module_ifo_id), return_errors => 1); if (!defined($gff)) { die "Cannot read " . $erf->resource_reference($module_ifo_id); } $gff->value("/Mod_Description/0", "Beta Version " . $Opt::cerea_version . " of the continued tales from Cerea Island"); $tlk = $gff->value("/Mod_CustomTlk"); if (defined($tlk) && $tlk =~ /(.*)\.tlk$/) { $gff->value("/Mod_CustomTlk", $1); } $gff->variable("Version", $Opt::cerea_version); $gff->variable("newline", "\\n"); $gff->variable("guild_list", join(",", keys %FixupModule::guild)); $erf->resource_data($module_ifo_id, GffWrite::write($gff)); } ###################################################################### # $str = fixup_string($str) # Make sure $str is not undef, and replace \n with newlines in it. sub fixup_string { my($str) = @_; $str = '' if (!defined($str)); $str =~ s/\\n/\n/g; return $str; } ###################################################################### # warning($string) sub warning { my($str) = @_; $FixupModule::warnings++; return if (defined($Opt::warningsskip) && $str =~ $Opt::warningsskip); $FixupModule::shown_warnings++; print(STDERR $str, "\n"); STDERR->autoflush(1); $| = 1; } ###################################################################### # Return Success 1; ###################################################################### # EOF ######################################################################