19/10/2003 @22:42:27 ^23:53:58

It's type in listing time again everyone!

Ages and ages ago I made a fairly useless factorisation program and, 1980's computer magazine style, invited people to type it in. There was also radioshow which slightly more usefully will record audio going into your sound card and mp3 it as it goes along, for a given length of time.

Today following this thread on alt.games.doom.ii I was finally sufficiently motivated to make dumptriggers, a script that goes through a WAD file, and for each line in each map in the WAD that has a trigger and a tag number (that is, controls some remote action like a door opening or floor lowering) prints out the line number, the action number, and the sector numbers which the line affects. The idea is as follows: say I have a secret door for which I can't find the switch or tripwire. I can go into Yadex and get its sector number, then grep the output for that sector number. This gives me a line number that I can search for, back in Yadex.

dumptriggers [wadfile.wad] will give you a bunch of lines that look like this

mapname Llinedef Aaction Ssector Ssector Ssector...

some real examples from doom2.wad MAP01:

MAP01 L00126 A062 S021
MAP01 L00084 A102 S046 S047 S050 S051

The first says that line 126 being activated will cause action 62 to occur to sector 21. The second says that line 84 does action 102 on sectors 46, 47, 50 and 51. Because I knocked this thing up in an afternoon it doesn't do anything useful like tell you what actions 62 or 102 actually are, you'll have to look them up. (As it happens the first is the lift with the imp in the large brown room at the end, and the second is the switch that lowers the four floors in the green and blue room at the top of the map)

Finally before I leave you with the listing to cut and paste type in, a note relevant to the usenet thread mentioned above: The UDS, despite being one of the most beautiful and inspiring documents ever written, nonetheless contains an occasional error. In particular if you take it at face value and only search for the linedef action numbers which it refers to as crushing ceiling activators, you will miss some, including significantly the large one that gives Doom2 MAP06 its name.

dumptriggers is hereby placed into the public domain, "as is", without any guarantee of fitness for a particular purpose; and if something screws up it's your responsibility and not mine! There are a lot of error checks it should do, but doesn't, nevertheless it runs successfully on the latest versions of doom2.wad and doomu.wad. Enjoy!

#!/usr/bin/perl
# dumptriggers 
# by RjY of anARCHy, 19:2/3/2003
# Placed into the public domain. Do what you like but don't bother me about it
use strict;
use warnings;

# takes optional wad file argument, else uses default filename as below
my $WAD = $ARGV[0] ? $ARGV[0] : "/usr/local/share/games/doom/doom2.wad";
open F, $WAD or die "$0: Cannot open $WAD: $!\n";

my ($BUF, $WAD_ID, $N, $dir, @start, @length, @name);

# read WAD file header
# see the UDS <http://www.gamers.org/dhs/helpdocs/dmsp1666.html> for WAD format
read F, $BUF, 12;
($WAD_ID, $N, $dir) = unpack("a4LL", $BUF);
die "$0: $WAD is not a WAD" unless ($WAD_ID =~ m/[IP]WAD/ && $N < 32000);

# read WAD file directory
seek F, $dir, 0;
for (my $i = 0; $i < $N; $i++) {
	read F, $BUF, 16;
	($start[$i], $length[$i], $name[$i]) = unpack("LLZ8", $BUF);
}

# main loop through possible map names
for (my $lev = 11; $lev < 83; $lev++) {
	my $mapname;
	# godsawful hack to work transparently with both types of map name
	if ($lev < 51) {
		$mapname = sprintf("E%1dM%1d", int $lev / 10, $lev % 10);
	} else {
		$mapname = sprintf("MAP%02d", $lev - 50);
	}

	my $i = 0;
	$i++ until ($i >= $N || $name[$i] eq $mapname);
	next if $i>=$N; # continue if this map name cannot be found

	# load linedefs and sectors
	my $j = $i; $j++ until $name[$j] eq 'LINEDEFS';
	my $lines; seek F, $start[$j], 0; read F, $lines, $length[$j];
	$j = $i; $j++ until $name[$j] eq 'SECTORS';
	my $sectors; seek F, $start[$j], 0; read F, $sectors, $length[$j];

	# construct hashes of line number to action, number to tag
	my (%actions, %tags, %sectags);
	for ($i = 0; 14*$i < length $lines; $i++) {
		my ($action, $tag) = unpack("SS", substr($lines, 14*$i+6, 4));
		if ($action && $tag) {
			$actions{$i} = $action;
			$tags{$i} = $tag;
		}
	}
	# make a list of sectors with a given tag
	for ($i = 0; 26*$i < length $sectors; $i++) {
		my $sectag = unpack("S", substr($sectors, 26*$i+24, 2));
		if ($sectag) {
			push @{$sectags{$sectag}}, $i;
		}
	}
	# loop through the actions found with list of associated sectors
	for (keys %actions) {
		printf("%s L%05d A%03d", $mapname, $_, $actions{$_});
		printf(" S%03d", $_) for (@{$sectags{$tags{$_}}});
		print "\n";
	}
}

close F;