package com.javaxyq.core;
import java.awt.AWTKeyStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import com.javaxyq.event.DownloadEvent;
import com.javaxyq.event.DownloadListener;
import com.javaxyq.ui.UIHelper;
import com.javaxyq.util.MP3Player;
import com.javaxyq.util.UIUtils;
import com.javaxyq.widget.Animation;
import com.javaxyq.widget.Cursor;
import com.javaxyq.widget.Player;
//DONE ��Ӷ����IJ��Ŵ�������ʹ�ü��ܶ�����ֻ���ţ��Σ������������Ч��
public class Canvas extends JPanel implements GameCanvas, DownloadListener{
private static final long serialVersionUID = -4190257004358562807L;
private class AnimatorThread extends Thread {
/** ������ʱ�� */
private long duration;
private boolean increased;
/**
* ����ˢ�µļ��
*/
private long interval;
private long passTime;
/**
* @param duration
* @param interval
* @param increased
* true������alpha,false ���Ǽ���alpha
*/
public AnimatorThread(long duration, long interval, boolean increased) {
this.duration = duration;
this.interval = interval;
this.increased = increased;
setName("animator");
}
public void run() {
synchronized (FADE_LOCK) {
while (passTime < duration) {
// System.out.println(this.getId()+" "+this.getName());
synchronized (UPDATE_LOCK) {
passTime += interval;
alpha = (int) (255.0 * passTime / duration);
if (!increased) {
alpha = 255 - alpha;
}
if (alpha < 0) {
alpha = 0;
}
if (alpha > 255) {
alpha = 255;
}
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
Canvas.this.fading = false;
// System.out.println("faded");
}
}
}
private int drawCount = 0;
protected long updateInterval = 15;
private final class DrawThread extends Thread {
{
this.setName("DrawThread");
this.setDaemon(true);
}
public void run() {
while (canvasValid) {
// System.out.println(this.getId()+" "+this.getName());
synchronized (Canvas.UPDATE_LOCK) {
if (isShowing() && isVisible()) {
drawCanvas();
drawCount ++;
}
try {
UPDATE_LOCK.wait(updateInterval );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static final Object FADE_LOCK = new Object();
public static final Object UPDATE_LOCK = new Object();
public static final Object MOVEMENT_LOCK = new Object();
public static final Object TILEMASKIMAGE_LOCK = new Object();
private boolean canvasValid = true;
protected int alpha = 0;
private Thread drawThread = new DrawThread();
private boolean fading;
private Cursor gameCursor;// ���ָ��
private long lastTime;
/**
* ��������
*/
private Animation movingObject = null;
private Point movingOffset = new Point();
private List<Player> npcs;
private Graphics2D offscreenGraphics;
private BufferedImage offscreenImage;
private Player player;// ��ɫ
private int maxWidth;
private int maxHeight;
private int viewportY;
private int viewportX;
public Canvas(int width,int height) {
setIgnoreRepaint(true);
setFocusable(true);
requestFocus(true);
setLayout(null);
setSize(width, height);
setPreferredSize(new Dimension(width,height));
offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_565_RGB);
offscreenGraphics = (Graphics2D) offscreenImage.getGraphics();
setBackground(Color.BLACK);
setForeground(Color.WHITE);
// ��ֹtab���л�����
Set<AWTKeyStroke> keystrokes = new HashSet<AWTKeyStroke>(
getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
keystrokes.remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keystrokes);
npcs = new ArrayList<Player>();
drawThread.start();
}
protected DataManager getDataManager() {
return ApplicationHelper.getApplication().getDataManager();
}
public void addNPC(Player npc) {
npcs.add(npc);
}
/**
* ��(x,y)����һ��NPC
*
* @param npc
* @param x
* @param y
*/
public void addNPC(Player npc, int x, int y) {
npc.setSceneLocation(x, y);
this.addNPC(npc);
}
public void removeNPC(Player npc) {
npcs.remove(npc);
}
protected void clearNPCs() {
this.npcs.clear();
}
/**
* ������Ϸ����
* @param g
* @param elapsedTime
*/
public void draw(Graphics g, long elapsedTime) {
if (g == null) {
return;
}
try {
g.setColor(Color.BLACK);
// npcs
drawNPC(g, elapsedTime);
// update comps on the canvas
drawComponents(g, elapsedTime);
// draw fade
drawFade(g);
drawDebug(g);
drawDownloading(g);
} catch (Exception e) {
System.out.printf("����Canvasʱʧ�ܣ�\n");
e.printStackTrace();
}
}
private void drawFade(Graphics g) {
if(alpha > 0) {
g.setColor(new Color(0, 0, 0, alpha));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
private void drawCanvas() {
//System.out.println("start drawCanvas: "+new java.util.Date());
Graphics g = this.getGraphics();
long currTime = System.currentTimeMillis();
if (lastTime == 0)
lastTime = currTime;
long elapsedTime = currTime - lastTime;
lastTime = System.currentTimeMillis();
if (g != null && offscreenGraphics!=null) {
this.draw(offscreenGraphics, elapsedTime);
// draw to real graphics
g.drawImage(offscreenImage, 0, 0, null);
// g.dispose();
}
// lastTime = System.currentTimeMillis();
if(System.currentTimeMillis()-currTime > 20) {
System.out.println("drawCanvas cost: "+(System.currentTimeMillis()-currTime));
}
}
protected void drawComponents(Graphics g, long elapsedTime) {
Component[] comps = getComponents();
for (int i = comps.length - 1; i >= 0; i--) {// reverse the z-order
Component c = comps[i];
Graphics g2 = g.create(c.getX(), c.getY(), c.getWidth(), c.getHeight());
c.paint(g2);
g2.dispose();
// g.setClip(c.getX(), c.getY(), c.getWidth(), c.getHeight());
// g.translate(c.getX(), c.getY());
// c.paint(g);
// g.translate(-c.getX(), -c.getY());
}
// g.setClip(0, 0, getWidth(), getHeight());
drawTooltips(g);
// update cursor
drawCursor(g, elapsedTime);
}
protected void drawCursor(Graphics g, long elapsedTime) {
Point p = getWindow().getMousePosition();
if (movingObject != null && p != null) {
movingObject.update(elapsedTime);
movingObject.draw(g, p.x + movingOffset.x, p.y + movingOffset.y);
}
if (gameCursor != null) {
if (p != null) {
gameCursor.setLocation(p.x, p.y);
}
gameCursor.update(elapsedTime);
gameCursor.draw(g);
}
}
protected void drawNPC(Graphics g, long elapsedTime) {
//System.out.println(System.currentTimeMillis()+" update NPC: "+elapsedTime);
for (int i = 0; i < npcs.size(); i++) {
Player npc = npcs.get(i);
npc.setHover(isHover(npc));
npc.update(elapsedTime);
Point p = npc.getLocation();
// p = localToView(p);
p.translate(-viewportX, -viewportY);
npc.draw(g, p.x, p.y);
}
}
protected void drawPlayer(Graphics g, long elapsedTime) {
//System.out.println(System.currentTimeMillis()+" update player: "+elapsedTime);
Player player = getPlayer();
if (player != null) {
player.setHover(isHover(player));
// long s1 = System.currentTimeMillis();
player.update(elapsedTime);
Point p = player.getLocation();
p = localToView(p);
player.draw(g, p.x, p.y);
// long s2 = System.currentTimeMillis();
// if(s2-s1>0) {
// System.out.printf("%s update player uses: %sms, elapsedTime: %s \n", s2, s2-s1, elapsedTime);
// }
}
}
private void drawTooltips(Graphics g) {
Component[] comps = getWindow().getLayeredPane().getComponentsInLayer(JLayeredPane.POPUP_LAYER);
for (Component comp : comps) {
if(comp.isShowing()) {
Graphics g1 = g.create(comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight());
try {
comp.paint(g1);
} catch (Exception e) {
//e.printStackTrace();
}
g1.dispose();
}
}
}
public void fadeIn(long t) {
this.fading = true;
long duration = t;
long interval = t / 10;
AnimatorThread thread = new AnimatorThread(duration, interval, false);
thread.start();
}
public void fadeOut(long t) {
this.fading = true;
long duration = t;
long interval = t / 10;
AnimatorThread thread = new AnimatorThread(duration, interval, true);
thread.start();
}
private Component findCompByName(Component parent, String name) {
if (!(parent instanceof Container)) {
return null;
}
Container container = (Container) parent;
Component[] comps = container.getComponents();
for (Component c : comps) {
if (name.equals(c.getName())) {
return c;
}
}
for (Component c : comps) {
Component result = this.findCompByName(c, name);
if (result != null) {
return result;
}
}
return null;
}
protected Component findCompByName(String name) {
return this.findCompByName(this, name);
}
/**
* find npc by name
*
* @param name
* @return
*/
public Player findNpcByName(String name) {
for (int i = 0; i < npcs.size(); i++) {
Player npc = npcs.get(i);
if (npc.getName().equals(name)) {
return npc;
}
}
return null;
}
public Cursor getGameCursor() {
return gameCursor;
}
public List<Player> getNpcs() {
return npcs;
}
public Graphics getOffscreenGraphics() {
return offscreenGraphics;
}
public void removeMovingObject() {
this.movingObject = null;
}
public void setGameCursor(String type) {
Cursor cursor = UIHelper.getCursor(type);
if(cursor!=null) {
this.gameCursor = cursor;
}
}
public void setMovingObject(Animation anim, Point offset) {
this.movingObject = anim;
this.movingOffset = offset;
}
public void waitForFading() {
while (this.fading) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void setViewPosition(int x, int y) {
this.viewportX = x;
this.viewportY = y;
reviseViewport();
}
private void reviseViewport() {
int canvasWidth = getWidth();
int canvasHeight = getHeight();
// int mapWidth = map.getWidth();
// int mapHeight = map.getHeight();
if (viewportX + canvasWidth > maxWidth) {
viewportX = maxWidth - canvasWidth;
} else if (viewportX < 0) {
viewportX = 0;
}
if (viewportY + canvasHeight > maxHeight) {
viewportY = maxHeight - canvasHeight;
} else if (viewportY < 0) {
viewportY = 0;
}
}
protected int getMaxWidth() {
return maxWidth;
}
protected void setMaxWidth(int maxWidth) {
this.maxWidth = maxWidth;
}
protected int getMaxHeight() {
return maxHeight;
}
protected void setMaxHeight(int maxHeight) {
this.maxHeight = maxHeight;
}
protected int getViewportY() {
return viewportY;
}
protected int getViewportX() {
return viewportX;
}
public Point getViewPosition() {
return new Point(viewportX, viewportY);
}
public Player getPlayer() {
return player;
}
protected void setPlayer(Player player) {
this.player = player;
}
/**
*
*/
public void dispose() {
canvasValid = false;
}
private long lastFPSTime;
private int lastDrawCount;
private double fps;
private DownloadEvent downloadEvt;
private long downloadUpdate;
private long downloadMsgDelay = 1000;
protected GameWindow window;
protected void drawDebug(Graphics g) {
if (g != null) {
g.setFont(UIUtils.TEXT_FONT);
double mb = 1024 * 1024;
double maxMem = Runtime.getRuntime().maxMemory() / mb;
double freeMem = Runtime.getRuntime().freeMemory() / mb;
int x = 100, y = 20;
g.setColor(Color.GREEN);
g.drawString(String.format("�ڴ棺%.2f/%.2f MB", freeMem, maxMem), x, y);
long nowtime = System.currentTimeMillis();
if(lastFPSTime !=0 ) {
if(nowtime >= lastFPSTime+1000) {
fps = (drawCount-lastDrawCount)*1000.0/(nowtime-lastFPSTime);
lastFPSTime = nowtime;
lastDrawCount = drawCount;
}
}else {
lastFPSTime = nowtime;
lastDrawCount = drawCount;
}
g.drawString(String.format("FPS: %.2f",fps), x+200, y);
}
}
protected void drawDownloading(Graphics g) {
if(this.downloadEvt!=null && System.currentTimeMillis()-downloadUpdate<downloadMsgDelay) {
String msg = "";
String resourceName = this.downloadEvt.getResource();
int size = this.downloadEvt.getSize();
int received = this.downloadEvt.getReceived();
//if(size==0)size = -1;//��֤������Ϊ0
//int percent = this.downloadEvt.getReceived()*100/size;
switch (this.downloadEvt.getId()) {
case DownloadEvent.DOWNLOAD_UPDATE:
msg = String.format("�������� %s�� ��%.2fKB��������%.2fKB ...",resourceName,size/1024.0, received/1024.0);
break;
case DownloadEvent.DOWNLOAD_STARTED:
msg = String.format("��ʼ���� %s ...", resourceName);
break;
case DownloadEvent.DOWNLOAD_COMPLETED:
//percent = 100;
msg = String.format("������� %s .", resourceName);
break;
case DownloadEvent.DOWNLOAD_INTERRUPTED:
msg = String.format("����ʧ�� %s .", resourceName);
break;
default:
break;
}
// int rw = 400;
// int rh = 30;
// int rx = (getWidth()-rw)/2;
// int ry = (getHeight()-rh)/2;
//��ʾ��Ϣ
int x = 100, y = 40;
g.setColor(Color.GREEN);
g.drawString(msg ,x , y);
//FontMetrics fm = g.getFontMetrics();
//g.drawString(msg ,rx +(rw-fm.stringWidth(msg))/2, ry-10);
//���
//g.setColor(Color.DARK_GRAY);
//g.drawRect(rx, ry, rw, rh);
//����
//g.setColor(Color.GREEN);
//g.fillRect(rx+2, ry+2, (int) ((rw-4)*percent/100.0), rh-4);
}
}
protected String getMusic() {
return null;
}
public void playMusic() {
if(GameMain.isPlayingMusic()) {
final String filename = getMusic();
if (filename != null) {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
MP3Player.loop(filename);
}
});
th.start();
}
}
}
public void stopMusic() {
MP3Player.stopLoop();
}
@Override
public void downloadCompleted(DownloadEvent e) {
this.downloadEvt = e;
this.downloadUpdate = System.currentTimeMillis();
}
@Override
public void downloadInterrupted(DownloadEvent e) {
this.downloadEvt = e;
this.downloadUpdate = System.currentTimeMillis();
}
@Override
public void downloadStarted(DownloadEvent e) {
this.downloadEvt = e;
this.downloadUpdate = System.currentTimeMillis();
}
@Override
public void downloadUpdate(DownloadEvent e) {
this.downloadEvt = e;
this.downloadUpdate = System.currentTimeMillis();
}
@Override
public JComponent getComponent() {
return this;
}
@Override
public GameWindow getWindow() {
return window;
}
@Override
public void setWindow(GameWindow window) {
this.window = window;
}
@Override
public boolean isHover(Player player) {
Point p = getMousePosition();
if (p == null) {
return false;
}
Point vp = localToView(player.getLocation());
boolean hover = player.contains(p.x - vp.x, p.y - vp.y);
return hover;
}
public Point localToView(Point p) {
return new Point(p.x - viewportX, p.y - viewportY);
}
public Point viewToLocal(Point p) {
return new Point(p.x + getViewportX(), p.y + getViewportY());
}
@Override
public Point localToScene(Point p) {
return null;
}
@Override
public Point sceneToLocal(Point p) {
return null;
}
@Override
public Point sceneToView(Point p) {
return null;
}
@Override
public Point viewToScene(Point p) {
return null;
}
@Override
public void setGameCursor(Cursor cursor) {
this.gameCursor = cursor;
}
}