00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 package com.realtime.crossfire.jxclient.map;
00023
00024 import com.realtime.crossfire.jxclient.animations.Animation;
00025 import com.realtime.crossfire.jxclient.animations.Animations;
00026 import com.realtime.crossfire.jxclient.faces.Face;
00027 import com.realtime.crossfire.jxclient.faces.FacesManager;
00028 import com.realtime.crossfire.jxclient.faces.FacesManagerListener;
00029 import com.realtime.crossfire.jxclient.guistate.GuiStateManager;
00030 import com.realtime.crossfire.jxclient.server.crossfire.CrossfireTickListener;
00031 import com.realtime.crossfire.jxclient.server.crossfire.CrossfireUpdateMapListener;
00032 import com.realtime.crossfire.jxclient.util.EventListenerList2;
00033 import java.util.Collection;
00034 import java.util.HashSet;
00035 import java.util.Set;
00036 import org.jetbrains.annotations.NotNull;
00037 import org.jetbrains.annotations.Nullable;
00038
00049 public class MapUpdaterState implements CrossfireTickListener, CrossfireUpdateMapListener, FacesManagerListener {
00050
00054 @NotNull
00055 private final Object sync = new Object();
00056
00060 @NotNull
00061 private final FacesManager facesManager;
00062
00066 @NotNull
00067 private final Animations animations;
00068
00072 private int mapWidth = 0;
00073
00077 private int mapHeight = 0;
00078
00082 @NotNull
00083 private final CfMap map = new CfMap();
00084
00088 @NotNull
00089 private final EventListenerList2<MapListener> mapListeners = new EventListenerList2<MapListener>(MapListener.class);
00090
00094 @NotNull
00095 private final EventListenerList2<NewmapListener> newmapListeners = new EventListenerList2<NewmapListener>(NewmapListener.class);
00096
00100 @NotNull
00101 private final EventListenerList2<MapScrollListener> mapScrollListeners = new EventListenerList2<MapScrollListener>(MapScrollListener.class);
00102
00106 @NotNull
00107 private final EventListenerList2<MapSizeListener> mapSizeListeners = new EventListenerList2<MapSizeListener>(MapSizeListener.class);
00108
00112 @NotNull
00113 private final CfMapAnimations visibleAnimations = new CfMapAnimations();
00114
00118 @NotNull
00119 private final Collection<Location> outOfViewMultiFaces = new HashSet<Location>();
00120
00127 public MapUpdaterState(@NotNull final FacesManager facesManager, @Nullable final GuiStateManager guiStateManager) {
00128 this.facesManager = facesManager;
00129 animations = new Animations(guiStateManager);
00130 }
00131
00136 public void addCrossfireMapListener(@NotNull final MapListener listener) {
00137 mapListeners.add(listener);
00138 }
00139
00144 public void removeCrossfireMapListener(@NotNull final MapListener listener) {
00145 mapListeners.remove(listener);
00146 }
00147
00152 public void addCrossfireNewmapListener(@NotNull final NewmapListener listener) {
00153 newmapListeners.add(listener);
00154 }
00155
00160 public void removeCrossfireNewmapListener(@NotNull final NewmapListener listener) {
00161 newmapListeners.remove(listener);
00162 }
00163
00168 public void addCrossfireMapScrollListener(@NotNull final MapScrollListener listener) {
00169 mapScrollListeners.add(listener);
00170 }
00171
00176 public void removeCrossfireMapScrollListener(@NotNull final MapScrollListener listener) {
00177 mapScrollListeners.remove(listener);
00178 }
00179
00184 public void addMapSizeListener(@NotNull final MapSizeListener listener) {
00185 mapSizeListeners.add(listener);
00186 }
00187
00192 public void removeMapSizeListener(@NotNull final MapSizeListener listener) {
00193 mapSizeListeners.remove(listener);
00194 }
00195
00199 public void reset() {
00200 synchronized (sync) {
00201 newMap(mapWidth, mapHeight);
00202 }
00203 }
00204
00208 @NotNull
00209 @Override
00210 public Object mapBegin() {
00211 return sync;
00212 }
00213
00217 @Override
00218 public void mapClear(final int x, final int y) {
00219 assert Thread.holdsLock(sync);
00220 visibleAnimations.remove(x, y);
00221 outOfViewMultiFaces.clear();
00222
00223 synchronized (map) {
00224 map.clearSquare(x, y);
00225 }
00226 }
00227
00231 @Override
00232 public void mapFace(@NotNull final Location location, final int faceNum) {
00233 mapFace(location, faceNum, true);
00234 }
00235
00242 public void mapFace(@NotNull final Location location, final int faceNum, final boolean clearAnimation) {
00243 assert Thread.holdsLock(sync);
00244 if (clearAnimation) {
00245 visibleAnimations.remove(location);
00246 }
00247 final Face face = facesManager.getFace2(faceNum);
00248 final int x = location.getX();
00249 final int y = location.getY();
00250 if (x >= mapWidth || y >= mapHeight) {
00251 if (face == null) {
00252 outOfViewMultiFaces.remove(location);
00253 } else if (face.getTileWidth() > 1 || face.getTileHeight() > 1) {
00254 outOfViewMultiFaces.add(location);
00255 }
00256 }
00257
00258 synchronized (map) {
00259 map.setFace(x, y, location.getLayer(), face);
00260 }
00261 }
00262
00266 @Override
00267 public void mapAnimation(@NotNull final Location location, final int animationNum, final int animationType) {
00268 assert Thread.holdsLock(sync);
00269 final Animation animation = animations.get(animationNum);
00270 if (animation == null) {
00271 System.err.println("unknown animation id "+animationNum+", ignoring");
00272 return;
00273 }
00274
00275
00276 synchronized (map) {
00277 map.setFace(location.getX(), location.getY(), location.getLayer(), null);
00278 }
00279 visibleAnimations.add(this, location, animation, animationType);
00280 }
00281
00285 @Override
00286 public void mapAnimationSpeed(@NotNull final Location location, final int animationSpeed) {
00287 assert Thread.holdsLock(sync);
00288 visibleAnimations.updateSpeed(this, location, animationSpeed);
00289 }
00290
00294 @Override
00295 public void mapSmooth(@NotNull final Location location, final int smooth) {
00296 assert Thread.holdsLock(sync);
00297
00298 synchronized (map) {
00299 map.setSmooth(location.getX(), location.getY(), location.getLayer(), smooth);
00300 }
00301 }
00302
00306 @Override
00307 public void mapDarkness(final int x, final int y, final int darkness) {
00308 assert Thread.holdsLock(sync);
00309
00310 synchronized (map) {
00311 map.setDarkness(x, y, darkness);
00312 }
00313 }
00314
00318 @Override
00319 public void magicMap(final int x, final int y, final byte[][] data) {
00320 assert Thread.holdsLock(sync);
00321
00322 synchronized (map) {
00323 map.setMagicMap(x, y, data);
00324 }
00325 }
00326
00330 @Override
00331 public void mapEnd() {
00332 mapEnd(true);
00333 }
00334
00341 public void mapEnd(final boolean alwaysProcess) {
00342 assert Thread.holdsLock(sync);
00343
00344 synchronized (map) {
00345 final Set<CfMapSquare> squares = map.getDirtyMapSquares();
00346 if (!alwaysProcess && squares.isEmpty()) {
00347 return;
00348 }
00349
00350 for (final MapListener listener : mapListeners.getListeners()) {
00351 listener.mapChanged(map, squares);
00352 }
00353 }
00354 }
00355
00359 @Override
00360 public void mapScroll(final int dx, final int dy) {
00361 assert Thread.holdsLock(sync);
00362
00363 synchronized (map) {
00364 for (final Location location : outOfViewMultiFaces) {
00365 visibleAnimations.remove(location);
00366 map.setFace(location.getX(), location.getY(), location.getLayer(), null);
00367 }
00368 outOfViewMultiFaces.clear();
00369
00370 if (map.processMapScroll(dx, dy, mapWidth, mapHeight)) {
00371 visibleAnimations.clear();
00372 } else {
00373 visibleAnimations.scroll(dx, dy);
00374 }
00375 }
00376
00377 for (final MapScrollListener mapscrollListener : mapScrollListeners.getListeners()) {
00378 mapscrollListener.mapScrolled(dx, dy);
00379 }
00380 }
00381
00385 @Override
00386 public void faceUpdated(@NotNull final Face face) {
00387 synchronized (mapBegin()) {
00388
00389 synchronized (map) {
00390 map.updateFace(face.getFaceNum(), mapWidth, mapHeight);
00391 }
00392 mapEnd();
00393 }
00394 }
00395
00399 @Override
00400 public void newMap(final int mapWidth, final int mapHeight) {
00401 synchronized (sync) {
00402 final boolean changed = this.mapWidth != mapWidth || this.mapHeight != mapHeight;
00403 this.mapWidth = mapWidth;
00404 this.mapHeight = mapHeight;
00405
00406 synchronized (map) {
00407 map.reset(mapWidth, mapHeight);
00408 }
00409
00410 visibleAnimations.setMapSize(mapWidth, mapHeight);
00411
00412 if (changed) {
00413 for (final MapSizeListener listener : mapSizeListeners.getListeners()) {
00414 listener.mapSizeChanged(mapWidth, mapHeight);
00415 }
00416 }
00417
00418 for (final NewmapListener listener : newmapListeners.getListeners()) {
00419 listener.commandNewmapReceived();
00420 }
00421 }
00422 }
00423
00428 @NotNull
00429 public CfMap getMap() {
00430 return map;
00431 }
00432
00437 public int getMapWidth() {
00438 return mapWidth;
00439 }
00440
00445 public int getMapHeight() {
00446 return mapHeight;
00447 }
00448
00452 @Override
00453 public void addAnimation(final int animation, final int flags, @NotNull final int[] faces) {
00454 animations.addAnimation(animation, flags, faces);
00455 }
00456
00460 @Override
00461 public void tick(final int tickNo) {
00462 synchronized (sync) {
00463 visibleAnimations.tick(this, tickNo);
00464 }
00465 }
00466
00467 }