dmolony-DiskBrowser/src/com/bytezone/diskbrowser/wizardry/MazeLevel.java

589 lines
20 KiB
Java
Executable File

package com.bytezone.diskbrowser.wizardry;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class MazeLevel extends AbstractFile
// -----------------------------------------------------------------------------------//
{
private static final int AUX0 = 768;
private static final int AUX1 = 800;
private static final int AUX2 = 832;
private static final String[] squareType =
{ "Normal", "Stairs", "Pit", "Chute", "Spinner", "Darkness", "Teleport", "Ouch", "Elevator",
"Rock/Water", "Fizzle", "Message/Item", "Monster" };
public final int level;
private List<MessageV1> messages;
private List<MonsterV1> monsters;
private List<ItemV1> items;
// ---------------------------------------------------------------------------------//
public MazeLevel (byte[] buffer, int level)
// ---------------------------------------------------------------------------------//
{
super ("Level " + level, buffer);
this.level = level;
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append ("West walls/doors\n\n");
text.append (HexFormatter.format (buffer, 0, 120, true, 0));
addWalls (text, 0);
text.append ("\nSouth walls/doors\n\n");
text.append (HexFormatter.format (buffer, 120, 120, true, 120));
addWalls (text, 120);
text.append ("\nEast walls/doors\n\n");
text.append (HexFormatter.format (buffer, 240, 120, true, 240));
addWalls (text, 240);
text.append ("\nNorth walls/doors\n\n");
text.append (HexFormatter.format (buffer, 360, 120, true, 360));
addWalls (text, 360);
text.append ("\nFIGHTS\n\n");
text.append (HexFormatter.format (buffer, 480, 80, true, 480));
addEncounters (text, 480);
text.append ("\nSQREXTRA\n\n");
text.append (HexFormatter.format (buffer, 560, 200, true, 560));
addExtras (text, 560);
text.append ("\nSQRTYPE\n\n");
text.append (String.format ("%04X: %s%n", 760, HexFormatter.getHexString (buffer, 760, 8)));
text.append ("\nAUX0\n\n");
text.append (HexFormatter.format (buffer, AUX0, 32, true, AUX0));
text.append ("\n");
text.append ("\nAUX1\n\n");
text.append (HexFormatter.format (buffer, AUX1, 32, true, AUX1));
text.append ("\n");
text.append ("\nAUX2\n\n");
text.append (HexFormatter.format (buffer, AUX2, 32, true, AUX2));
text.append ("\n");
List<MazeAddress> messageList = new ArrayList<> ();
List<MazeAddress> monsterList = new ArrayList<> ();
addTable (text, messageList, monsterList);
text.append ("\n\nENMYCALC\n\n");
text.append (HexFormatter.format (buffer, 864, buffer.length - 864, true, 864));
addEnmyCalc (text, 864);
text.append ("\n\n");
for (MazeAddress address : messageList)
{
MessageV1 message = getMessage (address.row);
if (message != null)
{
text.append (String.format ("%nMessage: %04X (%d)%n", address.row, address.row));
text.append (message.getText ());
text.append ("\n");
}
}
for (MazeAddress address : monsterList)
{
MonsterV1 monster = getMonster (address.column);
if (monster != null)
{
text.append (String.format ("%nMonster: %04X%n", address.column));
text.append (monster.getText ());
text.append ("\n");
}
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private void addWalls (StringBuilder text, int ptr)
// ---------------------------------------------------------------------------------//
{
text.append ("\n\n");
for (int col = 0; col < 20; col++)
{
text.append (
String.format (" Col %2d : %s : ", col, HexFormatter.getHexString (buffer, ptr, 6)));
for (int i = 0; i < 5; i++)
{
int val = buffer[ptr++] & 0xFF;
for (int j = 0; j < 4; j++)
{
int wall = (val & 0x03); // right to left ordering
text.append (String.format ("%d ", wall));
val >>>= 2;
}
}
// assert buffer[ptr] == 0;
ptr++; // skip last byte
text.append ("\n");
}
}
// ---------------------------------------------------------------------------------//
private void addEncounters (StringBuilder text, int ptr)
// ---------------------------------------------------------------------------------//
{
text.append ("\n\n");
for (int i = 0; i < 20; i++)
{
text.append (
String.format (" Col %2d: %s ", i, HexFormatter.getHexString (buffer, ptr + i * 4, 4)));
int val = Utility.readTriple (buffer, ptr + i * 4);
for (int j = 0; j < 20; j++)
{
text.append ((val & 0x01) == 0 ? " " : " 1");
val >>>= 1;
}
text.append ("\n");
}
}
// ---------------------------------------------------------------------------------//
private void addExtras (StringBuilder text, int ptr)
// ---------------------------------------------------------------------------------//
{
text.append ("\n\n");
for (int i = 0; i < 20; i++)
{
text.append (String.format (" Col %2d: ", i));
for (int j = 0; j < 10; j++)
{
int val = buffer[ptr++] & 0xFF;
int left = (val & 0xF0) >> 4;
int right = (val & 0x0F);
String sLeft = left == 0 ? " " : String.format ("%X", left);
String sRight = right == 0 ? " " : String.format ("%X", right);
text.append (String.format ("%s %s ", sRight, sLeft));
}
text.append ("\n");
}
}
// ---------------------------------------------------------------------------------//
private void addTable (StringBuilder text, List<MazeAddress> messageList,
List<MazeAddress> monsterList)
// ---------------------------------------------------------------------------------//
{
if (monsters == null)
{
System.out.println ("monsters is null");
return;
}
text.append ("\n\nSQREXTRA SQRTYPE TSQUARE AUX0 AUX1 AUX2\n");
for (int j = 0; j < 16; j++)
{
String extraText = "";
int val = buffer[760 + j / 2] & 0xFF;
String extra = (j % 2) == 0 ? squareType[val % 16] : squareType[val / 16];
MazeAddress address = getAddress (j);
int cellFlag = (j % 2) == 0 ? val % 16 : val / 16;
if (cellFlag == 11)
{
extraText = "Msg:" + String.format ("%04X ", address.row);
messageList.add (address); // to print at the end
int messageType = address.column; // AUX3
if (messageType == 2)
{
extraText += "Obtained: ";
if (items != null)
extraText += items.get (address.level).getName ();
}
if (messageType == 4)
{
if (address.level < monsters.size ())
extraText += monsters.get (address.level).getName ();
else
extraText += "Obtained: " + items.get ((address.level - 64536) * -1).getName ();
}
if (messageType == 5)
{
extraText += "Requires: ";
if (items != null)
extraText += items.get (address.level).getName ();
}
}
if (cellFlag == 12)
{
monsterList.add (address);
extraText = "Encounter: ";
if (monsters != null)
extraText += monsters.get (address.column).getName ();
}
text.append (String.format (" %X --> %X %-15s %04X %04X %04X %s%n", j,
cellFlag, extra, address.level, address.row, address.column, extraText));
}
}
// ---------------------------------------------------------------------------------//
private void addEnmyCalc (StringBuilder text, int ptr)
// ---------------------------------------------------------------------------------//
{
text.append ("\n\n");
int savePtr = ptr;
text.append (String.format ("MINENEMY %04X %04X %04X%n", Utility.getShort (buffer, ptr),
Utility.getShort (buffer, ptr + 10), Utility.getShort (buffer, ptr + 20)));
ptr += 2;
text.append (String.format ("MULTWORS %04X %04X %04X%n", Utility.getShort (buffer, ptr),
Utility.getShort (buffer, ptr + 10), Utility.getShort (buffer, ptr + 20)));
ptr += 2;
text.append (String.format ("WORSE01 %04X %04X %04X%n", Utility.getShort (buffer, ptr),
Utility.getShort (buffer, ptr + 10), Utility.getShort (buffer, ptr + 20)));
ptr += 2;
text.append (String.format ("RANGE0N %04X %04X %04X%n", Utility.getShort (buffer, ptr),
Utility.getShort (buffer, ptr + 10), Utility.getShort (buffer, ptr + 20)));
ptr += 2;
text.append (String.format ("PERCWORS %04X %04X %04X%n", Utility.getShort (buffer, ptr),
Utility.getShort (buffer, ptr + 10), Utility.getShort (buffer, ptr + 20)));
ptr = savePtr;
for (int i = 0; i < 3; i++)
{
String pct = i == 0 ? "75" : i == 1 ? "18.75" : "6.25";
text.append (String.format ("%nEnemy #%d %s%%%n%n", (i + 1), pct));
int minenemy = Utility.getShort (buffer, ptr);
int multwors = Utility.getShort (buffer, ptr + 2);
int worse01 = Utility.getShort (buffer, ptr + 4);
int range0n = Utility.getShort (buffer, ptr + 6);
int percwors = Utility.getShort (buffer, ptr + 8);
ptr += 10;
int max = multwors * worse01;
for (int id = minenemy; id < minenemy + range0n + max; id++)
{
if (id == minenemy + range0n)
text.append ("\n");
MonsterV1 monster = monsters == null ? null : monsters.get (id);
text.append (String.format ("%3d %-16s %n", id, monster));
}
}
}
// ---------------------------------------------------------------------------------//
@Override
public BufferedImage getImage ()
// ---------------------------------------------------------------------------------//
{
Dimension cellSize = new Dimension (22, 22);
image = new BufferedImage (20 * cellSize.width + 1, 20 * cellSize.height + 1,
BufferedImage.TYPE_USHORT_555_RGB);
Graphics2D g = image.createGraphics ();
g.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (int row = 0; row < 20; row++)
for (int column = 0; column < 20; column++)
{
MazeCell cell = getLocation (row, column);
int x = column * cellSize.width;
int y = image.getHeight () - (row + 1) * cellSize.height - 1;
cell.draw (g, x, y);
}
return image;
}
// ---------------------------------------------------------------------------------//
public void setMessages (List<MessageV1> messages)
// ---------------------------------------------------------------------------------//
{
this.messages = messages;
}
// ---------------------------------------------------------------------------------//
public void setMonsters (List<MonsterV1> monsters)
// ---------------------------------------------------------------------------------//
{
this.monsters = monsters;
}
// ---------------------------------------------------------------------------------//
public void setItems (List<ItemV1> items)
// ---------------------------------------------------------------------------------//
{
this.items = items;
}
// ---------------------------------------------------------------------------------//
public MazeCell getLocation (int row, int column)
// ---------------------------------------------------------------------------------//
{
MazeAddress address = new MazeAddress (level, row, column);
MazeCell cell = new MazeCell (address);
// doors and walls
int BYTES_PER_COL = 6; // 20 / 4 = 5 bytes + 1 wasted
int CELLS_PER_BYTE = 4; // 2 bits each
int BITS_PER_ROW = 2;
int offset = column * BYTES_PER_COL + row / CELLS_PER_BYTE;
int value = buffer[offset] & 0xFF;
value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW; // shift 0, 2, 4, 6 bits
cell.westWall = ((value & 1) == 1); // use rightmost bit
value >>>= 1; // shift 1 bit
cell.westDoor = ((value & 1) == 1); // use rightmost bit
value = buffer[offset + 120] & 0xFF;
value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW;
cell.southWall = ((value & 1) == 1);
value >>>= 1;
cell.southDoor = ((value & 1) == 1);
value = buffer[offset + 240] & 0xFF;
value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW;
cell.eastWall = ((value & 1) == 1);
value >>>= 1;
cell.eastDoor = ((value & 1) == 1);
value = buffer[offset + 360] & 0xFF;
value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW;
cell.northWall = ((value & 1) == 1);
value >>>= 1;
cell.northDoor = ((value & 1) == 1);
// monster table
BYTES_PER_COL = 4; // 3 bytes + 1 wasted
CELLS_PER_BYTE = 8; // 1 bit each
BITS_PER_ROW = 1;
offset = column * BYTES_PER_COL + row / CELLS_PER_BYTE; // 4 bytes/column, 1 bit/row
value = buffer[offset + 480] & 0xFF;
value >>>= row % 8;
cell.monsterLair = ((value & 1) == 1);
// square extra
offset = column * 10 + row / 2; // 10 bytes/column, 4 bits/row
value = buffer[offset + 560] & 0xFF;
int b = (row % 2 == 0) ? value % 16 : value / 16; // 0:F
int c = buffer[760 + b / 2] & 0xFF; // 760:767
int cellFlag = (b % 2 == 0) ? c % 16 : c / 16;
switch (cellFlag)
{
case 0: // normal
break;
case 1: // stairs
cell.stairs = true;
cell.addressTo = getAddress (b);
break;
case 2: // pit
cell.pit = true;
break;
case 3: // chute
cell.chute = true;
cell.addressTo = getAddress (b);
break;
case 4: // spinner
cell.spinner = true;
break;
case 5: // dark
cell.darkness = true;
break;
case 6: // transfer
cell.teleport = true;
cell.addressTo = getAddress (b);
break;
case 7: // ouchy
cell.unknown = cellFlag;
break;
case 8: // buttonz
cell.elevator = true;
MazeAddress elevatorAddress = getAddress (b);
cell.elevatorTo = elevatorAddress.row;
cell.elevatorFrom = elevatorAddress.column;
break;
case 9: // rock/water
cell.rock = true;
break;
case 10: // fizzle
cell.spellsBlocked = true;
break;
case 11: // screen message
MazeAddress messageAddress = getAddress (b);
MessageV1 m = getMessage (messageAddress.row);
if (m != null)
cell.message = m;
cell.messageType = messageAddress.column;
switch (cell.messageType)
{
case 2: // obtain Item
if (items != null)
cell.itemObtained = items.get (messageAddress.level);
break;
case 4:
int itemID = messageAddress.level;
if (itemID <= 100)
{
cell.monsterID = itemID;
cell.monsters = monsters;
}
else
{
int val = (itemID - 64536) * -1;
// this gives Index error: 20410, Size 104 in Wizardry_III/legacy2.dsk
if (items != null && val < items.size ())
cell.itemObtained = items.get (val); // check this
if (cell.itemObtained == null)
System.out.printf ("Item %d (%d) not found on level %d%n", val, value, level);
}
break;
case 5: // requires Item
if (items != null)
cell.itemRequired = items.get (messageAddress.level);
break;
}
break;
case 12: // encounter
MazeAddress monsterAddress = getAddress (b);
cell.monsterID = monsterAddress.column;
cell.monsters = monsters;
break;
default:
System.out.println ("Unknown extra: " + cellFlag);
cell.unknown = cellFlag;
break;
}
return cell;
}
// ---------------------------------------------------------------------------------//
private MazeAddress getAddress (int b) // 0:F
// ---------------------------------------------------------------------------------//
{
// converts AUX1, AUX2, AUX3 into a MazeAddress (level, row, column)
b *= 2;
return new MazeAddress (Utility.getShort (buffer, 768 + b), Utility.getShort (buffer, 800 + b),
Utility.getShort (buffer, 832 + b));
}
// ---------------------------------------------------------------------------------//
private MessageV1 getMessage (int messageNo)
// ---------------------------------------------------------------------------------//
{
if (messages == null)
return null;
for (MessageV1 m : messages)
if (m.match (messageNo))
return m;
return null;
}
// ---------------------------------------------------------------------------------//
private MonsterV1 getMonster (int monsterNo)
// ---------------------------------------------------------------------------------//
{
if (monsters == null)
return null;
for (MonsterV1 m : monsters)
if (m.match (monsterNo))
return m;
return null;
}
// ---------------------------------------------------------------------------------//
public int getRows ()
// ---------------------------------------------------------------------------------//
{
return 20;
}
// ---------------------------------------------------------------------------------//
public int getColumns ()
// ---------------------------------------------------------------------------------//
{
return 20;
}
/*
* Pascal code decompiled by Tom Ewers
*
* TWALL = (OPEN, WALL, DOOR, HIDEDOOR);
TSQUARE = (NORMAL, STAIRS, PIT, CHUTE, SPINNER, DARK, TRANSFER,
OUCHY, BUTTONZ, ROCKWATE, FIZZLE, SCNMSG, ENCOUNTE);
TMAZE = RECORD
W : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL;
S : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL;
E : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL;
N : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL;
FIGHTS : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF 0..1;
SQREXTRA : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF 0..15;
SQRETYPE : PACKED ARRAY[ 0..15] OF TSQUARE;
AUX0 : PACKED ARRAY[ 0..15] OF INTEGER;
AUX1 : PACKED ARRAY[ 0..15] OF INTEGER;
AUX2 : PACKED ARRAY[ 0..15] OF INTEGER;
ENMYCALC : PACKED ARRAY[ 1..3] OF RECORD
MINENEMY : INTEGER;
MULTWORS : INTEGER;
WORSE01 : INTEGER;
RANGE0N : INTEGER;
PERCWORS : INTEGER;
END;
END;
*/
}