📄 mappy.java
字号:
//
// ______________________________
// --------------------------
// * Mappy Java Version *
// __________________________
// ------------------------------
//
// (c) Slimer 2001
//
import java.awt.image.MemoryImageSource;
import java.awt.Rectangle;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Color;
import java.awt.Shape;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.File;
// These are required for the Log class.
import java.io.StringWriter;
import java.io.PrintWriter;
import java.util.Date;
import java.text.DateFormat;
//------------------------------------------------------------------------------
/**
* Mappy Playback Library for Java Release 6<br>
* <br>
* Based on MappyJV Release1 14/07/99 (C)1999 <a href="mailto:rburrows@bigfoot.com">Robin
* Burrows</a>. <br>
* <br>
* This class allows the playback of <a href="http://www.tilemap.co.uk">
* Mappy</a> <code>.FMP</code> files. Current limitations are:
* <ul>
* <li>Pillars are not supported.
* <li>Animation Blocks are not fully supported.
* </ul>
* After constructing a Mappy class, you must load a Map into it before it can
* be used. If any of your image blocks are transparent then be sure to set the
* transparent colour key via <code>setTransparentColour()</code> before you
* call a <code>loadMap()</code> method. It is also important that you set
* the screen area that the Map is to be drawn into (via <code>setScreenDimensions()
* </code>) before any drawing functions are called.<br>
* <br>
* As a Map can have up to 8 layers, the Mappy class introduces the concept of
* a current layer. This current layer is used implicity (i.e. without you
* specifing)in most of the map operations (e.g. <code>drawForeground()</code>),
* therefore you must set the current Map layer before calling any Map layer
* methods. <br>
* <br>
* Despite the source for the Mappy Playback being quite large I have kept it
* in a single source (<code>.java</code>) file to ease portability. For the
* same reason, <code>Mappy</code> has been moved out of it's default package
* of <code>com.alienfactory.gaming</code>. Future releases may well begin to
* spread accross several classes (over 2000 lines, I mean, come on!). <br>
* <br>
* See the readme.txt file for more info<br><br>
* @version 1.23 (6 December 2002)
* @author <a href="mailto:slimer@alienfactory.co.uk">Slimer</a> of <a href="http://www.alienfactory.co.uk">Alien Factory</a>.
* @see #setTransparentColour(int)
* @see #loadMap(File, Component)
* @see #setScreenDimensions(Rectangle)
* @see Block
**/
//------------------------------------------------------------------------------
public class Mappy {
private static Log log = new Log();
private static boolean m_blnLogEnabled = true;
private static final int MIN_NO_OF_LAYERS = 0;
private static final int MAX_NO_OF_LAYERS = 7;
private int m_intMapWidth, m_intMapHeight;
private int m_intBlockWidth, m_intBlockHeight;
private int m_intBlockByteSize;
private int m_intNoOfBlocks;
private int m_intNoOfImages;
private int m_intColourDepth; // the no. of bits that represent a colour
private String m_strMapDescription = ""; // this is whatever's found in the ATHR chunk
// FMP1.0 data:
private int m_intMapType; // type of map (always 0 for FMP0.5)
private int m_intBlockGapX; // gap between block starts horizontally
private int m_intBlockGapY; // gap between block starts vertically
private int m_intBlockStaggerX; // offset for odd rows horizontally
private int m_intBlockStaggerY; // offset for odd rows vertically
private int m_intClickMask; // index of image to detect solid areas
// end FMP1.0 data
private byte m_bytColourMap[]; // Colour map 0xRRGGBB
private Block m_clsMapBlockStrPt[]; // Block structure object array
private AnimStructure m_clsMapAniStrPt[]; // Anim structure object array
private int m_intTransparentColour; // Transparency: Index for 8bit, 0xRRGGBB for others
private Image m_imgOpaqueBlocks[]; // The Images for opaque blocks
private Image m_imgTransparentBlocks[]; // The Images for transparent blocks
private short m_shtTheMap[][][]; // The Entire Map Matrix!
private boolean m_blnMapLoaded; // indicates wether or not a map has been loaded
private int m_intScreenWidth = 640;
private int m_intScreenHeight = 480;
private int m_intScreenOffsetX = 0;
private int m_intScreenOffsetY = 0;
private int m_intX[], m_intY[];
private int m_intMaxLayerNo = 0;
private int m_intCurrentMapLayer = 0;
//______________________________________________________________________________
//------------------------------------------------------------------------------
// Constructors
//______________________________________________________________________________
//------------------------------------------------------------------------------
/**
* Constructs a new Mappy Playback Library. <br>
**/
public Mappy() {
m_intX = new int[MAX_NO_OF_LAYERS + 1];
m_intY = new int[MAX_NO_OF_LAYERS + 1];
m_intTransparentColour = 0;
m_blnMapLoaded = false;
}
//______________________________________________________________________________
//------------------------------------------------------------------------------
// Private Methods : General
//______________________________________________________________________________
//------------------------------------------------------------------------------
/**
* Reads 4 bytes from the InputStream and converts them into a String. <br>
**/
private String readID(InputStream stream) throws IOException {
byte bytID[];
String strID;
// default return value
strID = "";
// read the ID into a byte array
bytID = new byte[4];
readStream(stream, bytID, 0, 4);
// convert byte array into a String
strID = new String(bytID);
// return ID
return strID;
}
/**
* Reads 4 bytes from the InputStream and converts them into an int. <br>
**/
private int readChunkLength(InputStream stream) throws IOException {
int intIndex;
byte bytLength[];
int intChunkLength;
// default return value
intChunkLength = 0;
// read the chunk length
bytLength = new byte[4];
readStream(stream, bytLength, 0, 4);
// convert to int
intChunkLength = convertToInt(bytLength, 0, 4);
// return ID
return intChunkLength;
}
/**
* Converts "intSize" bytes from the given array (at position "intOffset")
* into an int. <br>
**/
private int convertToInt(byte bytArray[], int intOffset, int intSize) {
int intIndex;
int intValue;
// default return value
intValue = 0;
// loop for all the required bytes
for (intIndex = 0; intIndex < intSize; intIndex++) {
// add the next few bits to the number
intValue += ((int) bytArray[intOffset + intIndex]) & 0xFF;
// if we're going around again, shift the value up
if (intIndex < (intSize - 1)) {
intValue <<= 8;
}
}
// return int value
return intValue;
}
/**
* Updated for MappyJV R5 by RB
* Hopefully this will be faster, I have left the old code there commented out<br><br>
* R6: reads more than 1 byte at a time<br><br>
*
* This method is to replace the standard InputStream.read(byte[], int, int).
* Reason being, in Java1.1.x, it friggin don't work! Believe me, I've spent
* hours on it! You keep getting misc and spurlious data creeping in - VERY
* annoying, I can tell you! <br>
* <br>
* Yet another reason to jump to Java1.4! Yeah... <br>
**/
private void readStream(InputStream stream, byte bytArray[], int intOffset, int intSize) throws IOException {
int intIndex = 0;
while (intIndex < intSize) {
intIndex += stream.read(bytArray, intIndex+intOffset, intSize-intIndex);
}
/*
byte bytTemp[] = new byte[1];
int intIndex;
for (intIndex = 0; intIndex < intSize; intIndex++) {
stream.read(bytTemp, 0, 1);
bytArray[intOffset++] = bytTemp[0];
}
*/
}
//______________________________________________________________________________
//------------------------------------------------------------------------------
// Private Methods : Chunk Reading
//______________________________________________________________________________
//------------------------------------------------------------------------------
/**
* Loads the ATHR chunk from the InputStream. <br>
* <br>
* ATHR: Up to 4 ASCII strings of author information. <br>
**/
private void loadATHR(InputStream stream, int intChunkLength) throws IOException {
byte bytATHR[];
String strDesc[];
int intStrStart, intStrEnd;
int intStrIndex;
String strFullDesc = "";
log.info("Found Chunk - ATHR, reading...");
bytATHR = new byte[intChunkLength];
readStream(stream, bytATHR, 0, bytATHR.length);
strDesc = new String[4];
intStrStart = intStrEnd = 0;
for (intStrIndex = 0; intStrIndex < strDesc.length; intStrIndex++) {
// find the end of the line
while (bytATHR[intStrEnd] != 0) intStrEnd++;
// convert the byte array to a string
strDesc[intStrIndex] = new String(bytATHR, intStrStart, intStrEnd - intStrStart);
// concat all into the one string
strFullDesc += strDesc[intStrIndex];
if ((intStrIndex < (strDesc.length-1)) && (!strDesc[intStrIndex].equals(""))) {
strFullDesc += "\n";
}
// start again
intStrEnd++;
intStrStart = intStrEnd;
}
log.info(" Author : " + strDesc[0]);
log.info(" Desc : " + strDesc[1]);
log.info(" : " + strDesc[2]);
log.info(" : " + strDesc[3]);
// save for future use
m_strMapDescription = strFullDesc;
}
/**
* Loads the MPHD chunk from the InputStream. <br>
* <br>
* MPHD: Map header, see struct in mappy.c <br>
**/
private boolean loadMPHD(InputStream stream, int intChunkLength) throws IOException {
byte bytMPHD[];
boolean blnLSB;
byte bytTemp;
int intIndex;
log.info("Found Chunk - MPHD, reading...");
log.info(" loading map header.");
// default LSB
blnLSB = false;
bytMPHD = new byte[intChunkLength];
readStream(stream, bytMPHD, 0, bytMPHD.length);
if (bytMPHD[0] > 1) {
log.error("Map is not supported (version higher than 1.0)");
}
if (bytMPHD[2] == 1) {
blnLSB = true;
// looks like we're swapping around the lsb and msb
for (intIndex = 4; intIndex < bytMPHD.length; intIndex += 2) {
if (intIndex < 24 || intIndex >= 28) {
bytTemp = bytMPHD[intIndex];
bytMPHD[intIndex] = bytMPHD[intIndex + 1];
bytMPHD[intIndex + 1] = bytTemp;
}
}
}
m_intMapType = bytMPHD[3];
if (m_intMapType > 3) {
log.error("Map is not supported (unknown type)");
}
// load Map Header
m_intMapWidth = (((int) bytMPHD[ 4])&0xFF)<<8 | ((int) bytMPHD[ 5])&0xFF;
m_intMapHeight = (((int) bytMPHD[ 6])&0xFF)<<8 | ((int) bytMPHD[ 7])&0xFF;
m_intBlockWidth = (((int) bytMPHD[12])&0xFF)<<8 | ((int) bytMPHD[13])&0xFF;
m_intBlockHeight = (((int) bytMPHD[14])&0xFF)<<8 | ((int) bytMPHD[15])&0xFF;
m_intColourDepth = (((int) bytMPHD[16])&0xFF)<<8 | ((int) bytMPHD[17])&0xFF;
m_intBlockByteSize = (((int) bytMPHD[18])&0xFF)<<8 | ((int) bytMPHD[19])&0xFF;
m_intNoOfBlocks = (((int) bytMPHD[20])&0xFF)<<8 | ((int) bytMPHD[21])&0xFF;
m_intNoOfImages = (((int) bytMPHD[22])&0xFF)<<8 | ((int) bytMPHD[23])&0xFF;
if (bytMPHD.length > 24) {
if (m_intColourDepth == 8) m_intTransparentColour = ((int) bytMPHD[24])&0xFF;
else m_intTransparentColour = (((int) bytMPHD[25])&0xFF)<<16 | (((int) bytMPHD[26])&0xFF)<<8 | ((int) bytMPHD[27])&0xFF;
}
if (bytMPHD.length > 28) {
m_intBlockGapX = (((int) bytMPHD[28])&0xFF)<<8 | ((int) bytMPHD[29])&0xFF;
m_intBlockGapY = (((int) bytMPHD[30])&0xFF)<<8 | ((int) bytMPHD[31])&0xFF;
m_intBlockStaggerX = (((int) bytMPHD[32])&0xFF)<<8 | ((int) bytMPHD[33])&0xFF;
m_intBlockStaggerY = (((int) bytMPHD[34])&0xFF)<<8 | ((int) bytMPHD[35])&0xFF;
} else {
m_intBlockGapX = m_intBlockWidth;
m_intBlockGapY = m_intBlockHeight;
m_intBlockStaggerX = 0;
m_intBlockStaggerY = 0;
}
if (bytMPHD.length > 36) {
m_intClickMask = (((int) bytMPHD[36])&0xFF)<<8 | ((int) bytMPHD[37])&0xFF;
} else {
m_intClickMask = 0;
}
log.info(" FMP version : " + Integer.toString((int) bytMPHD[0]) + "." + Integer.toString((int) bytMPHD[1]));
log.info(" MapWidth : " + Integer.toString(m_intMapWidth));
log.info(" MapHeight : " + Integer.toString(m_intMapHeight));
log.info(" BlockWidth : " + Integer.toString(m_intBlockWidth));
log.info(" BlockHeight : " + Integer.toString(m_intBlockHeight));
log.info(" ColourDepth : " + Integer.toString(m_intColourDepth));
log.info(" BlockByteSize : " + Integer.toString(m_intBlockByteSize));
log.info(" NoOfBlocks : " + Integer.toString(m_intNoOfBlocks));
log.info(" NoOfImages : " + Integer.toString(m_intNoOfImages));
// return LSB
return blnLSB;
}
/**
* Loads the CMAP chunk from the InputStream. <br>
* <br>
* CMAP: Colour palette for 8bit maps, for however many colours are needed. <br>
**/
private void loadCMAP(InputStream stream, int intChunkLength) throws IOException {
log.info("Found Chunk - CMAP, reading...");
// read in the colour map
log.info(" loading " + Integer.toString(intChunkLength / 3) + " entries.");
m_bytColourMap = new byte[intChunkLength];
readStream(stream, m_bytColourMap, 0, m_bytColourMap.length);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -