package com.javaxyq.widget; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EventObject; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import javax.swing.event.EventListenerList; import com.javaxyq.core.Application; import com.javaxyq.core.ApplicationHelper; import com.javaxyq.core.SpriteFactory; import com.javaxyq.data.CharacterUtils; import com.javaxyq.data.WeaponItem; import com.javaxyq.event.EventDispatcher; import com.javaxyq.event.EventException; import com.javaxyq.event.EventTarget; import com.javaxyq.event.PlayerEvent; import com.javaxyq.event.PlayerListener; import com.javaxyq.model.PlayerVO; import com.javaxyq.search.Searcher; import com.javaxyq.ui.FloatPanel; import com.javaxyq.util.Cheat; import com.javaxyq.util.MP3Player; import com.javaxyq.util.UIUtils; import com.javaxyq.util.WASDecoder; /** * ��Ϸ���� * * @author ����ΰ * @history 2008-5-14 ����ΰ ��������������ߵĴ��� * 2013-12-31 wpaul modify */ public class Player extends AbstractWidget implements EventTarget { private static final long serialVersionUID = 4030203990139411828L; public static final String STATE_STAND = "stand"; public static final String STATE_WALK = "walk"; private Object MOVE_LOCK = new Object(); private Object UPDATE_LOCK = new Object(); private String id; /** ��ɫ������(����ң��/����/����) */ private String character; /** ��ʷð�ݶԻ� */ private List<FloatPanel> chatPanels; private Sprite person; private Sprite weapon; private Sprite shadow; /** ·������ */ private Queue<Point> path = new ConcurrentLinkedQueue<Point>(); private String name; private String state = STATE_STAND; private int x; private int y; private int direction; private Color nameForeground = UIUtils.COLOR_NAME; private Color nameBackground = UIUtils.COLOR_NAME_BACKGROUND; private Color highlightColor = UIUtils.COLOR_NAME_HIGHLIGHT; private List<String> chatHistory = new ArrayList<String>(); private Font nameFont; private boolean visible = true; private boolean moving = false; private boolean stepping = false; private EventListenerList listenerList = new EventListenerList(); /** ����X���� */ private int sceneX; /** ����Y���� */ private int sceneY; /** ��ǰ���ƶ���[x,y] */ private Point nextStep; /** ������ǰ�����ƶ� */ private boolean movingOn = false; /** Ⱦɫ���� */ private String profile; private int[] profileData; private int[] colorations = null;// new int[3]; /** * �������� */ private PlayerVO data; /** * ״̬Ч������ */ private Map<String, Animation> stateEffects = new HashMap<String, Animation>(); /** * ����Ч������ */ private Animation onceEffect = null; private boolean directionMoving; private boolean isHover; private Searcher searcher; private int delay; private WeaponItem weaponItem; public Player(String id, String name, String character) { this.id = id; this.name = name; this.character = character; shadow = SpriteFactory.loadShadow(); nameFont = UIUtils.TEXT_NAME_FONT; chatPanels = new ArrayList<FloatPanel>(); } public String getId() { return id; } public int getHeight() { return this.person.getHeight(); } public int getWidth() { return this.person.getWidth(); } public boolean isHover() { return isHover; } public void setHover(boolean isHover) { this.isHover = isHover; } /** * ȡ����һ�����ƶ��� * * @return */ private Point popPath() { // System.out.println("path count:" + path.size()); if (this.path != null && !this.path.isEmpty()) { Point step = this.path.poll(); while (step != null && step.x == this.sceneX && step.y == this.sceneY) { step = this.path.poll(); } return step; } return null; } /** * ����·���IJ�����������ƶ����� * * @param step * @return */ private int calculateStepDirection(Point step) { System.out.println("dx,dy is:"+step.x+","+step.y); System.out.println("scenex,y is:"+sceneX+","+sceneY); int dx = step.x - this.sceneX; int dy = step.y - this.sceneY; double r = Math.sqrt(dx*dx+dy*dy); double cos=dx/r; int angle=(int) Math.floor(Math.acos(cos)*180/Math.PI); if(dy>0){ angle=360-angle; } int dir = 0; System.out.println("angle is:"+angle); if(angle>337 || angle<=22){ dir = Sprite.DIR_RIGHT; }else if(angle>22 && angle<=67){ dir = Sprite.DIR_DOWN_RIGHT; }else if(angle>67 && angle<=112){ dir = Sprite.DIR_DOWN; }else if(angle>112 && angle<=157){ dir = Sprite.DIR_DOWN_LEFT; }else if(angle>157 && angle<=202){ dir = Sprite.DIR_LEFT; }else if(angle>202 && angle<=247){ dir = Sprite.DIR_UP_LEFT; }else if(angle>247 && angle<=292){ dir = Sprite.DIR_UP; }else if(angle>292 && angle<=337){ dir = Sprite.DIR_UP_RIGHT; } /*if (dx < 0) { if (dy < 0) { dir = Sprite.DIR_DOWN_LEFT; } else if (dy > 0) { dir = Sprite.DIR_UP_LEFT; } else { dir = Sprite.DIR_LEFT; } } else if (dx > 0) { if (dy < 0) { dir = Sprite.DIR_DOWN_RIGHT; } else if (dy > 0) { dir = Sprite.DIR_UP_RIGHT; } else { dir = Sprite.DIR_RIGHT; } } else {// x=0 if (dy < 0) { dir = Sprite.DIR_DOWN; } else if (dy > 0) { dir = Sprite.DIR_UP; } else { // no move dir = -1; } }*/ return dir; } public void changeDirection(Point mouse) { // FIXME ����ת�� int direction = computeDirection(getLocation(), mouse); setDirection(direction); } public boolean contains(int x, int y) { boolean b = person.contains(x, y) || shadow.contains(x, y); if (weapon != null && !b) { b = weapon.contains(x, y); } return b; } public Point getLocation() { return new Point(x, y); } public String getName() { return name; } /*synchronized*/ public void say(String chatText) { if(Cheat.process(chatText)) { return ; } this.chatHistory.add(chatText); this.chatPanels.add(new FloatPanel(chatText)); if (this.chatPanels.size() > 3) { this.chatPanels.remove(0); } System.out.println(name + " ˵: " + chatText); } public List<String> getChatHistory() { return chatHistory; } public void move() { // TODO synchronized(MOVE_LOCK) { this.prepareStep(); } } /** * ��ij�����ƶ�һ�� * * @param direction */ public void stepTo(int direction) { this.clearPath(); int dx = 0; int dy = 0; switch (direction) { case Sprite.DIR_LEFT: dx = -1; break; case Sprite.DIR_UP: dy = 1; break; case Sprite.DIR_RIGHT: dx = 1; break; case Sprite.DIR_DOWN: dy = -1; break; case Sprite.DIR_DOWN_LEFT: dx = -1; dy = -1; break; case Sprite.DIR_UP_LEFT: dx = -1; dy = 1; break; case Sprite.DIR_UP_RIGHT: dx = -1; dy = 1; break; case Sprite.DIR_DOWN_RIGHT: dx = 1; dy = -1; break; default: break; } Point next = new Point(this.sceneX + dx, this.sceneY + dy); this.addStep(next); // System.out.printf("step to:%s, (%s,%s)\n", direction, next.x, // next.y); this.prepareStep(); } public void moveBy(int dx, int dy) { this.x += dx; this.y += dy; } public void setDirection(int direction) { if (this.direction != direction) { this.direction = direction; System.out.printf("[debug]player.direction=%s\n", this.direction); //delay = 5; if(person!=null) { person.setDirection(direction); person.resetFrames(); } if (weapon != null) { weapon.setDirection(direction); weapon.resetFrames(); } } else { amendDirection(); } } public void amendDirection() { if(person != null) { person.setDirection(direction); int index = person.getCurrAnimation().getIndex(); if (weapon != null) { weapon.setDirection(direction); weapon.getCurrAnimation().setIndex(index); } } } public void setName(String name) { this.name = name; } public void setState(String state) { if(state == null) { state = STATE_STAND; } if (this.state != state) { System.out.println("[debug] setState: "+state); this.state = state; this.person = createPerson(state); this.weapon = createWeapon(state); this.person.setDirection(this.direction); this.person.resetFrames(); if (this.weapon != null) { this.weapon.setDirection(this.direction); this.weapon.resetFrames(); } } } private Sprite createPerson(String state) { int charNo = Integer.parseInt(this.character); if(weaponItem != null){ if(!CharacterUtils.isFirstWeapon(this.character, weaponItem.getType())) { if(!CharacterUtils.isNormalState(state)) { charNo +=12; } } } String filename = String.format("/shape/char/%04d/%s.tcp", charNo, state); Sprite sprite = SpriteFactory.loadSprite(filename); return sprite; } private Sprite createWeapon(String state) { if(weaponItem != null){ int charNo = Integer.parseInt(this.character); int resNo = Integer.parseInt(weaponItem.getResNo()); if(!CharacterUtils.isFirstWeapon(this.character, weaponItem.getType())) { if(CharacterUtils.isNormalState(state)) { resNo +=50; }else { charNo +=12; } } String filename = String.format("/shape/char/%04d/%02d/%s.tcp", charNo, resNo, state); Sprite sprite = SpriteFactory.loadSprite(filename); return sprite; } return null; } public String getState() { return state; } public void stop(boolean force) { synchronized(MOVE_LOCK) { if (force) { stopAction(); } else { this.movingOn = false; } this.directionMoving = false; // this.setState(STATE_STAND); // System.out.println("stop"); } } private void stopAction() { synchronized(MOVE_LOCK) { this.moving = false; this.movingOn = false; this.setState(STATE_STAND); // System.out.println("stop action!"); } } public void update(long elapsedTime) { shadow.update(elapsedTime); person.update(elapsedTime); if (weapon != null) { weapon.update(elapsedTime); } // effect Collection<Animation> stateEffs = stateEffects.values(); for (Animation effect : stateEffs) { effect.update(elapsedTime); } if (this.onceEffect != null) { this.onceEffect.update(elapsedTime); if (this.onceEffect.getRepeat() == 0) { // ������ϣ��Ƴ����� this.onceEffect = null; } } } public /*synchronized*/ void updateMovement(long elapsedTime) { // ����״̬�ı�player��sprite��character���ܸı䣩 this.setState(this.isMoving() ? STATE_WALK: this.state); if (this.isMoving()) { // ����ƶ����,����STEP_OVER��Ϣ if (this.isStepOver()) { fireEvent(new PlayerEvent(this, PlayerEvent.STEP_OVER)); prepareStep(); } else {// �����ƶ��� if(this.delay > 0) { this.delay --; return; } Point d = this.calculateIncrement(elapsedTime); if (d.x != 0 || d.y != 0) { x += d.x; y += d.y; PlayerEvent evt = new PlayerEvent(this, PlayerEvent.MOVE); evt.setAttribute(PlayerEvent.MOVE_INCREMENT, d); fireEvent(evt); // System.out.printf("pos:(%s,%s)\tmove->:(%s,%s)\n", x, y, // d.x, d.y); } } } } private Point calculateIncrement(long elapsedTime) { int dx = 0, dy = 0; // �����������Ե����ƶ� if (searcher.pass(this.nextStep.x, this.nextStep.y)) { // ���������Ŀ���Ļ��Ƚ� double radian = Math.atan(1.0 * (nextStep.y - sceneY) / (nextStep.x - sceneX)); // �����ƶ��� int distance = (int) (Application.NORMAL_SPEED * elapsedTime); dx = (int) (distance * Math.cos(radian)); dy = (int) (distance * Math.sin(radian)); // �����ƶ����� if (nextStep.x > sceneX) { dx = Math.abs(dx); } else { dx = -Math.abs(dx); } if (nextStep.y > sceneY) { dy = -Math.abs(dy); } else { dy = Math.abs(dy); } } else if (this.directionMoving) {// �����ϰ���ʱ����ס������ƶ� // TODO �����ƶ��ķ��� // switch (this.direction) { // case Sprite.DIRECTION_BOTTOM: // // break; // // default: // break; // } } else if (!this.directionMoving) {// �����ϰ���ʱ���ɿ������(û�м����ƶ�) stopAction(); } return new Point(dx, dy); } /** * �Ƿ����һ�����ƶ�<br> * ���ˮƽ���ߴ�ֱ�����ƶ����ڵ��ڲ���,����Ϊ�ƶ���� * * @return */ private boolean isStepOver() { return getSceneLocation().equals(nextStep); } /** * �����¼� * * @param event */ public void handleEvent(PlayerEvent event) { final PlayerListener[] listeners = listenerList.getListeners(PlayerListener.class); switch (event.getId()) { case PlayerEvent.STEP_OVER: for (PlayerListener listener : listeners) { listener.stepOver(this); } break; case PlayerEvent.WALK: for (PlayerListener listener : listeners) { listener.walk(event); } break; case PlayerEvent.MOVE: for (PlayerListener listener : listeners) { listener.move(this, (Point) event.getAttribute(PlayerEvent.MOVE_INCREMENT)); } break; case PlayerEvent.CLICK: for (PlayerListener listener : listeners) { listener.click(event); } break; case PlayerEvent.TALK: for (PlayerListener listener : listeners) { listener.talk(event); } break; default: break; } } public void removePlayerListener(PlayerListener l) { listenerList.remove(PlayerListener.class, l); } /** * �Ƿ񵽴�Ŀ�ĵ� * * @return */ public boolean isArrived() { return false; } public Color getNameBackground() { return nameBackground; } public void setNameBackground(Color textBackground) { this.nameBackground = textBackground; } public Color getNameForeground() { return nameForeground; } public void setNameForeground(Color textForeground) { this.nameForeground = textForeground; } public void setLocation(int x, int y) { this.x = x; this.y = y; } public boolean isMoving() { return moving; } public boolean isDirectionMoving() { return directionMoving; } public void setDirectionMoving(boolean directionMoving) { this.directionMoving = directionMoving; } public void addPlayerListener(PlayerListener l) { listenerList.add(PlayerListener.class, l); } public void clearPath() { this.path.clear(); } public void addStep(Point p) { this.path.add(p); } public void setPath(Collection<Point> path) { this.path.clear(); this.path.addAll(path); if (path == null || path.isEmpty()) { System.out.println("path is empty."); } else { // System.out.println("new path:"); //for (Point p : path) { //System.out.printf("(%s,%s)\n", p.x, p.y); //} // System.out.println(); } } public /*synchronized*/ void draw(Graphics g, int x, int y) { shadow.draw(g, x, y); person.draw(g, x, y); if (weapon != null) weapon.draw(g, x, y); // draw name g.setFont(nameFont); int textY = y + 30; int textX = x - g.getFontMetrics().stringWidth(name) / 2; Graphics2D g2d = (Graphics2D) g.create(); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setColor(nameBackground); g2d.drawString(name, textX + 1, textY + 1); g2d.setColor(isHover ? highlightColor : nameForeground); g2d.drawString(name, textX, textY); g2d.dispose(); // ����ð�ݶԻ����� int chatY = y - person.getRefPixelY() - 10; for (int i = chatPanels.size() - 1; i >= 0; i--) { FloatPanel chatPanel = chatPanels.get(i); if (shouldDisplay(chatPanel)) { int chatX = x - chatPanel.getWidth() / 2; chatY -= chatPanel.getHeight() + 2; Graphics g2 = g.create(chatX, chatY, chatPanel.getWidth(), chatPanel.getHeight()); chatPanel.paint(g2); g2.dispose(); } else { chatPanels.remove(i); } } // effect Collection<Animation> stateEffs = stateEffects.values(); for (Animation effect : stateEffs) { effect.draw(g, x, y); } if (this.onceEffect != null) onceEffect.draw(g, x, y); if(ApplicationHelper.getApplication().isDebug()) { g.drawLine(x-10, y, x+10, y); g.drawLine(x, y-10, x, y+10); } } private boolean shouldDisplay(FloatPanel chatPanel) { return System.currentTimeMillis() - chatPanel.getCreateTime() < Application.CHAT_REMAIND_TIME; } @Override public void dispose() { // TODO Player: dispose } public Sprite getWeapon() { return weapon; } public void setWeapon(Sprite weapon) { this.weapon = weapon; } public int getX() { return x; } public int getY() { return y; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } public int getDirection() { return direction; } public boolean isStepping() { return stepping; } /** * ׼����һ�� */ private void prepareStep() { synchronized(MOVE_LOCK) { this.nextStep = this.popPath(); // ·���Ѿ�Ϊ��,ֹͣ�ƶ� if (this.nextStep == null) { if (this.movingOn) { this.stepTo(direction); } else { this.stopAction(); } } this.stepAction(); } } private void stepAction() { if (this.nextStep != null) { this.moving = true; // ������һ���ķ��� int dir = calculateStepDirection(this.nextStep); if (dir != -1) { /*switch(this.direction) { case Sprite.DIR_DOWN_RIGHT: if(dir == Sprite.DIR_RIGHT || dir == Sprite.DIR_DOWN) { return; } break; case Sprite.DIR_DOWN_LEFT: if(dir == Sprite.DIR_LEFT || dir == Sprite.DIR_DOWN) { return; } break; case Sprite.DIR_UP_LEFT: if(dir == Sprite.DIR_LEFT || dir == Sprite.DIR_UP) { return; } break; case Sprite.DIR_UP_RIGHT: if(dir == Sprite.DIR_RIGHT || dir == Sprite.DIR_UP) { return; } break; case Sprite.DIR_DOWN: if(dir == Sprite.DIR_DOWN_LEFT || dir == Sprite.DIR_DOWN_RIGHT) { return; } break; case Sprite.DIR_LEFT: if(dir == Sprite.DIR_DOWN_LEFT || dir == Sprite.DIR_UP_LEFT) { return; } break; case Sprite.DIR_UP: if(dir == Sprite.DIR_UP_RIGHT || dir == Sprite.DIR_UP_LEFT) { return; } break; case Sprite.DIR_RIGHT: if(dir == Sprite.DIR_DOWN_RIGHT || dir == Sprite.DIR_UP_RIGHT) { return; } break; }*/ setDirection(dir); } } } public int getSceneX() { return sceneX; } public int getSceneY() { return sceneY; } public Point getSceneLocation() { return new Point(sceneX, sceneY); } public void setSceneLocation(int x, int y) { this.sceneX = x; this.sceneY = y; } public void setSceneLocation(Point p) { setSceneLocation(p.x, p.y); } public void moveOn() { this.movingOn = true; } @Override public String toString() { return "[name=" + this.name + ",x=" + this.x + ",y=" + this.y + ",sceenX=" + this.sceneX + ",sceneY=" + this.sceneY + "]"; } public List<Point> getPath() { Point[] paths = new Point[path.size()]; path.toArray(paths); return Arrays.asList(paths); } public String getCharacter() { return character; } public boolean handleEvent(EventObject evt) throws EventException { if (evt instanceof PlayerEvent) { PlayerEvent playerEvt = (PlayerEvent) evt; handleEvent(playerEvt); } return false; } public void fireEvent(PlayerEvent e) { EventDispatcher.getInstance().dispatchEvent(e); } public Sprite getPerson() { return person; } public Sprite getShadow() { return shadow; } public int[] getColorations() { return colorations; } public void setColorations(int[] colorations, boolean recreate) { this.colorations = colorations; if (recreate) { this.coloring(colorations); } } public void coloring(int[] colorations) { // ���¸ı���ɫ��ľ��� this.person = createPerson(state); this.weapon = createWeapon(state); this.person.setDirection(this.direction); this.person.resetFrames(); if (this.weapon != null) { this.weapon.setDirection(this.direction); this.weapon.resetFrames(); } } public String getProfile() { return profile; } public void setProfile(String profile) { this.profile = profile; } public int getColorationCount(int part) { if (this.profileData == null) { // ������ɫ���� WASDecoder decoder = new WASDecoder(); decoder.loadColorationProfile("shape/char/" + this.character + "/00.pp"); int partCount = decoder.getSectionCount(); this.profileData = new int[partCount]; for (int i = 0; i < partCount; i++) { this.profileData[i] = decoder.getSchemeCount(i); } } return this.profileData[part]; } /** * ���β���Ч������ * * @param name * @param sound TODO */ public void playEffect(String name, boolean sound) { Animation s = SpriteFactory.loadAnimation("/magic/" + name + ".tcp"); s.setRepeat(1); this.onceEffect = s; if(sound) { try { MP3Player.play("sound/magic/" + name + ".mp3"); } catch (Exception e) { e.printStackTrace(); } } } /** * ���״̬Ч�� * * @param name */ public void addStateEffect(String name) { Animation s = SpriteFactory.loadAnimation("/magic/" + name + ".tcp"); this.stateEffects.put(name, s); } /** * ȡ��״̬Ч�� * * @param name */ public void removeStateEffect(String name) { this.stateEffects.remove(name); } /** * ����ij�������Ķ��� * * @param state */ public void playOnce(String state) { this.setState(state); this.person.setRepeat(1); if (this.weapon != null) { this.weapon.setRepeat(1); } MP3Player.play("sound/char/" + this.character + "/" + state + ".mp3"); } /** * �ȴ���ǰ�������� */ public void waitFor() { this.person.getCurrAnimation().waitFor(); } /** * �ȴ�Ч���������� * * @param name */ public void waitForEffect(String name) { if (this.onceEffect != null) this.onceEffect.waitFor(); } public int getTop() { return y - this.person.getRefPixelY(); } public int getLeft() { return x - this.person.getRefPixelX(); } public PlayerVO getData() { data.direction = this.direction; data.state = this.state; data.colorations = this.colorations; data.sceneLocation = this.getSceneLocation(); return data; } public void setData(PlayerVO data) { this.data = data; this.setDirection(data.direction); //������ɫ���� this.setColorations(data.colorations, true); this.setState(data.state); this.setSceneLocation(data.getSceneLocation()); } /** * ���ȫ�������� */ public void removeAllListeners() { PlayerListener[] listeners = listenerList.getListeners(PlayerListener.class); for (int i = 0; i < listeners.length; i++) { this.removePlayerListener(listeners[i]); } } @Override public void setAlpha(float alpha) { super.setAlpha(alpha); shadow.setAlpha(alpha); person.setAlpha(alpha); if(this.weapon!=null) { this.weapon.setAlpha(alpha); } } @Override protected void doDraw(Graphics2D g2, int x, int y, int width, int height) { } public Searcher getSearcher() { return searcher; } public void setSearcher(Searcher searcher) { this.searcher = searcher; } /** * ����Ŀ���������ĵ�ĽǶ� * * @param src * @param mouse * @return 8������֮һ */ public static int computeDirection(Point src, Point mouse) { double dy, dx, k; int direction = Sprite.DIR_DOWN_RIGHT; dy = mouse.y - src.y; dx = mouse.x - src.x; if (dx == 0) { return (dy >= 0) ? Sprite.DIR_DOWN : Sprite.DIR_UP; } else if (dy == 0) { return (dx >= 0) ? Sprite.DIR_RIGHT : Sprite.DIR_LEFT; } k = Math.abs(dy / dx); if (k >= k2) { if (dy > 0) direction = Sprite.DIR_DOWN; else direction = Sprite.DIR_UP; } else if (k <= k1) { if (dx > 0) direction = Sprite.DIR_RIGHT; else direction = Sprite.DIR_LEFT; } else if (dy > 0) { if (dx > 0) direction = Sprite.DIR_DOWN_RIGHT; else direction = Sprite.DIR_DOWN_LEFT; } else { if (dx > 0) direction = Sprite.DIR_UP_RIGHT; else direction = Sprite.DIR_UP_LEFT; } return direction; } private static double k1 = Math.tan(Math.PI / 8); private static double k2 = 3 * k1; public int getRefPixelX() { return person.getRefPixelX(); } public int getRefPixelY() { return person.getRefPixelY(); } /** * �������� * @param item */ public void takeupWeapon(WeaponItem item) { if(item == null){ takeoffWeapon(); return; } this.weaponItem = item; this.weapon = createWeapon(state); if (this.weapon != null) { this.weapon.setDirection(this.direction); this.weapon.resetFrames(); this.person.resetFrames(); } } /** * �������� */ public void takeoffWeapon() { this.weaponItem = null; this.weapon = null; } }