tilemap.java

来自「mywork是rcp开发的很好的例子」· Java 代码 · 共 546 行 · 第 1/2 页

JAVA
546
字号
/************************************************************
 *
 * Copyright (c) 2003 Chemi. All rights reserved.
 * 
 * This program and the accompanying materials
 * are made available under the terms of the MIT License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/mit-license.html
 *
 ************************************************************/

package es.org.chemi.games.sokoban.ui;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.zip.ZipFile;

import net.sf.pim.game.util.DiamondMap;
import net.sf.pim.game.util.DiamondMap.IsoDirection;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import es.org.chemi.games.sokoban.SokobanPlugin;
import es.org.chemi.games.sokoban.actions.PauseAction;
import es.org.chemi.games.sokoban.events.TileMapKeyListener;
import es.org.chemi.games.sokoban.events.TileMapMouseListener;
import es.org.chemi.games.sokoban.util.Constants;
import es.org.chemi.games.sokoban.util.Preferences;
import es.org.chemi.games.util.Counter;
import es.org.chemi.games.util.Timer;

public class TileMap extends Composite
{
	private ArrayList lines = new ArrayList();
	private ArrayList holes = new ArrayList();
	private ArrayList oldHoles = null;
	Tile[][] tiles = null;
	private Tile[][] oldTiles = null;
	private Tile boyTile = null;
	private Tile oldBoyTile = null;
	
	private Preferences preferences = null;
	private HashMap counters = null;
	private Action undoAction = null;
	private PauseAction pauseAction = null;
	
	int width = 0;
	int height = 0;
	int deltaWidth = 0;
	int deltaHeight = 0;
	
	Image doubleBuffering = null;
	boolean isNewLevel = true;
	boolean isDemo =false;
	private TileMapKeyListener keyListener = null;
	private TileMapMouseListener mouseListner =null;
	
	public TileMap(Composite parent, int style, Preferences preferences, HashMap counters)
	{
		super(parent,style);
		this.preferences = preferences;
		this.counters = counters;
		
		this.keyListener=new TileMapKeyListener(this);
		this.mouseListner=new TileMapMouseListener(this);
		
		this.addPaintListener(new PaintListener()
		{
			public void paintControl(PaintEvent e)
			{
				SokobanPlugin.trace(this.getClass().getName(),"Refresh process of game field started."); //$NON-NLS-1$
				
				// Doublebuffering.
				GC tempGC = new GC(doubleBuffering);
				tempGC.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
				
				if(isNewLevel){
					tempGC.fillRectangle(0,0,doubleBuffering.getBounds().width,doubleBuffering.getBounds().height);
					Image back = SokobanPlugin.getResourceManager().getImage(Constants.IMAGE_BACKGROUND);
					tempGC.drawImage(back,0,0,back.getBounds().width,back.getBounds().height,0, 0,doubleBuffering.getBounds().width,doubleBuffering.getBounds().height);
				}
				
				for(int row=0; row<tiles.length; row++)
				{
					for(int column=0; column<tiles[0].length; column++)
					{
						if(tiles[row][column].isModified())
						{
							if(isNewLevel){
								tiles[row][column].repaint(tempGC,deltaWidth,deltaHeight);
							}else{
								//考虑局部刷新,还要绘制周围受影响的Tile,设置剪切矩形
								Point ptCenter=DiamondMap.tilePlotter(new Point(column,row),Constants.TILE_SIZE, Constants.TILE_SIZE/2,deltaWidth, deltaHeight);
								//实际贴图图片高度目前没有固定,有48*48/48*32/48*38/48*24,取最大值减1
								int rectWidth=Constants.TILE_SIZE-2,rectHeight=Constants.TILE_SIZE-2;
								Rectangle rect=new Rectangle(ptCenter.x+1,ptCenter.y+1,rectWidth,rectHeight);
								tempGC.setClipping(rect);
								for(IsoDirection dir : IsoDirection.values()){
										Point pt=DiamondMap.tileWalker(new Point(column,row),dir);
										//防止越界,且WEST/EAST肯定不相交
										if(pt.x >=0 && pt.y >= 0 && pt.x < width && pt.y < height &&  dir != IsoDirection.WEST && dir != IsoDirection.EAST)
											tiles[pt.y][pt.x].repaint(tempGC,deltaWidth,deltaHeight);
								}
								tempGC.setClipping((Rectangle)null);
							}
							tiles[row][column].setModified(false);
						}
					}
				}
				if(isNewLevel){
					isNewLevel = false;
				}
				tempGC.dispose();
				e.gc.drawImage(doubleBuffering,0,0);
				SokobanPlugin.trace(this.getClass().getName(),"Refresh process of game field finished."); //$NON-NLS-1$
			}
		});
		
		this.addControlListener(new ControlAdapter()
		{
			public void controlResized(ControlEvent e)
			{
				resetDelta();				
				//Reset the double buffering image.
				if(doubleBuffering != null)
					doubleBuffering.dispose();
				if(getSize().x != 0 && getSize().y != 0) // This line is needed to support Eclipse R2.0.x products
					doubleBuffering = new Image(null,getSize().x,getSize().y);
				//绘制背景
				GC tempGC = new GC(doubleBuffering);
				Image back = SokobanPlugin.getResourceManager().getImage(Constants.IMAGE_BACKGROUND);
				tempGC.drawImage(back,0,0,back.getBounds().width,back.getBounds().height,0, 0,doubleBuffering.getBounds().width,doubleBuffering.getBounds().height);
				tempGC.dispose();

				//Force tile repaint.
				if(tiles != null)
				{
					for(int row=0; row<height; row++)
					{
						for(int column=0; column<width; column++)
							tiles[row][column].setModified(true);
					}
				}
			}
		});
	}
	
	public void loadMap() throws IOException
	{
		// Empty information from previous map.
		lines.clear();
		holes.clear();
		width=0;
		height=0;
		// Read the map file.
		ZipFile zf=new ZipFile(new File(FileLocator.getBundleFile(SokobanPlugin.getDefault().getBundle()),"levels/"+preferences.getMode()+".map"));
		BufferedReader br = new BufferedReader(new InputStreamReader(zf.getInputStream(zf.getEntry(""+preferences.getLevel(preferences.getMode())+".sok"))));

		while(true)
		{
			String line = br.readLine();
			if(line == null)
			{
				br.close();
				zf.close();
				break;
			}
			
			if(!line.trim().isEmpty() && !line.startsWith("#Level") && !line.startsWith("#-") && line.compareTo(Constants.EMPTY_LINE) != 0) //$NON-NLS-1$
			{
				lines.add(line);
				width = Math.max(width,line.length());
			}
		}
		width += 2;
		height = lines.size()+2;
		tiles = new Tile[height][width];
		
		parseLines();
		resetCounters();
    	resetDelta();
    	fixMap();
    	resetWall();
	}
	/**
	 * 1.修正map中有些块没有初始化情况
	 * 2.将墙包围之外的floor置为false
	 * 3.去掉多余的墙
	 * 4.修订拐角刷新的bug
	 * 5.增加一圈外地板
	 * 为了简洁,每个处理都进行一次循环
	 */
	private void fixMap() {
		//1修正map中有些块没有初始化情况
		for(int row=0; row<tiles.length; row++)
		{
			for(int column=0; column<tiles[0].length; column++)
			{
				if(tiles[row][column] == null){
					tiles[row][column]=new Tile(row,column);
					tiles[row][column].setFloor(true);
				}
			}
		}
		//2.1将墙包围之外的floor置为false
		for(int row=0; row<tiles.length; row++)
		{
			int cs=-1,ce=0;
			for(int column=0; column<tiles[0].length; column++)
			{
				if(tiles[row][column].isWall()){
					if(cs == -1) cs=column;
					if(column > ce) ce=column;
				}
			}
			for(int i=0;i<cs;i++)tiles[row][i].setFloor(false);
			for(int i=ce;i<tiles[0].length;i++)tiles[row][i].setFloor(false);
		}
		//2.2颠倒行列
		for(int column=0; column<tiles[0].length; column++)
		{
			int cs=-1,ce=0;
			for(int row=0; row<tiles.length; row++)
			{
				if(tiles[row][column].isWall()){
					if(cs == -1) cs=row;
					if(row > ce) ce=row;
				}
			}
			for(int i=0;i<cs;i++)tiles[i][column].setFloor(false);
			for(int i=ce;i<tiles.length;i++)tiles[i][column].setFloor(false);
		}	
		//3.去掉多余的墙
		for(int row=0; row<tiles.length; row++)
		{
			for(int column=0; column<tiles[0].length; column++){
				//多余的墙特征,其东南、东北、西南、西北均为墙或为空
				Tile tile = tiles[row][column];
				if(tile.isWall() && 
						(neighborTile(tile, IsoDirection.SOUTH_EAST).isWall() ||neighborTile(tile, IsoDirection.SOUTH_EAST).isBlank())&&
						(neighborTile(tile, IsoDirection.SOUTH_WEST).isWall() ||neighborTile(tile, IsoDirection.SOUTH_WEST).isBlank())&&
						(neighborTile(tile, IsoDirection.NORTH_EAST).isWall() ||neighborTile(tile, IsoDirection.NORTH_EAST).isBlank())&&
						(neighborTile(tile, IsoDirection.NORTH_WEST).isWall() ||neighborTile(tile, IsoDirection.NORTH_WEST).isBlank()))
					tile.setWall(false);	
			}
		}		
		//4.修订拐角刷新的bug,顺便美化拐角
		for(int row=0; row<tiles.length; row++)
		{
			for(int column=0; column<tiles[0].length; column++){
				//该块空白,其西南、东南为墙,其南为地板或小人
				Tile tile = tiles[row][column];
				if(tile.isBlank()){
					//预防bug
					if( neighborTile(tile, IsoDirection.SOUTH_WEST).isWall() && neighborTile(tile, IsoDirection.SOUTH_EAST).isWall() && (neighborTile(tile, IsoDirection.SOUTH).isFloor() || neighborTile(tile, IsoDirection.SOUTH).equals(boyTile)))

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?