From c34614f09b4087057c41786fa8cb4db85151769e Mon Sep 17 00:00:00 2001 From: Oskar Wiksten Date: Sat, 22 Jun 2013 12:38:49 +0200 Subject: [PATCH] First attempt at dynamic map tiles that change based on quest progress. --- .../AndorsTrail/activity/MainActivity.java | 20 +- .../controller/ItemController.java | 6 +- .../AndorsTrail/controller/MapController.java | 33 +- .../controller/MovementController.java | 4 +- .../controller/WorldMapController.java | 35 ++- .../listeners/LootBagListeners.java | 26 -- ...agListener.java => MapLayoutListener.java} | 4 +- .../listeners/MapLayoutListeners.java | 36 +++ .../AndorsTrail/model/map/LayeredTileMap.java | 38 +-- .../rpg/AndorsTrail/model/map/MapLayer.java | 15 - .../rpg/AndorsTrail/model/map/MapSection.java | 43 +++ .../model/map/ReplaceableMapSection.java | 25 ++ .../model/map/TMXMapFileParser.java | 34 ++- .../model/map/TMXMapTranslator.java | 288 ++++++++++-------- .../gpl/rpg/AndorsTrail/view/MainView.java | 22 +- 15 files changed, 390 insertions(+), 239 deletions(-) delete mode 100644 AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/LootBagListeners.java rename AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/{LootBagListener.java => MapLayoutListener.java} (61%) create mode 100644 AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/MapLayoutListeners.java create mode 100644 AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java create mode 100644 AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/ReplaceableMapSection.java diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java index fcd60d5ee..3b4c086a4 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java @@ -121,6 +121,7 @@ public final class MainActivity extends Activity implements PlayerMovementListen break; case INTENTREQUEST_CONVERSATION: MovementController.refreshMonsterAggressiveness(world.model.currentMap, world.model.player); + controllers.mapController.applyCurrentMapReplacements(getResources()); break; case INTENTREQUEST_SAVEGAME: if (resultCode != Activity.RESULT_OK) break; @@ -138,15 +139,26 @@ public final class MainActivity extends Activity implements PlayerMovementListen final Player player = world.model.player; return Savegames.saveWorld(world, this, slot, getString(R.string.savegame_currenthero_displayinfo, player.getLevel(), player.getTotalExperience(), player.getGold())); } - + + @Override + protected void onStart() { + super.onStart(); + if (!AndorsTrailApplication.getApplicationFromActivity(this).getWorldSetup().isSceneReady) return; + subscribeToModelChanges(); + } + + @Override + protected void onStop() { + super.onStop(); + unsubscribeFromModel(); + } + @Override protected void onPause() { super.onPause(); controllers.gameRoundController.pause(); controllers.movementController.stopMovement(); - unsubscribeFromModel(); - save(Savegames.SLOT_QUICKSAVE); } @@ -155,8 +167,6 @@ public final class MainActivity extends Activity implements PlayerMovementListen super.onResume(); if (!AndorsTrailApplication.getApplicationFromActivity(this).getWorldSetup().isSceneReady) return; - subscribeToModelChanges(); - controllers.gameRoundController.resume(); if (world.model.uiSelections.isInCombat) { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ItemController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ItemController.java index 16a991f41..f700a6bfe 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ItemController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ItemController.java @@ -6,7 +6,6 @@ import java.util.Collection; import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; -import com.gpl.rpg.AndorsTrail.controller.listeners.LootBagListeners; import com.gpl.rpg.AndorsTrail.controller.listeners.QuickSlotListeners; import com.gpl.rpg.AndorsTrail.model.ModelContainer; import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection; @@ -23,8 +22,7 @@ public final class ItemController { private final ControllerContext controllers; private final WorldContext world; - public final QuickSlotListeners quickSlotListeners = new QuickSlotListeners(); - public final LootBagListeners lootBagListeners = new LootBagListeners(); + public final QuickSlotListeners quickSlotListeners = new QuickSlotListeners(); public ItemController(ControllerContext controllers, WorldContext world) { this.controllers = controllers; @@ -213,7 +211,7 @@ public final class ItemController { if (loot.hasItems()) return false; world.model.currentMap.removeGroundLoot(loot); - lootBagListeners.onLootBagRemoved(world.model.currentMap, loot.position); + controllers.mapController.mapLayoutListeners.onLootBagRemoved(world.model.currentMap, loot.position); return true; // The bag was removed. } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java index 3541ff256..79f667811 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java @@ -1,20 +1,23 @@ package com.gpl.rpg.AndorsTrail.controller; +import android.content.res.Resources; import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; +import com.gpl.rpg.AndorsTrail.controller.listeners.MapLayoutListeners; import com.gpl.rpg.AndorsTrail.controller.listeners.WorldEventListeners; import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection; import com.gpl.rpg.AndorsTrail.model.actor.Monster; import com.gpl.rpg.AndorsTrail.model.actor.Player; -import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; -import com.gpl.rpg.AndorsTrail.model.map.MapObject; +import com.gpl.rpg.AndorsTrail.model.map.*; import com.gpl.rpg.AndorsTrail.util.Coord; +import com.gpl.rpg.AndorsTrail.util.CoordRect; public final class MapController { private final ControllerContext controllers; private final WorldContext world; public final WorldEventListeners worldEventListeners = new WorldEventListeners(); + public final MapLayoutListeners mapLayoutListeners = new MapLayoutListeners(); public MapController(ControllerContext controllers, WorldContext world) { this.controllers = controllers; @@ -106,4 +109,30 @@ public final class MapController { m.resetTemporaryData(); } } + + public void applyCurrentMapReplacements(final Resources res) { + if (!applyReplacements(world.model.currentTileMap)) return; + + world.model.currentMap.visited = false; // Force worldmap png to be updated. + WorldMapController.updateWorldMapForCurrentMap(world, res); + world.model.currentMap.visited = true; + + mapLayoutListeners.onMapTilesChanged(world.model.currentMap, world.model.currentTileMap); + } + + private boolean applyReplacements(LayeredTileMap map) { + if (map.replacements == null) return false; + boolean hasUpdated = false; + for(ReplaceableMapSection replacement : map.replacements) { + if (replacement.isApplied) continue; + if (!satisfiesCondition(replacement)) continue; + map.applyReplacement(replacement); + hasUpdated = true; + } + return hasUpdated; + } + + public boolean satisfiesCondition(ReplaceableMapSection replacement) { + return world.model.player.hasExactQuestProgress(replacement.requireQuestStage); + } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java index 4532bd2c7..afd05e734 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java @@ -99,6 +99,8 @@ public final class MovementController implements TimedMessageTask.Callback { controllers.monsterSpawnController.spawnAll(newMap, model.currentTileMap); } } + controllers.mapController.applyCurrentMapReplacements(res); + WorldMapController.updateWorldMapForCurrentMap(world, res); newMap.visited = true; moveBlockedActors(newMap, model.currentTileMap); refreshMonsterAggressiveness(newMap, model.player); @@ -294,8 +296,6 @@ public final class MovementController implements TimedMessageTask.Callback { world.model.currentTileMap = mapTiles; world.tileManager.currentMapTiles = cachedTiles; world.tileManager.cacheAdjacentMaps(res, world, nextMap); - - WorldMapController.updateWorldMap(world, nextMap, mapTiles, cachedTiles, res); } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java index d64db9b75..163b8bfa5 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java @@ -38,14 +38,22 @@ public final class WorldMapController { private static final int WORLDMAP_SCREENSHOT_TILESIZE = 8; public static final int WORLDMAP_DISPLAY_TILESIZE = WORLDMAP_SCREENSHOT_TILESIZE; - - public static void updateWorldMap(final WorldContext world, final PredefinedMap map, final LayeredTileMap mapTiles, final TileCollection cachedTiles, final Resources res) { - + + public static void updateWorldMapForCurrentMap(final WorldContext world, final Resources res) { + updateWorldMap(world, world.model.currentMap, world.model.currentTileMap, world.tileManager.currentMapTiles, res); + } + + public static void updateWorldMap( + final WorldContext world, + final PredefinedMap map, + final LayeredTileMap mapTiles, + final TileCollection cachedTiles, + final Resources res) { final String worldMapSegmentName = world.maps.getWorldMapSegmentNameForMap(map.name); if (worldMapSegmentName == null) return; - + if (!shouldUpdateWorldMap(map, worldMapSegmentName)) return; - + (new AsyncTask() { @Override protected Void doInBackground(Void... arg0) { @@ -53,10 +61,13 @@ public final class WorldMapController { try { updateCachedBitmap(map, renderer); updateWorldMapSegment(res, world, worldMapSegmentName); + if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { + L.log("WorldMapController: Updated worldmap segment " + worldMapSegmentName + " for map " + map.name); + } } catch (IOException e) { L.log("Error creating worldmap file for map " + map.name + " : " + e.toString()); } - return null; + return null; } }).execute(); } @@ -114,15 +125,15 @@ public final class WorldMapController { canvas.scale(scale, scale); synchronized (cachedTiles) { - drawMapLayer(canvas, mapTiles.layers[LayeredTileMap.LAYER_GROUND]); - tryDrawMapLayer(canvas, LayeredTileMap.LAYER_OBJECTS); - tryDrawMapLayer(canvas, LayeredTileMap.LAYER_ABOVE); + drawMapLayer(canvas, mapTiles.currentLayout.layerGround); + tryDrawMapLayer(canvas, mapTiles.currentLayout.layerObjects); + tryDrawMapLayer(canvas, mapTiles.currentLayout.layerAbove); } return image; } - - private void tryDrawMapLayer(Canvas canvas, final int layerIndex) { - if (mapTiles.layers.length > layerIndex) drawMapLayer(canvas, mapTiles.layers[layerIndex]); + + private void tryDrawMapLayer(Canvas canvas, final MapLayer layer) { + if (layer != null) drawMapLayer(canvas, layer); } private void drawMapLayer(Canvas canvas, final MapLayer layer) { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/LootBagListeners.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/LootBagListeners.java deleted file mode 100644 index 440908eac..000000000 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/LootBagListeners.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gpl.rpg.AndorsTrail.controller.listeners; - -import com.gpl.rpg.AndorsTrail.util.ListOfListeners; -import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; -import com.gpl.rpg.AndorsTrail.util.Coord; - -public final class LootBagListeners extends ListOfListeners implements LootBagListener { - - private final Function2 onLootBagCreated = new Function2() { - @Override public void call(LootBagListener listener, PredefinedMap map, Coord p) { listener.onLootBagCreated(map, p); } - }; - - private final Function2 onLootBagRemoved = new Function2() { - @Override public void call(LootBagListener listener, PredefinedMap map, Coord p) { listener.onLootBagRemoved(map, p); } - }; - - @Override - public void onLootBagCreated(PredefinedMap map, Coord p) { - callAllListeners(this.onLootBagCreated, map, p); - } - - @Override - public void onLootBagRemoved(PredefinedMap map, Coord p) { - callAllListeners(this.onLootBagRemoved, map, p); - } -} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/LootBagListener.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/MapLayoutListener.java similarity index 61% rename from AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/LootBagListener.java rename to AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/MapLayoutListener.java index 816c9129e..897e6eb9a 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/LootBagListener.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/MapLayoutListener.java @@ -1,9 +1,11 @@ package com.gpl.rpg.AndorsTrail.controller.listeners; +import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap; import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; import com.gpl.rpg.AndorsTrail.util.Coord; -public interface LootBagListener { +public interface MapLayoutListener { void onLootBagCreated(PredefinedMap map, Coord p); void onLootBagRemoved(PredefinedMap map, Coord p); + void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap); } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/MapLayoutListeners.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/MapLayoutListeners.java new file mode 100644 index 000000000..946c36d05 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/listeners/MapLayoutListeners.java @@ -0,0 +1,36 @@ +package com.gpl.rpg.AndorsTrail.controller.listeners; + +import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap; +import com.gpl.rpg.AndorsTrail.util.ListOfListeners; +import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; +import com.gpl.rpg.AndorsTrail.util.Coord; + +public final class MapLayoutListeners extends ListOfListeners implements MapLayoutListener { + + private final Function2 onLootBagCreated = new Function2() { + @Override public void call(MapLayoutListener listener, PredefinedMap map, Coord p) { listener.onLootBagCreated(map, p); } + }; + + private final Function2 onLootBagRemoved = new Function2() { + @Override public void call(MapLayoutListener listener, PredefinedMap map, Coord p) { listener.onLootBagRemoved(map, p); } + }; + + private final Function2 onMapTilesChanged = new Function2() { + @Override public void call(MapLayoutListener listener, PredefinedMap map, LayeredTileMap tileMap) { listener.onMapTilesChanged(map, tileMap); } + }; + + @Override + public void onLootBagCreated(PredefinedMap map, Coord p) { + callAllListeners(this.onLootBagCreated, map, p); + } + + @Override + public void onLootBagRemoved(PredefinedMap map, Coord p) { + callAllListeners(this.onLootBagRemoved, map, p); + } + + @Override + public void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap) { + callAllListeners(this.onMapTilesChanged, map, tileMap); + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java index 851826ca3..658c5afe8 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java @@ -11,46 +11,36 @@ import com.gpl.rpg.AndorsTrail.util.CoordRect; import com.gpl.rpg.AndorsTrail.util.Size; public final class LayeredTileMap { - public static final int LAYER_GROUND = 0; - public static final int LAYER_OBJECTS = 1; - public static final int LAYER_ABOVE = 2; - private static final ColorFilter colorFilterBlack20 = createGrayScaleColorFilter(0.2f); private static final ColorFilter colorFilterBlack40 = createGrayScaleColorFilter(0.4f); private static final ColorFilter colorFilterBlack60 = createGrayScaleColorFilter(0.6f); private static final ColorFilter colorFilterBlack80 = createGrayScaleColorFilter(0.8f); private final Size size; - public final MapLayer[] layers; - public final Collection usedTileIDs; - private final boolean[][] isWalkable; + public final MapSection currentLayout; + public final ReplaceableMapSection[] replacements; public final String colorFilter; - + public final Collection usedTileIDs; public LayeredTileMap( Size size, - MapLayer[] layers, - boolean[][] isWalkable, - Collection usedTileIDs, - String colorFilter) { - assert(size.width > 0); - assert(size.height > 0); - assert(layers.length == 3); - assert(isWalkable.length == size.width); - assert(isWalkable[0].length == size.height); + MapSection layout, + ReplaceableMapSection[] replacements, + String colorFilter, + Collection usedTileIDs) { this.size = size; - this.layers = layers; - this.usedTileIDs = usedTileIDs; - this.isWalkable = isWalkable; + this.currentLayout = layout; + this.replacements = replacements; this.colorFilter = colorFilter; + this.usedTileIDs = usedTileIDs; } public final boolean isWalkable(final Coord p) { if (isOutside(p.x, p.y)) return false; - return isWalkable[p.x][p.y]; + return currentLayout.isWalkable[p.x][p.y]; } public final boolean isWalkable(final int x, final int y) { if (isOutside(x, y)) return false; - return isWalkable[x][y]; + return currentLayout.isWalkable[x][y]; } public final boolean isWalkable(final CoordRect p) { for (int y = 0; y < p.size.height; ++y) { @@ -98,4 +88,8 @@ public final class LayeredTileMap { 0.00f, 0.00f, 0.00f, 1.0f, 0.0f }); } + + public void applyReplacement(ReplaceableMapSection replacement) { + replacement.apply(currentLayout); + } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapLayer.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapLayer.java index 906ae4dc3..0d2dbfa98 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapLayer.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapLayer.java @@ -4,27 +4,12 @@ import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.util.Size; public final class MapLayer { - private final Size size; public final int[][] tiles; public MapLayer(Size size) { - this.size = size; tiles = new int[size.width][size.height]; } public void setTile(int type, int x, int y) { tiles[x][y] = type; } - public void setTile(int type, Coord p) { - setTile(type, p.x, p.y); - } - public final boolean isOutside(int x, int y) { - if (x < 0) return true; - if (y < 0) return true; - if (x >= size.width) return true; - if (y >= size.height) return true; - return false; - } - public final boolean isOutside(Coord p) { - return isOutside(p.x, p.y); - } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java new file mode 100644 index 000000000..77600cd56 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java @@ -0,0 +1,43 @@ +package com.gpl.rpg.AndorsTrail.model.map; + +import com.gpl.rpg.AndorsTrail.util.CoordRect; + +public final class MapSection { + public final MapLayer layerGround; + public final MapLayer layerObjects; + public final MapLayer layerAbove; + public final boolean[][] isWalkable; + + public MapSection( + MapLayer layerGround, + MapLayer layerObjects, + MapLayer layerAbove, + boolean[][] isWalkable) { + this.layerGround = layerGround; + this.layerObjects = layerObjects; + this.layerAbove = layerAbove; + this.isWalkable = isWalkable; + } + + public void replaceLayerContentsWith(final MapSection replaceLayersWith, final CoordRect replacementArea) { + replaceTileLayerSection(layerGround, replaceLayersWith.layerGround, replacementArea); + replaceTileLayerSection(layerObjects, replaceLayersWith.layerObjects, replacementArea); + replaceTileLayerSection(layerAbove, replaceLayersWith.layerAbove, replacementArea); + if (replaceLayersWith.isWalkable != null) { + final int dy = replacementArea.topLeft.y; + final int height = replacementArea.size.height; + for (int sx = 0, dx = replacementArea.topLeft.x; sx < replacementArea.size.width; ++sx, ++dx) { + System.arraycopy(replaceLayersWith.isWalkable[sx], 0, isWalkable[dx], dy, height); + } + } + } + + private static void replaceTileLayerSection(MapLayer dest, MapLayer src, CoordRect area) { + if (src == null) return; + final int dy = area.topLeft.y; + final int height = area.size.height; + for (int sx = 0, dx = area.topLeft.x; sx < area.size.width; ++sx, ++dx) { + System.arraycopy(src.tiles[sx], 0, dest.tiles[dx], dy, height); + } + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/ReplaceableMapSection.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/ReplaceableMapSection.java new file mode 100644 index 000000000..7ec9012d4 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/ReplaceableMapSection.java @@ -0,0 +1,25 @@ +package com.gpl.rpg.AndorsTrail.model.map; + +import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress; +import com.gpl.rpg.AndorsTrail.util.CoordRect; + +public final class ReplaceableMapSection { + public boolean isApplied = false; + public final CoordRect replacementArea; + public final MapSection replaceLayersWith; + public final QuestProgress requireQuestStage; + + public ReplaceableMapSection( + CoordRect replacementArea, + MapSection replaceLayersWith, + QuestProgress requireQuestStage) { + this.replacementArea = replacementArea; + this.replaceLayersWith = replaceLayersWith; + this.requireQuestStage = requireQuestStage; + } + + public void apply(MapSection dest) { + dest.replaceLayerContentsWith(replaceLayersWith, replacementArea); + isApplied = true; + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java index 3c4f9b523..c60614eee 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java @@ -16,16 +16,16 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; public final class TMXMapFileParser { - public static TMXMap read(Resources r, int xmlResourceId, String name) { - return read(r.getXml(xmlResourceId), xmlResourceId, name); + public static TMXObjectMap readObjectMap(Resources r, int xmlResourceId, String name) { + return readObjectMap(r.getXml(xmlResourceId), xmlResourceId, name); } - public static TMXLayerMap readLayeredTileMap(Resources r, int xmlResourceId, String name) { + public static TMXLayerMap readLayerMap(Resources r, int xmlResourceId, String name) { return readLayerMap(r.getXml(xmlResourceId), name); } - private static TMXMap read(XmlResourceParser xrp, int xmlResourceId, String name) { - final TMXMap map = new TMXMap(); + private static TMXObjectMap readObjectMap(XmlResourceParser xrp, int xmlResourceId, String name) { + final TMXObjectMap map = new TMXObjectMap(); map.xmlResourceId = xmlResourceId; try { // Map format: http://sourceforge.net/apps/mediawiki/tiled/index.php?title=Examining_the_map_format @@ -35,7 +35,6 @@ public final class TMXMapFileParser { String s = xrp.getName(); if (s.equals("map")) { map.name = name; - map.orientation = xrp.getAttributeValue(null, "orientation"); map.width = xrp.getAttributeIntValue(null, "width", -1); map.height = xrp.getAttributeIntValue(null, "height", -1); map.tilewidth = xrp.getAttributeIntValue(null, "tilewidth", -1); @@ -72,8 +71,11 @@ public final class TMXMapFileParser { if (eventType == XmlResourceParser.START_TAG) { String s = xrp.getName(); if (s.equals("map")) { + map.name = name; map.width = xrp.getAttributeIntValue(null, "width", -1); map.height = xrp.getAttributeIntValue(null, "height", -1); + map.tilewidth = xrp.getAttributeIntValue(null, "tilewidth", -1); + map.tileheight = xrp.getAttributeIntValue(null, "tileheight", -1); XmlResourceParserUtils.readCurrentTagUntilEnd(xrp, new XmlResourceParserUtils.TagHandler() { public void handleTag(XmlResourceParser xrp, String tagName) throws XmlPullParserException, IOException { if (tagName.equals("tileset")) { @@ -82,6 +84,8 @@ public final class TMXMapFileParser { layers.add(readTMXMapLayer(xrp)); } else if (tagName.equals("property")) { map.properties.add(readTMXProperty(xrp)); + } else if (tagName.equals("objectgroup")) { + map.objectGroups.add(readTMXObjectGroup(xrp)); } } }); @@ -241,19 +245,21 @@ public final class TMXMapFileParser { (buffer[offset + 3] << 24) & 0xff000000; } - public static final class TMXMap extends TMXLayerMap { + public static final class TMXObjectMap extends TMXMap { public int xmlResourceId; - public String name; - public String orientation; - public int tilewidth; - public int tileheight; public final ArrayList objectGroups = new ArrayList(); } - public static class TMXLayerMap { - public int width; - public int height; + public static final class TMXLayerMap extends TMXMap { public TMXTileSet[] tileSets; public TMXLayer[] layers; + public final ArrayList objectGroups = new ArrayList(); + } + public static class TMXMap { + public String name; + public int width; + public int height; + public int tilewidth; + public int tileheight; public final ArrayList properties = new ArrayList(); } public static final class TMXTileSet { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java index cfa5d236d..c0f4e6dbb 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java @@ -1,8 +1,6 @@ package com.gpl.rpg.AndorsTrail.model.map; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; +import java.util.*; import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; import com.gpl.rpg.AndorsTrail.model.actor.MonsterType; @@ -12,12 +10,12 @@ import com.gpl.rpg.AndorsTrail.model.item.DropListCollection; import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXLayer; import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXLayerMap; import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXMap; +import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectMap; import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObject; import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectGroup; import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXProperty; import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXTileSet; import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress; -import com.gpl.rpg.AndorsTrail.resource.DynamicTileLoader; import com.gpl.rpg.AndorsTrail.resource.tiles.TileCache; import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.util.CoordRect; @@ -28,25 +26,24 @@ import com.gpl.rpg.AndorsTrail.util.Size; import android.content.res.Resources; public final class TMXMapTranslator { - private final ArrayList maps = new ArrayList(); + private final ArrayList maps = new ArrayList(); public void read(Resources r, int xmlResourceId, String name) { - maps.add(TMXMapFileParser.read(r, xmlResourceId, name)); + maps.add(TMXMapFileParser.readObjectMap(r, xmlResourceId, name)); } public static LayeredTileMap readLayeredTileMap(Resources res, TileCache tileCache, PredefinedMap map) { - TMXLayerMap resultMap = TMXMapFileParser.readLayeredTileMap(res, map.xmlResourceId, map.name); - return transformMap(resultMap, tileCache, map.name); + TMXLayerMap resultMap = TMXMapFileParser.readLayerMap(res, map.xmlResourceId, map.name); + return transformMap(resultMap, tileCache); } public ArrayList transformMaps(MonsterTypeCollection monsterTypes, DropListCollection dropLists) { return transformMaps(maps, monsterTypes, dropLists); } - public ArrayList transformMaps(Collection maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) { + public ArrayList transformMaps(Collection maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) { ArrayList result = new ArrayList(); - Tile tile = new Tile(); - for (TMXMap m : maps) { + for (TMXObjectMap m : maps) { assert(m.name != null); assert(m.name.length() > 0); assert(m.width > 0); @@ -64,13 +61,7 @@ public final class TMXMapTranslator { for (TMXObjectGroup group : m.objectGroups) { for (TMXObject object : group.objects) { - final Coord topLeft = new Coord( - Math.round(((float)object.x) / m.tilewidth) - ,Math.round(((float)object.y) / m.tileheight) - ); - final int width = Math.round(((float)object.width) / m.tilewidth); - final int height = Math.round(((float)object.height) / m.tileheight); - final CoordRect position = new CoordRect(topLeft, new Size(width, height)); + final CoordRect position = getTMXObjectPosition(object, m); if (object.type == null) { if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) @@ -148,6 +139,8 @@ public final class TMXMapTranslator { DropList dropList = dropLists.getDropList(object.name); if (dropList == null) continue; mapObjects.add(MapObject.createNewContainerArea(position, dropList)); + } else if (object.type.equals("replace")) { + // Do nothing. Will be handled when reading map layers instead. } else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { L.log("OPTIMIZE: Map " + m.name + ", has unrecognized object type \"" + object.type + "\" for name \"" + object.name + "\"."); } @@ -163,66 +156,155 @@ public final class TMXMapTranslator { return result; } - - private static LayeredTileMap transformMap(TMXLayerMap map, TileCache tileCache, String mapName) { + private static CoordRect getTMXObjectPosition(TMXObject object, TMXMap m) { + final Coord topLeft = new Coord( + Math.round(((float)object.x) / m.tilewidth) + ,Math.round(((float)object.y) / m.tileheight) + ); + final int width = Math.round(((float)object.width) / m.tilewidth); + final int height = Math.round(((float)object.height) / m.tileheight); + return new CoordRect(topLeft, new Size(width, height)); + } + + private static final String LAYERNAME_GROUND = "ground"; + private static final String LAYERNAME_OBJECTS = "objects"; + private static final String LAYERNAME_ABOVE = "above"; + private static final String LAYERNAME_WALKABLE = "walkable"; + private static final SetOfLayerNames defaultLayerNames = new SetOfLayerNames(LAYERNAME_GROUND, LAYERNAME_OBJECTS, LAYERNAME_ABOVE, LAYERNAME_WALKABLE); + + private static LayeredTileMap transformMap(TMXLayerMap map, TileCache tileCache) { final Size mapSize = new Size(map.width, map.height); - final MapLayer[] layers = new MapLayer[] { - new MapLayer(mapSize) - ,new MapLayer(mapSize) - ,new MapLayer(mapSize) - }; - boolean[][] isWalkable = new boolean[map.width][map.height]; - for (int y = 0; y < map.height; ++y) { - for (int x = 0; x < map.width; ++x) { - isWalkable[x][y] = true; - } - } - Tile tile = new Tile(); String colorFilter = null; for (TMXProperty prop : map.properties) { if (prop.name.equalsIgnoreCase("colorfilter")) colorFilter = prop.value; } HashSet usedTileIDs = new HashSet(); + HashMap layersPerLayerName = new HashMap(); for (TMXLayer layer : map.layers) { - int ixMapLayer = 0; String layerName = layer.name; assert(layerName != null); assert(layerName.length() > 0); layerName = layerName.toLowerCase(); - boolean isWalkableLayer = false; - if (layerName.startsWith("object")) { - ixMapLayer = LayeredTileMap.LAYER_OBJECTS; - } else if (layerName.startsWith("ground")) { - ixMapLayer = LayeredTileMap.LAYER_GROUND; - } else if (layerName.startsWith("above")) { - ixMapLayer = LayeredTileMap.LAYER_ABOVE; - } else if (layerName.startsWith("walk")) { - isWalkableLayer = true; - } else { - continue; + if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + if (layersPerLayerName.containsKey(layerName)) { + L.log("WARNING: Map \"" + map.name + "\" contains multiple layers with name \"" + layerName + "\"."); + } } - - for (int y = 0; y < layer.height; ++y) { - for (int x = 0; x < layer.width; ++x) { - int gid = layer.gids[x][y]; - if (gid <= 0) continue; - - if (!getTile(map, gid, tile)) continue; + layersPerLayerName.put(layerName, layer); + } - if (isWalkableLayer) { - isWalkable[x][y] = false; - } else { - int tileID = tileCache.getTileID(tile.tilesetName, tile.localId); - layers[ixMapLayer].tiles[x][y] = tileID; - usedTileIDs.add(tileID); + MapSection defaultLayout = transformMapSection(map, + tileCache, + new CoordRect(new Coord(0,0), mapSize), + layersPerLayerName, + usedTileIDs, + defaultLayerNames); + + ArrayList replaceableSections = new ArrayList(); + for (TMXObjectGroup objectGroup : map.objectGroups) { + for(TMXObject obj : objectGroup.objects) { + if ("replace".equals(obj.type)) { + final CoordRect position = getTMXObjectPosition(obj, map); + SetOfLayerNames layerNames = new SetOfLayerNames(); + for (TMXProperty prop : obj.properties) { + if (prop.name.equalsIgnoreCase(LAYERNAME_GROUND)) layerNames.groundLayerName = prop.value; + else if (prop.name.equalsIgnoreCase(LAYERNAME_OBJECTS)) layerNames.objectsLayerName = prop.value; + else if (prop.name.equalsIgnoreCase(LAYERNAME_ABOVE)) layerNames.aboveLayersName = prop.value; + else if (prop.name.equalsIgnoreCase(LAYERNAME_WALKABLE)) layerNames.walkableLayersName = prop.value; + else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + L.log("OPTIMIZE: Map " + map.name + " contains replace area with unknown property \"" + prop.name + "\"."); + } } + MapSection replacementSection = transformMapSection(map, tileCache, position, layersPerLayerName, usedTileIDs, layerNames); + QuestProgress requireQuestStage = QuestProgress.parseQuestProgress(obj.name); + if (requireQuestStage == null) { + if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + L.log("OPTIMIZE: Map " + map.name + " contains replace area that cannot be parsed as a quest stage."); + } + continue; + } + replaceableSections.add(new ReplaceableMapSection(position, replacementSection, requireQuestStage)); } } } - return new LayeredTileMap(mapSize, layers, isWalkable, usedTileIDs, colorFilter); + + ReplaceableMapSection[] replaceableSections_ = null; + if (!replaceableSections.isEmpty()) { + replaceableSections_ = replaceableSections.toArray(new ReplaceableMapSection[replaceableSections.size()]); + } + return new LayeredTileMap(mapSize, defaultLayout, replaceableSections_, colorFilter, usedTileIDs); } - + + private static MapSection transformMapSection( + TMXLayerMap srcMap, + TileCache tileCache, + CoordRect area, + HashMap layersPerLayerName, + HashSet usedTileIDs, + SetOfLayerNames layerNames + ) { + final MapLayer layerGround = transformMapLayer(findLayer(layersPerLayerName, layerNames.groundLayerName, srcMap.name), srcMap, tileCache, area, usedTileIDs); + final MapLayer layerObjects = transformMapLayer(findLayer(layersPerLayerName, layerNames.objectsLayerName, srcMap.name), srcMap, tileCache, area, usedTileIDs); + final MapLayer layerAbove = transformMapLayer(findLayer(layersPerLayerName, layerNames.aboveLayersName, srcMap.name), srcMap, tileCache, area, usedTileIDs); + boolean[][] isWalkable = transformWalkableMapLayer(findLayer(layersPerLayerName, layerNames.walkableLayersName, srcMap.name), area); + return new MapSection(layerGround, layerObjects, layerAbove, isWalkable); + } + + private static TMXLayer findLayer(HashMap layersPerLayerName, String layerName, String mapName) { + if (layerName == null) return null; + if (layerName.length() == 0) return null; + TMXLayer result = layersPerLayerName.get(layerName); + if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + if (result == null) { + L.log("WARNING: Cannot find maplayer \"" + layerName + "\" requested by map \"" + mapName + "\"."); + } + } + return result; + } + + private static MapLayer transformMapLayer( + TMXLayer srcLayer, + TMXLayerMap srcMap, + TileCache tileCache, + CoordRect area, + HashSet usedTileIDs + ) { + if (srcLayer == null) return null; + final MapLayer result = new MapLayer(area.size); + Tile tile = new Tile(); + for (int dy = 0, sy = area.topLeft.y; dy < area.size.height; ++dy, ++sy) { + for (int dx = 0, sx = area.topLeft.x; dx < area.size.width; ++dx, ++sx) { + int gid = srcLayer.gids[sx][sy]; + if (gid <= 0) continue; + + if (!getTile(srcMap, gid, tile)) continue; + + int tileID = tileCache.getTileID(tile.tilesetName, tile.localId); + result.tiles[dx][dy] = tileID; + usedTileIDs.add(tileID); + } + } + return result; + } + + private static boolean[][] transformWalkableMapLayer(TMXLayer srcLayer, CoordRect area) { + if (srcLayer == null) return null; + final boolean[][] isWalkable = new boolean[area.size.width][area.size.height]; + for (int x = 0; x < area.size.width; ++x) { + Arrays.fill(isWalkable[x], true); + } + for (int dy = 0, sy = area.topLeft.y; dy < area.size.height; ++dy, ++sy) { + for (int dx = 0, sx = area.topLeft.x; dx < area.size.width; ++dx, ++sx) { + int gid = srcLayer.gids[sx][sy]; + if (gid > 0) { + isWalkable[dx][dy] = false; + } + } + } + return isWalkable; + } + private static boolean getTile(final TMXLayerMap map, final int gid, final Tile dest) { for(int i = map.tileSets.length - 1; i >= 0; --i) { TMXTileSet ts = map.tileSets[i]; @@ -232,7 +314,7 @@ public final class TMXMapTranslator { return true; } } - L.log("WARNING: Cannot find tile for gid " + gid); //(" + x + ", " + y + "), ") + L.log("WARNING: Cannot find tile for gid " + gid); return false; } @@ -240,73 +322,23 @@ public final class TMXMapTranslator { public String tilesetName; public int localId; } - - /* - public static final class TMXMap extends TMXLayerMap { - public int xmlResourceId; - public String name; - public String orientation; - public int tilewidth; - public int tileheight; - public ArrayList objectGroups = new ArrayList(); - } - public static class TMXLayerMap { - public int width; - public int height; - public TMXTileSet[] tileSets; - public TMXLayer[] layers; - } - public static final class TMXTileSet { - public int firstgid; - public String name; - public int tilewidth; - public int tileheight; - public String imageSource; - public String imageName; - } - public static final class TMXLayer { - public String name; - public int width; - public int height; - public int[][] gids; - } - public static final class TMXObjectGroup { - public String name; - public int width; - public int height; - public ArrayList objects = new ArrayList(); - } - public static final class TMXObject { - public String name; - public String type; - public int x; - public int y; - public int width; - public int height; - public ArrayList properties = new ArrayList(); - } - public static final class TMXProperty { - public String name; - public String value; - } - */ - /* - - - - - - - - H4sIAAAAAAAAA/NgYGDwIBK7AbEnHkyOOmwYXR02MwZSHQyTah4xGADnAt2SkAEAAA== - - - - - H4sIAAAAAAAAA2NgoA1gYUHlP2HGro6NBbt4MysqXw2oLhEqlgSlU4H0YjR12EAbUE0KFnXPgG5iRLJ/GQ6zHuNwOy7gxE6aemQAAJRT7VKQAQAA - - - - */ + private static final class SetOfLayerNames { + public String groundLayerName; + public String objectsLayerName; + public String aboveLayersName; + public String walkableLayersName; + public SetOfLayerNames() { + this.groundLayerName = null; + this.objectsLayerName = null; + this.aboveLayersName = null; + this.walkableLayersName = null; + } + public SetOfLayerNames(String groundLayerName, String objectsLayerName, String aboveLayersName, String walkableLayersName) { + this.groundLayerName = groundLayerName; + this.objectsLayerName = objectsLayerName; + this.aboveLayersName = aboveLayersName; + this.walkableLayersName = walkableLayersName; + } + } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index 61e7e517b..5059e1517 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -38,7 +38,7 @@ public final class MainView extends SurfaceView CombatSelectionListener, MonsterSpawnListener, MonsterMovementListener, - LootBagListener, + MapLayoutListener, VisualEffectFrameListener, GameRoundListener { @@ -276,8 +276,8 @@ public final class MainView extends SurfaceView private void doDrawRect(Canvas canvas, CoordRect area) { - drawMapLayer(canvas, area, currentTileMap.layers[LayeredTileMap.LAYER_GROUND]); - tryDrawMapLayer(canvas, area, LayeredTileMap.LAYER_OBJECTS); + drawMapLayer(canvas, area, currentTileMap.currentLayout.layerGround); + tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); for (BloodSplatter splatter : currentMap.splatters) { drawFromMapPosition(canvas, area, splatter.position, splatter.iconID); @@ -296,7 +296,7 @@ public final class MainView extends SurfaceView } } - tryDrawMapLayer(canvas, area, LayeredTileMap.LAYER_ABOVE); + tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerAbove); if (model.uiSelections.selectedPosition != null) { if (model.uiSelections.selectedMonster != null) { @@ -307,8 +307,8 @@ public final class MainView extends SurfaceView } } - private void tryDrawMapLayer(Canvas canvas, final CoordRect area, final int layerIndex) { - if (currentTileMap.layers.length > layerIndex) drawMapLayer(canvas, area, currentTileMap.layers[layerIndex]); + private void tryDrawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) { + if (layer != null) drawMapLayer(canvas, area, layer); } private void drawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) { @@ -401,7 +401,7 @@ public final class MainView extends SurfaceView public void subscribe() { controllers.gameRoundController.gameRoundListeners.add(this); controllers.effectController.visualEffectFrameListeners.add(this); - controllers.itemController.lootBagListeners.add(this); + controllers.mapController.mapLayoutListeners.add(this); controllers.movementController.playerMovementListeners.add(this); controllers.combatController.combatSelectionListeners.add(this); controllers.monsterSpawnController.monsterSpawnListeners.add(this); @@ -412,7 +412,7 @@ public final class MainView extends SurfaceView controllers.monsterSpawnController.monsterSpawnListeners.remove(this); controllers.combatController.combatSelectionListeners.remove(this); controllers.movementController.playerMovementListeners.remove(this); - controllers.itemController.lootBagListeners.remove(this); + controllers.mapController.mapLayoutListeners.remove(this); controllers.effectController.visualEffectFrameListeners.remove(this); controllers.gameRoundController.gameRoundListeners.remove(this); } @@ -494,6 +494,12 @@ public final class MainView extends SurfaceView redrawTile(p, REDRAW_TILE_BAG); } + @Override + public void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap) { + if (map != currentMap) return; + redrawAll(REDRAW_ALL_MAP_CHANGED); + } + @Override public void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset) { redrawAreaWithEffect(animation, tileID, textYOffset);