Adventure - Pathfinding update (#3675)

* Adventure - reward scene controller button availability

* Adventure A* & main quest prep

* Update pom.xml

* box2d natives

---------

Co-authored-by: kevlahnota <anthonycalosa@gmail.com>
This commit is contained in:
TabletopGeneral
2023-08-22 23:56:20 -07:00
committed by GitHub
parent 74711f6bc5
commit 168f999a57
46 changed files with 4297 additions and 235 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -256,5 +256,11 @@
<artifactId>gdx-controllers-desktop</artifactId> <artifactId>gdx-controllers-desktop</artifactId>
<version>2.2.3-SNAPSHOT</version> <version>2.2.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>com.badlogicgames.gdx</groupId>
<artifactId>gdx-box2d-platform</artifactId>
<version>1.11.0</version>
<classifier>natives-desktop</classifier>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -80,6 +80,16 @@
<version>5.2.3</version> <version>5.2.3</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.badlogicgames.gdx</groupId>
<artifactId>gdx-box2d</artifactId>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>com.badlogicgames.gdx</groupId>
<artifactId>gdx-ai</artifactId>
<version>1.8.2</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,5 +1,11 @@
package forge.adventure.character; package forge.adventure.character;
import com.badlogic.gdx.ai.steer.Steerable;
import com.badlogic.gdx.ai.steer.SteeringAcceleration;
import com.badlogic.gdx.ai.steer.SteeringBehavior;
import com.badlogic.gdx.ai.steer.behaviors.*;
import com.badlogic.gdx.ai.steer.utils.paths.LinePath;
import com.badlogic.gdx.ai.utils.Location;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.Batch;
@@ -16,12 +22,16 @@ import forge.adventure.player.AdventurePlayer;
import forge.adventure.util.Current; import forge.adventure.util.Current;
import forge.adventure.util.MapDialog; import forge.adventure.util.MapDialog;
import forge.adventure.util.Reward; import forge.adventure.util.Reward;
import forge.adventure.util.pathfinding.MovementBehavior;
import forge.adventure.util.pathfinding.NavigationVertex;
import forge.adventure.util.pathfinding.ProgressableGraphPath;
import forge.card.CardRarity; import forge.card.CardRarity;
import forge.deck.Deck; import forge.deck.Deck;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.MyRandom; import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -30,7 +40,20 @@ import java.util.stream.Collectors;
* EnemySprite * EnemySprite
* Character sprite that represents an Enemy * Character sprite that represents an Enemy
*/ */
public class EnemySprite extends CharacterSprite { public class EnemySprite extends CharacterSprite implements Steerable<Vector2> {
private static final SteeringAcceleration<Vector2> steerOutput =
new SteeringAcceleration<Vector2>(new Vector2());
Vector2 position;
float orientation;
Vector2 linearVelocity = new Vector2(1, 0);
float angularVelocity;
float maxSpeed;
boolean independentFacing;
SteeringBehavior<Vector2> behavior;
boolean tagged;
EnemyData data; EnemyData data;
public MapDialog dialog; //Dialog to show on contact. Overrides standard battle (can be started as an action) public MapDialog dialog; //Dialog to show on contact. Overrides standard battle (can be started as an action)
public MapDialog defeatDialog; //Dialog to show on defeat. Overrides standard death (can be removed as an action) public MapDialog defeatDialog; //Dialog to show on defeat. Overrides standard death (can be removed as an action)
@@ -53,9 +76,12 @@ public class EnemySprite extends CharacterSprite {
public float threatRange = 0.0f; //If range < threatRange, begin pursuit public float threatRange = 0.0f; //If range < threatRange, begin pursuit
public float pursueRange = 0.0f; //If range > pursueRange, abandon pursuit public float pursueRange = 0.0f; //If range > pursueRange, abandon pursuit
public float fleeRange = 0.0f; //If range < fleeRange, attempt to move away to fleeRange public float fleeRange = 0.0f; //If range < fleeRange, attempt to move away to fleeRange
private boolean aggro = false; public float speedModifier = 0.0f; // Increase or decrease default speed
public boolean aggro = false;
public boolean ignoreDungeonEffect = false; public boolean ignoreDungeonEffect = false;
public String questStageID; public String questStageID;
private ProgressableGraphPath<NavigationVertex> navPath;
public Vector2 fleeTarget;
public EnemySprite(EnemyData enemyData) { public EnemySprite(EnemyData enemyData) {
this(0,enemyData); this(0,enemyData);
@@ -64,16 +90,19 @@ public class EnemySprite extends CharacterSprite {
public EnemySprite(int id, EnemyData enemyData) { public EnemySprite(int id, EnemyData enemyData) {
super(id,enemyData.sprite); super(id,enemyData.sprite);
data = enemyData; data = enemyData;
initializeBaseMovementBehavior();
} }
public void parseWaypoints(String waypoints){ public void parseWaypoints(String waypoints){
String[] wp = waypoints.replaceAll("\\s", "").split(","); String[] wp = waypoints.replaceAll("\\s", "").split(",");
for (String s : wp) { for (String s : wp) {
movementBehaviors.addLast(new MovementBehavior()); movementBehaviors.addLast(new MovementBehavior());
if (s.startsWith("wait")) { if (!movementBehaviors.isEmpty()) {
movementBehaviors.peekLast().duration = Float.parseFloat(s.substring(4)); if (s.startsWith("wait")) {
} else { movementBehaviors.peekLast().duration = Float.parseFloat(s.substring(4));
movementBehaviors.peekLast().destination = Integer.parseInt(s); } else {
movementBehaviors.peekLast().destination = s;
}
} }
} }
} }
@@ -83,7 +112,13 @@ public class EnemySprite extends CharacterSprite {
float scale = data == null ? 1f : data.scale; float scale = data == null ? 1f : data.scale;
if (scale < 0) if (scale < 0)
scale = 1f; scale = 1f;
boundingRect.set(getX(), getY(), getWidth()*scale, getHeight()*scale);
float width = getWidth()*scale* collisionHeight;
float height = getHeight()*scale* collisionHeight;
float x = getX() + (getWidth() - width)/2;
float y = getY() + (getHeight() - height)/2;
boundingRect.set(x, y, width, height);
unfreezeRange = 30f * scale; unfreezeRange = 30f * scale;
} }
@@ -94,6 +129,183 @@ public class EnemySprite extends CharacterSprite {
moveBy(diff.x, diff.y,delta); moveBy(diff.x, diff.y,delta);
} }
public void initializeBaseMovementBehavior() {
Location<Vector2> seekTarget = new Location<Vector2>() {
@Override
public Vector2 getPosition() {
return navPath.nodes.get(0).pos;
}
@Override
public float getOrientation() {
return 0;
}
@Override
public void setOrientation(float orientation) {
}
@Override
public float vectorToAngle(Vector2 vector) {
return 0;
}
@Override
public Vector2 angleToVector(Vector2 outVector, float angle) {
return null;
}
@Override
public Location<Vector2> newLocation() {
return null;
}
};
Seek<Vector2> seek = new Seek<>(this);
seek.setTarget(seekTarget);
Array<Vector2> wp = new Array<>();
if (navPath != null && navPath.nodes != null) {
for (NavigationVertex v : navPath.nodes)
wp.add(v.pos);
}
LinePath<Vector2> linePath = null;
FollowPath<Vector2, LinePath.LinePathParam> followWaypoints = null;
if (wp.size == 1) {
wp.insert(0, pos());
}
if (wp.size >= 2) {
linePath = new LinePath<Vector2>(wp, false);
followWaypoints = new FollowPath<>(this, linePath);
followWaypoints.setPathOffset(0.5f);
}
Arrive<Vector2> moveDirectlyToDestination = new Arrive<>(this, new Location<Vector2>() {
@Override
public Vector2 getPosition() {
if (navPath == null || navPath.nodes.size == 0)
return pos();
return navPath.get(0).pos;
}
@Override
public float getOrientation() {
return 0;
}
@Override
public void setOrientation(float orientation) {
}
@Override
public float vectorToAngle(Vector2 vector) {
return 0;
}
@Override
public Vector2 angleToVector(Vector2 outVector, float angle) {
return null;
}
@Override
public Location<Vector2> newLocation() {
return null;
}
})
.setTimeToTarget(0.01f)
.setArrivalTolerance(0f)
.setDecelerationRadius(10);
if (followWaypoints != null)
setBehavior(followWaypoints);
else
setBehavior(moveDirectlyToDestination);
}
public void setBehavior(SteeringBehavior<Vector2> behavior) {
this.behavior = behavior;
}
public SteeringBehavior<Vector2> getBehavior() {
return behavior;
}
public void update(float delta) {
if(behavior != null) {
behavior.calculateSteering(steerOutput);
while (steerOutput.isZero() && navPath != null && navPath.getCount() > 1) {
navPath.remove(0);
behavior.calculateSteering(steerOutput);
}
applySteering(delta);
}
}
private void applySteering(float delta) {
if(!steerOutput.linear.isZero()) {
Vector2 force = steerOutput.linear.scl(delta);
force.setLength(Math.min(speed() * delta, force.len()));
moveBy(force.x, force.y);
}
}
@Override
public float vectorToAngle (Vector2 vector) {
return (float)Math.atan2(-vector.x, vector.y);
}
@Override
public Vector2 angleToVector (Vector2 outVector, float angle) {
outVector.x = -(float)Math.sin(angle);
outVector.y = (float)Math.cos(angle);
return outVector;
}
@Override
public Vector2 getLinearVelocity() {
return linearVelocity;
}
@Override
public float getAngularVelocity() {
return angularVelocity;
}
@Override
public float getBoundingRadius() {
return getWidth()/2;
}
@Override
public boolean isTagged() {
return tagged;
}
@Override
public Vector2 getPosition() {
return pos();
}
@Override
public float getOrientation() {
return orientation;
}
@Override
public void setOrientation(float value) {
orientation = value;
}
@Override
public Location<Vector2> newLocation() {
return null;
}
@Override
public void setTagged(boolean value) {
tagged = value;
}
public void freezeMovement(){ public void freezeMovement(){
_freeze = true; _freeze = true;
setPosition(_previousPosition6.x, _previousPosition6.y); setPosition(_previousPosition6.x, _previousPosition6.y);
@@ -102,15 +314,15 @@ public class EnemySprite extends CharacterSprite {
// Combined with player doing the same, should no longer be colliding to immediately re-enter battle if mob still present // Combined with player doing the same, should no longer be colliding to immediately re-enter battle if mob still present
} }
public Vector2 getTargetVector(PlayerSprite player, float delta) { public Vector2 getTargetVector(PlayerSprite player, ArrayList<NavigationVertex> sortedGraphNodes, float delta) {
//todo - this can be integrated into overworld movement as well, giving flee behaviors or moving to generated waypoints //todo - this can be integrated into overworld movement as well, giving flee behaviors or moving to generated waypoints
Vector2 target = pos(); Vector2 target = pos();
Vector2 routeToPlayer = new Vector2(player.pos()).sub(target); Vector2 spriteToPlayer = new Vector2(player.pos()).sub(target);
if (_freeze){ if (_freeze){
//Mob has defeated player in battle, hold still until player has a chance to move away. //Mob has defeated player in battle, hold still until player has a chance to move away.
//Without this moving enemies can immediately restart battle. //Without this moving enemies can immediately restart battle.
if (routeToPlayer.len() < unfreezeRange) { if (spriteToPlayer.len() < unfreezeRange) {
timer += delta; timer += delta;
return Vector2.Zero; return Vector2.Zero;
} }
@@ -119,53 +331,81 @@ public class EnemySprite extends CharacterSprite {
} }
} }
NavigationVertex targetPoint = null;
if (threatRange > 0 || fleeRange > 0){ if (threatRange > 0 || fleeRange > 0){
if (routeToPlayer.len() <= threatRange || (aggro && routeToPlayer.len() <= pursueRange)) if (spriteToPlayer.len() <= threatRange || (aggro && spriteToPlayer.len() <= pursueRange))
{ {
if (sortedGraphNodes != null) {
for (NavigationVertex candidate : sortedGraphNodes) {
Vector2 candidateToPlayer = new Vector2(candidate.pos).sub(player.pos());
if ((candidateToPlayer.x * candidateToPlayer.x) + (candidateToPlayer.y * candidateToPlayer.y) <
(spriteToPlayer.x * spriteToPlayer.x) + (spriteToPlayer.y * spriteToPlayer.y)) {
targetPoint = candidate;
break;
}
}
}
aggro = true; aggro = true;
return routeToPlayer; if (targetPoint != null) {
return targetPoint.pos;
}
return new Vector2(player.pos());
} }
if (routeToPlayer.len() <= fleeRange) if (spriteToPlayer.len() <= fleeRange)
{ {
Float fleeDistance = fleeRange - routeToPlayer.len(); //todo: replace with inverse A* variant, seeking max total distance from player in X generations
return new Vector2(target).sub(player.pos()).setLength(fleeDistance); // of movement, valuing each node by distance from player divided by closest distance(s) in path
// in order to make close passes to escape less appealing than maintaining moderate distance
float fleeDistance = fleeRange - spriteToPlayer.len();
return new Vector2(pos()).sub(player.pos()).setLength(fleeDistance).add(pos());
}
if (aggro && spriteToPlayer.len() > pursueRange) {
aggro = false;
initializeBaseMovementBehavior();
} }
} }
if (movementBehaviors.size() > 0){ if (movementBehaviors.peek() != null){
MovementBehavior peek = movementBehaviors.peek();
//TODO - This first block needs to be redone, doesn't work as intended and can also possibly skip behaviors in rare situations
// if (peek.getDuration() == 0 && target.equals(_previousPosition6) && timer >= _movementTimeout)
// {
// //stationary in an untimed behavior, move on to next behavior attempt to get unstuck
// if (movementBehaviors.size() > 1) {
// MovementBehavior current = movementBehaviors.pop();
// current.currentTargetVector = null;
// movementBehaviors.addLast(current);
// }
// }
//else
if (peek.getDuration() == 0 && peek.getNextTargetVector(pos()).dst(pos()) < 2){
//this is a location based behavior that has been completed. Move on to the next behavior
MovementBehavior current = movementBehaviors.pop();
current.currentTargetVector = null;
movementBehaviors.addLast(current);
if (movementBehaviors.peek().getDuration() == 0 && target.equals(_previousPosition6) && timer >= _movementTimeout)
{
//stationary in an untimed behavior, move on to next behavior attempt to get unstuck
if (movementBehaviors.size() > 1) {
movementBehaviors.addLast(movementBehaviors.pop());
timer = 0.0f;
}
} }
else if (movementBehaviors.peek().pos().sub(pos()).len() < 0.3){ else if ( peek.getDuration() > 0)
//this is a location based behavior that has been completed. Move on if there are more behaviors
if (movementBehaviors.size() > 1) {
movementBehaviors.addLast(movementBehaviors.pop());
timer = 0.0f;
}
}
else if ( movementBehaviors.peek().getDuration() > 0)
{ {
if (timer >= movementBehaviors.peek().getDuration() + delta) if (timer >= peek.getDuration() + delta)
{ {
//this is a timed behavior that has been completed. Move to the next behavior and restart the timer //this is a timed behavior that has been completed. Move to the next behavior and restart the timer
movementBehaviors.addLast(movementBehaviors.pop()); MovementBehavior current = movementBehaviors.pop();
timer = 0.0f; current.currentTargetVector = null;
movementBehaviors.addLast(current);
} }
else{ else{
timer += delta;//this is a timed behavior that has not been completed, continue this behavior timer += delta;//this is a timed behavior that has not been completed, continue this behavior
return new Vector2(pos());
} }
} }
if (movementBehaviors.peek().pos().len() > 0.3) if (peek.getNextTargetVector(pos()).dst(pos()) > 0.3) {
target = new Vector2(movementBehaviors.peek().pos()).sub(pos()); target = new Vector2(peek.getNextTargetVector(pos()));
else target = Vector2.Zero; }
else target = new Vector2(pos());
} }
else target = Vector2.Zero; else target = new Vector2(pos());
return target; return target;
} }
public void updatePositon() public void updatePositon()
@@ -313,7 +553,7 @@ public class EnemySprite extends CharacterSprite {
} }
public float speed() { public float speed() {
return data.speed; return Float.max(data.speed + speedModifier, 0);
} }
public float getLifetime() { public float getLifetime() {
@@ -322,44 +562,71 @@ public class EnemySprite extends CharacterSprite {
return Math.max(data.lifetime, lifetime); return Math.max(data.lifetime, lifetime);
} }
//Pathfinding integration below this line
public class MovementBehavior { public void setNavPath(ProgressableGraphPath<NavigationVertex> navPath) {
this.navPath = navPath;
//temporary placeholders for overworld behavior integration
public boolean wander = false;
public boolean flee = false;
public boolean stop = false;
//end temporary
float duration = 0.0f;
float x = 0.0f;
float y = 0.0f;
int destination = 0;
public float getX(){
return x;
}
public float getY(){
return y;
}
public float getDuration(){
return duration;
}
public int getDestination(){
return destination;
}
public void setX(float newVal){
x = newVal;
}
public void setY(float newVal){
y = newVal;
}
public Vector2 pos() {
return new Vector2(getX(), getY());
}
} }
public ProgressableGraphPath<NavigationVertex> getNavPath() {
return navPath;
}
@Override
public float getZeroLinearSpeedThreshold() {
return 0;
}
@Override
public void setZeroLinearSpeedThreshold(float value) {
}
@Override
public float getMaxLinearSpeed() {
return 500;
}
@Override
public void setMaxLinearSpeed(float maxLinearSpeed) {
}
@Override
public float getMaxLinearAcceleration() {
return 5000;
}
@Override
public void setMaxLinearAcceleration(float maxLinearAcceleration) {
}
@Override
public float getMaxAngularSpeed() {
return 0;
}
@Override
public void setMaxAngularSpeed(float maxAngularSpeed) {
}
@Override
public float getMaxAngularAcceleration() {
return 0;
}
@Override
public void setMaxAngularAcceleration(float maxAngularAcceleration) {
}
public void steer(Vector2 currentVector) {
}
} }

View File

@@ -316,6 +316,7 @@ public class RewardScene extends UIScene {
if (type == Type.Shop) { if (type == Type.Shop) {
this.shopActor = shopActor; this.shopActor = shopActor;
this.changes = shopActor.getMapStage().getChanges(); this.changes = shopActor.getMapStage().getChanges();
addToSelectable(restockButton);
} }
for (Actor actor : new Array.ArrayIterator<>(generated)) { for (Actor actor : new Array.ArrayIterator<>(generated)) {
actor.remove(); actor.remove();
@@ -323,6 +324,7 @@ public class RewardScene extends UIScene {
((RewardActor) actor).dispose(); ((RewardActor) actor).dispose();
} }
} }
addToSelectable(doneButton);
generated.clear(); generated.clear();
Actor card = ui.findActor("cards"); Actor card = ui.findActor("cards");

View File

@@ -1,6 +1,5 @@
package forge.adventure.stage; package forge.adventure.stage;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.controllers.Controllers; import com.badlogic.gdx.controllers.Controllers;
import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.Batch;
@@ -14,6 +13,7 @@ import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.objects.TiledMapTileMapObject; import com.badlogic.gdx.maps.tiled.objects.TiledMapTileMapObject;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.*;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputEvent;
@@ -23,9 +23,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog; import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.*;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Scaling;
import com.badlogic.gdx.utils.Timer; import com.badlogic.gdx.utils.Timer;
import com.github.tommyettinger.textra.TextraButton; import com.github.tommyettinger.textra.TextraButton;
import com.github.tommyettinger.textra.TextraLabel; import com.github.tommyettinger.textra.TextraLabel;
@@ -37,6 +35,9 @@ import forge.adventure.data.*;
import forge.adventure.pointofintrest.PointOfInterestChanges; import forge.adventure.pointofintrest.PointOfInterestChanges;
import forge.adventure.scene.*; import forge.adventure.scene.*;
import forge.adventure.util.*; import forge.adventure.util.*;
import forge.adventure.util.pathfinding.NavigationMap;
import forge.adventure.util.pathfinding.NavigationVertex;
import forge.adventure.util.pathfinding.ProgressableGraphPath;
import forge.adventure.world.WorldSave; import forge.adventure.world.WorldSave;
import forge.assets.FBufferedImage; import forge.assets.FBufferedImage;
import forge.assets.FImageComplex; import forge.assets.FImageComplex;
@@ -53,6 +54,7 @@ import forge.sound.SoundSystem;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.*; import java.util.*;
import java.util.Queue;
/** /**
@@ -61,9 +63,10 @@ import java.util.*;
public class MapStage extends GameStage { public class MapStage extends GameStage {
public static MapStage instance; public static MapStage instance;
final Array<MapActor> actors = new Array<>(); final Array<MapActor> actors = new Array<>();
public com.badlogic.gdx.physics.box2d.World gdxWorld;
TiledMap map; public TiledMap tiledMap;
Array<Rectangle> collisionRect = new Array<>(); public Array<Rectangle> collisionRect = new Array<>();
public Map<Float, NavigationMap> navMaps = new HashMap<>();
private boolean isInMap = false; private boolean isInMap = false;
MapLayer spriteLayer; MapLayer spriteLayer;
private PointOfInterestChanges changes; private PointOfInterestChanges changes;
@@ -87,7 +90,13 @@ public class MapStage extends GameStage {
private boolean respawnEnemies; private boolean respawnEnemies;
private boolean canFailDungeon = false; private boolean canFailDungeon = false;
protected ArrayList<EnemySprite> enemies = new ArrayList<>(); protected ArrayList<EnemySprite> enemies = new ArrayList<>();
protected Map<Integer, Vector2> waypoints = new HashMap<>(); public Map<Integer, Vector2> waypoints = new HashMap<>();
//todo: add additional graphs for other sprite sizes if desired. Current implementation
// allows for mobs of any size to fit into 16x16 tiles for navigation purposes
float collisionWidthMod = 0.4f;
float defaultSpriteSize = 16f;
float navMapSize = defaultSpriteSize * collisionWidthMod;
public boolean getDialogOnlyInput() { public boolean getDialogOnlyInput() {
return dialogOnlyInput; return dialogOnlyInput;
@@ -126,6 +135,7 @@ public class MapStage extends GameStage {
private boolean freezeAllEnemyBehaviors = false; private boolean freezeAllEnemyBehaviors = false;
protected MapStage() { protected MapStage() {
gdxWorld = new World(new Vector2(0, 0),false);
dialog = Controls.newDialog(""); dialog = Controls.newDialog("");
eventTouchDown = new InputEvent(); eventTouchDown = new InputEvent();
eventTouchDown.setPointer(-1); eventTouchDown.setPointer(-1);
@@ -309,10 +319,11 @@ public class MapStage extends GameStage {
} }
public void loadMap(TiledMap map, String sourceMap, String targetMap, int spawnTargetId) { public void loadMap(TiledMap map, String sourceMap, String targetMap, int spawnTargetId) {
gdxWorld = new World(new Vector2(0, 0),false);
isLoadingMatch = false; isLoadingMatch = false;
isInMap = true; isInMap = true;
GameHUD.getInstance().showHideMap(false); GameHUD.getInstance().showHideMap(false);
this.map = map; this.tiledMap = map;
for (MapActor actor : new Array.ArrayIterator<>(actors)) { for (MapActor actor : new Array.ArrayIterator<>(actors)) {
actor.remove(); actor.remove();
foregroundSprites.removeActor(actor); foregroundSprites.removeActor(actor);
@@ -320,6 +331,7 @@ public class MapStage extends GameStage {
positions.clear(); positions.clear();
actors.clear(); actors.clear();
collisionRect.clear(); collisionRect.clear();
waypoints.clear();
if (collisionGroup != null) if (collisionGroup != null)
collisionGroup.remove(); collisionGroup.remove();
@@ -401,8 +413,9 @@ public class MapStage extends GameStage {
} while (oldSize != collisionRect.size); } while (oldSize != collisionRect.size);
if (spriteLayer == null) System.err.print("Warning: No spriteLayer present in map.\n"); if (spriteLayer == null) System.err.print("Warning: No spriteLayer present in map.\n");
replaceWaypoints(); navMaps.clear();
navMaps.put(navMapSize, new NavigationMap(navMapSize));
navMaps.get(navMapSize).initializeGeometryGraph();
getPlayerSprite().stop(); getPlayerSprite().stop();
} }
@@ -428,17 +441,6 @@ public class MapStage extends GameStage {
} }
} }
void replaceWaypoints() {
for (EnemySprite enemy : enemies) {
for (EnemySprite.MovementBehavior behavior : enemy.movementBehaviors) {
if (behavior.getDestination() > 0 && waypoints.containsKey(behavior.getDestination())) {
behavior.setX(waypoints.get(behavior.getDestination()).x);
behavior.setY(waypoints.get(behavior.getDestination()).y);
}
}
}
}
static public boolean containsOrEquals(Rectangle r1, Rectangle r2) { static public boolean containsOrEquals(Rectangle r1, Rectangle r2) {
float xmi = r2.x; float xmi = r2.x;
float xma = xmi + r2.width; float xma = xmi + r2.width;
@@ -456,7 +458,7 @@ public class MapStage extends GameStage {
for (MapObject collision : cell.getTile().getObjects()) { for (MapObject collision : cell.getTile().getObjects()) {
if (collision instanceof RectangleMapObject) { if (collision instanceof RectangleMapObject) {
Rectangle r = ((RectangleMapObject) collision).getRectangle(); Rectangle r = ((RectangleMapObject) collision).getRectangle();
collisionRect.add(new Rectangle((Math.round(layer.getTileWidth() * x) + r.x), (Math.round(layer.getTileHeight() * y) + r.y), Math.round(r.width), Math.round(r.height))); collisionRect.add(new Rectangle(((layer.getTileWidth() * x) + r.x), ((layer.getTileHeight() * y) + r.y), Math.round(r.width), Math.round(r.height)));
} }
} }
} }
@@ -657,6 +659,10 @@ public class MapStage extends GameStage {
if (dialogObject != null && !dialogObject.toString().isEmpty()) { if (dialogObject != null && !dialogObject.toString().isEmpty()) {
mob.parseWaypoints(dialogObject.toString()); mob.parseWaypoints(dialogObject.toString());
} }
if (prop.containsKey("speedModifier")) //Increase or decrease default speed for this mob
{
mob.speedModifier = Float.parseFloat(prop.get("speedModifier").toString());
}
enemies.add(mob); enemies.add(mob);
addMapActor(obj, mob); addMapActor(obj, mob);
@@ -1026,8 +1032,6 @@ public class MapStage extends GameStage {
for (Integer i : idsToRemove) deleteObject(i); for (Integer i : idsToRemove) deleteObject(i);
} }
final Rectangle tempBoundingRect = new Rectangle();
@Override @Override
protected void onActing(float delta) { protected void onActing(float delta) {
if (isPaused() || isDialogOnlyInput()) if (isPaused() || isDialogOnlyInput())
@@ -1040,6 +1044,9 @@ public class MapStage extends GameStage {
} }
else return; else return;
} }
float mobSize = navMapSize; //todo: replace with actual size if multiple nav maps implemented
ArrayList<NavigationVertex> verticesNearPlayer = new ArrayList<>(navMaps.get(mobSize).navGraph.getNodes());
verticesNearPlayer.sort(Comparator.comparingInt(o -> Math.round((o.pos.x - player.pos().x) * (o.pos.x - player.pos().x) + (o.pos.y - player.pos().y) * (o.pos.y - player.pos().y))));
if (!freezeAllEnemyBehaviors) { if (!freezeAllEnemyBehaviors) {
while (it.hasNext()) { while (it.hasNext()) {
@@ -1048,35 +1055,52 @@ public class MapStage extends GameStage {
continue; continue;
} }
mob.updatePositon(); mob.updatePositon();
mob.targetVector = mob.getTargetVector(player, delta);
Vector2 currentVector = new Vector2(mob.targetVector); ProgressableGraphPath<NavigationVertex> navPath = new ProgressableGraphPath<>(0);
if (mob.getData().flying) {
navPath.add(new NavigationVertex(mob.getTargetVector(player, null,delta)));
} else {
Vector2 destination = mob.getTargetVector(player, verticesNearPlayer, delta);
if (destination.epsilonEquals(mob.pos()) && !mob.aggro) {
mob.setAnimation(CharacterSprite.AnimationTypes.Idle);
continue;
}
if (destination.equals(mob.targetVector) && mob.getNavPath() != null)
navPath = mob.getNavPath();
if (navPath.nodes.size == 0 || !destination.equals(mob.targetVector)) {
mob.targetVector = destination;
navPath = navMaps.get(mobSize).findShortestPath(mobSize, mob.pos(), mob.targetVector);
}
if (mob.aggro) {
navPath.add(new NavigationVertex(player.pos()));
}
}
if (navPath == null || navPath.getCount() == 0 || navPath.get(0) == null) {
mob.setAnimation(CharacterSprite.AnimationTypes.Idle);
continue;
}
Vector2 currentVector = null;
while (navPath.getCount() > 0 && navPath.get(0) != null && (navPath.get(0).pos == null || navPath.get(0).pos.dst(mob.pos()) < 0.5f)) {
navPath.remove(0);
}
if (navPath.getCount() != 0) {
currentVector = new Vector2(navPath.get(0).pos).sub(mob.pos());
}
mob.setNavPath(navPath);
mob.clearActions(); mob.clearActions();
if (mob.targetVector.len() == 0.0f) { if (currentVector == null || (currentVector.x == 0.0f && currentVector.y == 0.0f)) {
mob.setAnimation(CharacterSprite.AnimationTypes.Idle); mob.setAnimation(CharacterSprite.AnimationTypes.Idle);
continue; continue;
} }
mob.steer(currentVector);
currentVector.setLength(Math.min(mob.speed() * delta, mob.targetVector.len())); mob.update(delta);
tempBoundingRect.set(mob.getX() + currentVector.x, mob.getY() + currentVector.y, mob.getWidth() * 0.4f, mob.getHeight() * 0.4f);
if (!mob.getData().flying && isColliding(tempBoundingRect))//if direct path is not possible
{
currentVector = adjustMovement(currentVector,tempBoundingRect);
tempBoundingRect.set(mob.getX() + currentVector.x, mob.getY(), mob.getWidth() * 0.4f, mob.getHeight() * 0.4f);
if (isColliding(tempBoundingRect))//if only x path is not possible
{
tempBoundingRect.set(mob.getX(), mob.getY() + currentVector.y, mob.getWidth() * 0.4f, mob.getHeight() * 0.4f);
if (!isColliding(tempBoundingRect))//if y path is possible
{
mob.moveBy(0, currentVector.y, delta);
}
} else {
mob.moveBy(currentVector.x, 0, delta);
}
} else {
mob.moveBy(currentVector.x, currentVector.y, delta);
}
} }
} }
@@ -1087,9 +1111,6 @@ public class MapStage extends GameStage {
if (positions.size() > 4) if (positions.size() > 4)
positions.remove(); positions.remove();
for (MapActor actor : new Array.ArrayIterator<>(actors)) { for (MapActor actor : new Array.ArrayIterator<>(actors)) {
if (actor.collideWithPlayer(player)) { if (actor.collideWithPlayer(player)) {
if (actor instanceof EnemySprite) { if (actor instanceof EnemySprite) {

View File

@@ -0,0 +1,56 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.ai.utils.Collision;
import com.badlogic.gdx.ai.utils.Ray;
import com.badlogic.gdx.ai.utils.RaycastCollisionDetector;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.RayCastCallback;
import com.badlogic.gdx.physics.box2d.World;
public class Box2dRaycastCollisionDetector implements RaycastCollisionDetector<Vector2> {
World world;
Box2dRaycastCollisionDetector.Box2dRaycastCallback callback;
public Box2dRaycastCollisionDetector (World world) {
this(world, new Box2dRaycastCollisionDetector.Box2dRaycastCallback());
}
public Box2dRaycastCollisionDetector (World world, Box2dRaycastCollisionDetector.Box2dRaycastCallback callback) {
this.world = world;
this.callback = callback;
}
@Override
public boolean collides (Ray<Vector2> ray) {
return findCollision(null, ray);
}
@Override
public boolean findCollision (Collision<Vector2> outputCollision, Ray<Vector2> inputRay) {
callback.collided = false;
if (!inputRay.start.epsilonEquals(inputRay.end, MathUtils.FLOAT_ROUNDING_ERROR)) {
callback.outputCollision = outputCollision;
world.rayCast(callback, inputRay.start, inputRay.end);
}
return callback.collided;
}
public static class Box2dRaycastCallback implements RayCastCallback {
public Collision<Vector2> outputCollision;
public boolean collided;
public Box2dRaycastCallback () {
}
@Override
public float reportRayFixture (Fixture fixture, Vector2 point, Vector2 normal, float fraction) {
if (outputCollision != null) outputCollision.set(point, normal);
collided = true;
return fraction;
}
}
}

View File

@@ -0,0 +1,12 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.ai.pfa.Heuristic;
public class EuclidianHeuristic implements Heuristic<NavigationVertex> {
@Override
public float estimate(NavigationVertex start, NavigationVertex end) {
return start.pos.dst(end.pos);
}
}

View File

@@ -0,0 +1,59 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.math.Vector2;
import forge.adventure.stage.MapStage;
import forge.util.Aggregates;
public class MovementBehavior {
public float duration = 0.0f;
float x = 0.0f;
float y = 0.0f;
public String destination = "";
public float getX(){
return x;
}
public float getY(){
return y;
}
public float getDuration(){
return duration;
}
public Vector2 currentTargetVector;
public Vector2 getNextTargetVector(Vector2 currentPosition){
if (currentTargetVector != null) {
return currentTargetVector;
}
if (destination.isEmpty()) {
currentTargetVector = new Vector2(currentPosition);
} else {
if (destination.startsWith("r")) {
String[] randomWaypoints = destination.replaceAll("r", "").split("-");
if (randomWaypoints.length > 0) {
int selectedWaypoint = Integer.parseInt(Aggregates.random(randomWaypoints));
if (MapStage.getInstance().waypoints.containsKey(selectedWaypoint)) {
currentTargetVector = new Vector2(MapStage.getInstance().waypoints.get(selectedWaypoint));
}
}
else {
currentTargetVector = new Vector2(currentPosition);
}
} else if (destination.startsWith("w")) {
currentTargetVector = new Vector2(currentPosition);
duration = Float.parseFloat(destination.replaceAll("w", ""));
} else if (MapStage.getInstance().waypoints.containsKey(Integer.parseInt(destination))) {
currentTargetVector = new Vector2(MapStage.getInstance().waypoints.get(Integer.parseInt(destination)));
}
}
return currentTargetVector;
}
public void setX(float newVal){
x = newVal;
}
public void setY(float newVal){
y = newVal;
}
}

View File

@@ -0,0 +1,31 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.ai.pfa.Connection;
import com.badlogic.gdx.math.Vector2;
public class NavigationEdge implements Connection<NavigationVertex> {
NavigationVertex fromVertex;
NavigationVertex toVertex;
float cost;
public NavigationEdge(NavigationVertex from, NavigationVertex to) {
this.fromVertex = from;
this.toVertex = to;
cost = Vector2.dst(fromVertex.pos.x, fromVertex.pos.y, toVertex.pos.x, toVertex.pos.y);
}
@Override
public float getCost() {
return cost;
}
@Override
public NavigationVertex getFromNode() {
return fromVertex;
}
@Override
public NavigationVertex getToNode() {
return toVertex;
}
}

View File

@@ -0,0 +1,163 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.ai.pfa.Connection;
import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder;
import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class NavigationGraph implements IndexedGraph<NavigationVertex> {
private int lastNodeIndex = 0;
Map<Integer, NavigationVertex> nodes = new HashMap<>();
EuclidianHeuristic navigationHeuristic = new EuclidianHeuristic();
public NavigationVertex addVertex(NavigationVertex node){
node.index = lastNodeIndex;
lastNodeIndex++;
nodes.put(node.index,node);
return node;
}
public NavigationVertex addVertex(Vector2 position) {
return addVertex(new NavigationVertex(position));
}
public NavigationVertex addVertex(float x, float y) {
return addVertex(new NavigationVertex(x,y));
}
public void removeVertex(NavigationVertex node) {
for (NavigationVertex v : node.incomingEdges.keys()) {
v.removeEdges(node);
}
nodes.remove(node.index >=0? node.index: lookupIndex(node));
}
public void removeVertex(Vector2 position) {
removeVertex(getVertexByPosition(position));
}
public void removeVertex(float x, float y) {
removeVertex(new Vector2(x,y));
}
public void removeVertices(Collection<NavigationVertex> vertices) {
for (NavigationVertex v : vertices) {
removeVertex(v);
}
}
public void removeVertexIf(Predicate<NavigationVertex> predicate) {
removeVertices(nodes.values().stream().filter(predicate).collect(Collectors.toList()));
}
public int lookupIndex(NavigationVertex item) {
return lookupIndex(item.pos);
}
public int lookupIndex(Vector2 pos) {
for (int i : nodes.keySet())
if (nodes.get(i).pos.equals(pos)) return i;
return -1;
}
public void addEdge(NavigationVertex fromNode, NavigationVertex toNode) {
if (fromNode.index < 0) {
fromNode = getVertexByPosition(fromNode.pos);
}
if (toNode.index < 0) {
toNode = getVertexByPosition(toNode.pos);
}
if (edgeExists(fromNode, toNode)) {
System.out.println(fromNode.pos + " is already connected to " + toNode.pos);
return;
}
if (!(fromNode.index < 0) || toNode.index < 0) {
NavigationEdge fromAToB = new NavigationEdge(fromNode, toNode);
NavigationEdge fromBToA = new NavigationEdge(toNode, fromNode);
fromNode.outgoingEdges.put(toNode, fromAToB);
fromNode.incomingEdges.put(toNode, fromBToA);
toNode.outgoingEdges.put(fromNode, fromBToA);
toNode.incomingEdges.put(fromNode, fromAToB);
}
}
public void addEdge(Vector2 fromNode, NavigationVertex toNode) {
addEdge(new NavigationVertex(fromNode), toNode);
}
public void addEdge(NavigationVertex fromNode, Vector2 toNode) {
addEdge(fromNode, new NavigationVertex(toNode));
}
public void addEdgeUnchecked(NavigationVertex fromNode, NavigationVertex toNode) {
//Assumes that nodes are in graph, are not connected already, and have correct index
NavigationEdge fromAToB = new NavigationEdge(fromNode, toNode);
NavigationEdge fromBToA = new NavigationEdge(toNode, fromNode);
fromNode.outgoingEdges.put(toNode, fromAToB);
fromNode.incomingEdges.put(toNode, fromBToA);
toNode.outgoingEdges.put(fromNode, fromBToA);
toNode.incomingEdges.put(fromNode, fromAToB);
}
public int getIndex(NavigationVertex node) {
return node.index;
}
public int getNodeCount() {
return lastNodeIndex;
}
@Override
public Array<Connection<NavigationVertex>> getConnections(NavigationVertex fromNode) {
return fromNode.getAllConnections();
}
public boolean edgeExists(NavigationVertex fromNode, NavigationVertex toNode) {
if (fromNode.index < 0) {
fromNode = getVertexByPosition(fromNode.pos);
}
if (toNode.index < 0) {
toNode = getVertexByPosition(toNode.pos);
}
return fromNode.outgoingEdges.containsKey(toNode);
}
public Collection<NavigationVertex> getNodes() {
return nodes.values();
}
public ProgressableGraphPath<NavigationVertex> findPath(Vector2 origin, Vector2 destination) {
ProgressableGraphPath<NavigationVertex> navPath = new ProgressableGraphPath<>();
NavigationVertex originVertex = getVertexByPosition(origin);
NavigationVertex destinationVertex = getVertexByPosition(destination);
if (originVertex.index > -1 && destinationVertex.index > -1) {
new IndexedAStarPathFinder<>(this).searchNodePath(originVertex, destinationVertex, navigationHeuristic, navPath);
}
return navPath;
}
public NavigationVertex getVertexByPosition(Vector2 position) {
return nodes.get(lookupIndex(position));
}
public boolean containsNode(Vector2 nodePosition) {
return nodes.containsKey(lookupIndex(nodePosition));
}
}

View File

@@ -0,0 +1,201 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.*;
import com.badlogic.gdx.utils.Array;
import forge.adventure.stage.MapStage;
import java.util.ArrayList;
import java.util.Comparator;
public class NavigationMap {
float spriteSize = 16f;
boolean rayCollided = false;
public NavigationGraph navGraph = new NavigationGraph();
Array<Rectangle> navBounds = new Array<>();
float half = (spriteSize / 2);
public NavigationMap(float spriteSize) {
this.spriteSize = spriteSize;
this.half = spriteSize / 2;
}
RayCastCallback callback = new RayCastCallback() {
@Override
public float reportRayFixture(Fixture fixture, Vector2 vector2, Vector2 vector21, float v) {
if (v < 1.0)
rayCollided = true;
return 0;
}
};
public void initializeGeometryGraph() {
navGraph = new NavigationGraph();
for (int i = 0; i < MapStage.getInstance().collisionRect.size; i++) {
Rectangle r1 = MapStage.getInstance().collisionRect.get(i);
if (r1.width < 3 && r1.height < 3)
continue;
int offsetX = -8;
int offsetY = 0;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.StaticBody;
bodyDef.position.set(r1.x + r1.getWidth() / 2 + offsetX, r1.y + r1.getHeight() / 2 + offsetY);
Body body = MapStage.getInstance().gdxWorld.createBody(bodyDef);
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(((r1.getWidth() + spriteSize) / 2), ((r1.getHeight() + spriteSize) / 2));
FixtureDef fixture = new FixtureDef();
fixture.shape = polygonShape;
fixture.density = 1;
body.createFixture(fixture);
polygonShape.dispose();
}
float width = Float.parseFloat(MapStage.getInstance().tiledMap.getProperties().get("width").toString());
float height = Float.parseFloat(MapStage.getInstance().tiledMap.getProperties().get("height").toString());
float tileHeight = Float.parseFloat(MapStage.getInstance().tiledMap.getProperties().get("tileheight").toString());
float tileWidth = Float.parseFloat(MapStage.getInstance().tiledMap.getProperties().get("tilewidth").toString());
NavigationVertex[][] points = new NavigationVertex[(int)width][(int)height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
points[i][j] = navGraph.addVertex(i* tileWidth + (tileWidth/ 2), j*tileHeight + (tileHeight/ 2));
if (i > 0) {
navGraph.addEdgeUnchecked(points[i][j],points[i-1][j]);
}
if (j > 0) {
navGraph.addEdgeUnchecked(points[i][j],points[i][j-1]);
}
if (i > 0 && j > 0) {
navGraph.addEdgeUnchecked(points[i][j],points[i-1][j-1]);
}
if (i > 0 && j + 1 < height) {
navGraph.addEdgeUnchecked(points[i][j],points[i-1][j+1]);
}
//remaining connections will be added by subsequent nodes
}
}
Array<Fixture> fixtures = new Array<>();
if (MapStage.getInstance().gdxWorld != null) {
MapStage.getInstance().gdxWorld.getFixtures(fixtures);
for (Fixture fix : fixtures) {
navGraph.removeVertexIf(vertex -> fix.testPoint(vertex.pos));
}
}
navGraph.removeVertexIf(v -> navGraph.getConnections(v).isEmpty());
//Add additional vertices for map waypoints
for (Vector2 waypointVector : MapStage.getInstance().waypoints.values()) {
NavigationVertex waypointVertex = navGraph.addVertex(waypointVector);
ArrayList<NavigationVertex> vertices = new ArrayList<>(navGraph.nodes.values());
vertices.sort(Comparator.comparingInt(o -> Math.round((o.pos.x - waypointVector.x) * (o.pos.x - waypointVector.x) + (o.pos.y - waypointVector.y) * (o.pos.y - waypointVector.y))));
for (int i = 0, j=0; i < vertices.size() && j < 4; i++) {
if (waypointVector.epsilonEquals(vertices.get(i).pos))
continue; //rayCast() crashes if params are equal
rayCollided = false;
MapStage.getInstance().gdxWorld.rayCast(callback, waypointVector, vertices.get(i).pos);
if (!rayCollided) {
navGraph.addEdgeUnchecked(waypointVertex, vertices.get(i));
j++;
}
}
}
}
public ProgressableGraphPath<NavigationVertex> findShortestPath(Float spriteSize, Vector2 origin, Vector2 destination) {
Array<Fixture> fixtures = new Array<>();
MapStage.getInstance().gdxWorld.getFixtures(fixtures);
boolean originPrecalculated = navGraph.containsNode(origin);
boolean destinationPrecalculated = navGraph.containsNode(destination);
try {
if (!originPrecalculated)
navGraph.addVertex(origin);
if (!destinationPrecalculated)
navGraph.addVertex(destination);
ArrayList<NavigationVertex> vertices = new ArrayList<>();
if (!(originPrecalculated && destinationPrecalculated)) {
vertices.addAll(navGraph.nodes.values());
vertices.sort(Comparator.comparingInt(o -> Math.round((o.pos.x - origin.x) * (o.pos.x - origin.x) + (o.pos.y - origin.y) * (o.pos.y - origin.y))));
}
if (!originPrecalculated) {
for (int i = 0, j=0; i < vertices.size() && j < 10; i++) {
if (origin.epsilonEquals(vertices.get(i).pos))
continue; //rayCast() crashes if params are equal
rayCollided = false;
MapStage.getInstance().gdxWorld.rayCast(callback, origin, vertices.get(i).pos);
if (!rayCollided) {
navGraph.addEdge(origin, vertices.get(i));
j++;
}
}
}
if (!destinationPrecalculated) {
for (int i = 0, j=0; i < vertices.size() && j < 10; i++) {
if (destination.epsilonEquals(vertices.get(i).pos))
continue; //shouldn't happen, but would crash during rayCast if it did
rayCollided = false;
MapStage.getInstance().gdxWorld.rayCast(callback, vertices.get(i).pos, destination);
if (!rayCollided) {
navGraph.addEdge(destination, vertices.get(i));
j++;
}
}
}
ProgressableGraphPath<NavigationVertex> shortestPath = navGraph.findPath(origin, destination);
if (false) { //todo - re-evaluate. 8-way node links may be smooth enough to skip the extra raycast overhead
//Trim path by cutting any unnecessary nodes
for (int i = 0; i < shortestPath.getCount(); i++) {
for (int j = shortestPath.getCount() - 1; j > i + 1; j--) {
rayCollided = false;
MapStage.getInstance().gdxWorld.rayCast(callback, shortestPath.get(i).pos, shortestPath.get(j).pos);
if (!rayCollided) {
shortestPath.remove(j - 1);
i = 0;
j = shortestPath.getCount();
}
}
}
}
if (!originPrecalculated)
navGraph.removeVertex(origin);
if (!destinationPrecalculated)
navGraph.removeVertex(destination);
return shortestPath;
}
catch(Exception e){
if (!originPrecalculated && navGraph.lookupIndex(origin) > -1)
navGraph.removeVertex(origin);
if (!destinationPrecalculated && navGraph.lookupIndex(destination) > -1)
navGraph.removeVertex(destination);
throw(e);
}
}
}

View File

@@ -0,0 +1,43 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.ai.pfa.Connection;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
public class NavigationVertex {
public Vector2 pos = Vector2.Zero;
public ObjectMap<NavigationVertex, NavigationEdge> incomingEdges = new ObjectMap<>();
public ObjectMap<NavigationVertex, NavigationEdge> outgoingEdges = new ObjectMap<>();
int index = -1;
public NavigationVertex(Vector2 position) {
pos = position;
}
public NavigationVertex(float x, float y) {
pos = new Vector2(x, y);
}
public boolean hasEdgeTo(NavigationVertex otherNode) {
return incomingEdges.containsKey(otherNode);
}
public Array<Connection<NavigationVertex>> getAllConnections() {
Array<Connection<NavigationVertex>> ret = new Array<>();
for (NavigationEdge e : incomingEdges.values()) {
ret.add(e);
}
for (NavigationEdge e : outgoingEdges.values()) {
ret.add(e);
}
return ret;
}
public void removeEdges(NavigationVertex node) {
outgoingEdges.remove(node);
incomingEdges.remove(node);
}
}

View File

@@ -0,0 +1,57 @@
package forge.adventure.util.pathfinding;
import com.badlogic.gdx.ai.pfa.GraphPath;
import com.badlogic.gdx.utils.Array;
import java.util.Iterator;
public class ProgressableGraphPath<N> implements GraphPath<N> {
public final Array<N> nodes;
/** Creates a {@code DefaultGraphPath} with no nodes. */
public ProgressableGraphPath () {
this(new Array<N>());
}
/** Creates a {@code DefaultGraphPath} with the given capacity and no nodes. */
public ProgressableGraphPath (int capacity) {
this(new Array<N>(capacity));
}
/** Creates a {@code DefaultGraphPath} with the given nodes. */
public ProgressableGraphPath (Array<N> nodes) {
this.nodes = nodes;
}
@Override
public void clear () {
nodes.clear();
}
@Override
public int getCount () {
return nodes.size;
}
@Override
public void add (N node) {
nodes.add(node);
}
@Override
public N get (int index) {
return nodes.get(index);
}
@Override
public void reverse () {
nodes.reverse();
}
@Override
public Iterator<N> iterator () {
return nodes.iterator();
}
public void remove (int index) { nodes.removeIndex(index);}
}

View File

@@ -0,0 +1,36 @@
[metadata]
Name=Archivist
[Main]
3 Ancient Den|BRC|1
4 Archive Trap|PLIST|1
3 Archivist|ULG|1
2 Archivist of Oghma|CLB|2
2 Automatic Librarian|DMU|1
4 Bury in Books|J22|1
4 Compulsive Research|CLB|1
4 Field Research|ZNR|1
3 Geist of the Archives|EMN|1
2 Ghost Quarter|SLD|1
2 Island|JMP|1
1 Island|JMP|3
1 Island|JMP|4
1 Island|JMP|5
2 Island|JMP|7
4 Jace's Archivist|M12|1
2 Key to the Archive|YMID|1
3 Magus of the Moat|FUT|1
2 Oath of Lieges|EXO|1
2 Ormos, Archive Keeper|JMP|1
4 Overwhelmed Archivist|MID|1
1 Plains|JMP|1
1 Plains|JMP|7
1 Preston, the Vanisher|J22|1
4 Razortide Bridge|BRC|1
4 Seat of the Synod|J22|1
2 Temple of Enlightenment|SCD|1
1 Tolarian Academy|VMA|1
1 Tome of the Infinite|J21|1
4 Walking Archive|PLIST|1
2 Winds of Abandon|PLIST|1
[Sideboard]
1 Wizard's Spellbook|AFR|1

View File

@@ -0,0 +1,24 @@
[metadata]
Name=Golem Sentinel
[Main]
4 Amaranthine Wall|DOM|1
4 Consulate Skygate|BBD|1
4 Living Wall|30A|1
3 Plains|DMU|1
7 Plains|DMU|2
6 Plains|DMU|3
5 Plains|DMU|4
4 Rolling Stones|STH|1
4 Secluded Steppe|J21|1
4 Shield-Wall Sentinel|DMU|1
4 Stalwart Shield-Bearers|ROE|1
4 Steel Wall|TD2|1
1 Sunweb|MIR|1
4 Walking Bulwark|DMU|1
4 Wall of Junk|DMR|1
4 Wall of Spears|DPA|1
1 Wall of Swords|30A|1
4 Weathered Sentinels|NCC|1
4 Wingmantle Chaplain|DMU|1
[Sideboard]

View File

@@ -0,0 +1,20 @@
[metadata]
Name=Golem Sentinel 2
[Main]
4 Buried Ruin|ONC|1
4 Chief of the Foundry|BRC|1
4 Chronomaton|DDM|1
4 Cryptic Caves|M20|1
4 Darksteel Citadel|BRC|1
4 Desert|AFC|1
4 Haunted Guardian|AVR|1
4 Lightning-Core Excavator|J21|1
4 Locthwain Gargoyle|J22|1
4 Patchwork Automaton|NEO|1
4 Steel Overseer|J22|1
4 Urza's Saga|MH2|1
4 Walking Ballista|J22|1
4 Wall of Forgotten Pharaohs|AKR|1
4 Wall of Junk|DMR|1
[Sideboard]

View File

@@ -0,0 +1,20 @@
[metadata]
Name=Golem Sentinel 3
[Main]
4 Buried Ruin|ONC|1
4 Chief of the Foundry|BRC|1
4 Chronomaton|DDM|1
4 Cryptic Caves|M20|1
4 Custodian of the Trove|DTK|1
4 Darksteel Citadel|BRC|1
4 Desert|AFC|1
4 Haunted Guardian|AVR|1
4 Lightning-Core Excavator|J21|1
4 Locthwain Gargoyle|J22|1
4 Patchwork Automaton|NEO|1
4 Steel Overseer|J22|1
4 Urza's Saga|MH2|1
4 Walking Ballista|J22|1
4 Wall of Forgotten Pharaohs|AKR|1
[Sideboard]

View File

@@ -0,0 +1,34 @@
[metadata]
Name=Pirate 2
[Main]
2 Abrade|SCD|1
2 Admiral Beckett Brass|PLIST|1
4 Aether Hub|KLR|1
1 Aethersphere Harvester|KLR|1
1 Canyon Slough|AKR|1
2 Captain Lannery Storm|J22|1
1 Censor|J21|1
3 Chandra, Torch of Defiance|Q06|1
4 Deadeye Tracker|XLN|1
4 Dire Fleet Captain|XLN|1
2 Dire Fleet Ravager|XLN|1
3 Dragonskull Summit|DMC|1
2 Dreamcaller Siren|XLN|1
3 Drowned Catacomb|SLD|1
4 Fatal Push|F17|1
3 Fathom Fleet Captain|XLN|1
2 Fell Flagship|XLN|1
1 Fetid Pools|AKR|1
2 Fiery Cannonade|CMR|1
3 Island|LTR|1
3 Jace, Cunning Castaway|PS18|1
3 Kari Zev, Skyship Raider|J22|1
2 Lightning-Rig Crew|CMR|1
3 Lookout's Dispersal|J22|1
2 March of the Drowned|XLN|1
2 Mountain|WHO|1
4 Spirebluff Canal|KLR|1
3 Swamp|LTR|1
4 Unlicensed Disintegration|KLD|1
[Sideboard]

View File

@@ -0,0 +1,29 @@
[metadata]
Name=Pirate 3
[Main]
4 Captain Lannery Storm|J22|1
2 Captivating Crew|XLN|1
4 Chart a Course|JMP|1
2 Daring Saboteur|NCC|1
3 Dire Fleet Hoarder|2XM|1
4 Dragonskull Summit|DMC|1
2 Dreamcaller Siren|XLN|1
4 Drowned Catacomb|SLD|1
2 Evolving Wilds|SIS|1
4 Fatal Push|F17|1
4 Fathom Fleet Captain|XLN|1
2 Fell Flagship|XLN|1
2 Island|LTR|1
4 Kitesail Freebooter|XLN|1
4 Lookout's Dispersal|J22|1
2 Mountain|WHO|1
4 Negate|MOM|1
4 Ruin Raider|XLN|1
4 Siren Stormtamer|CMR|1
4 Spirebluff Canal|KLR|1
2 Swamp|LTR|1
4 Tezzeret the Schemer|KLR|1
2 Walk the Plank|XLN|1
2 Wanted Scoundrels|XLN|1
[Sideboard]

View File

@@ -0,0 +1,29 @@
[metadata]
Name=Pirate Captain 2
[Main]
1 Blood Money|CLB|2
2 Deadeye Tracker|XLN|1
4 Deadly Derision|MOM|1
4 Desperate Castaways|MB1|1
2 Dire Fleet Hoarder|2XM|1
2 Dire Fleet Interloper|XLN|1
2 Dire Fleet Poisoner|J21|1
1 Dire Fleet Ravager|XLN|1
4 Drain Life|5ED|1
4 Fathom Fleet Captain|XLN|1
2 Sleek Schooner|XLN|1
2 Kitesail Freebooter|XLN|1
2 March of the Drowned|XLN|1
1 Marut|CLB|1
4 Pirate's Cutlass|CMR|1
1 Pitiless Plunderer|RIX|1
2 Reckoner Bankbuster|NEO|1
1 Revel in Riches|XLN|1
6 Swamp|XLN|1
8 Swamp|XLN|2
2 Swamp|XLN|3
4 Swamp|XLN|4
1 Treasure Chest|AFR|3
1 Treasure Vault|AFR|1
2 Undercity Scrounger|NEO|1
[Sideboard]

View File

@@ -0,0 +1,25 @@
[metadata]
Name=Spirit
[Main]
2 Dreamshackle Geist|DBL|1
4 Dungeon Geists|DKA|1
4 Ephara's Dispersal|MOM|1
2 Geist of the Archives|EMN|1
10 Island|MOM|1
4 Island|MOM|2
4 Island|MOM|3
1 Kira, Great Glass-Spinner|JMP|1
4 Lantern Bearer|DBL|1
1 Latch Seeker|AVR|1
2 Mirrorhall Mimic|DBL|1
1 Murmuring Phantasm|JMP|1
4 Patrician Geist|DBL|1
4 Shriekgeist|IMA|1
4 Sinister Sabotage|SCD|1
1 Stormbound Geist|DKA|1
4 Supreme Phantom|M19|1
2 Think Tank|ODY|1
4 Thoughtbound Phantasm|GRN|1
4 Tocasia's Dig Site|BRO|1
[Sideboard]

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="8">
<layer id="1" name="Background" width="30" height="20">
<data encoding="base64" compression="zlib">
eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==
</data>
</layer>
<layer id="2" name="Ground" width="30" height="20">
<data encoding="base64" compression="zlib">
eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==
</data>
</layer>
<layer id="3" name="Walls" width="30" height="20">
<properties>
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==
</data>
</layer>
<layer id="4" name="Overlay" width="30" height="20">
<data encoding="base64" compression="zlib">
eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==
</data>
</layer>
<objectgroup id="5" name="Objects"/>
<objectgroup id="6" name="Waypoints">
<object id="1" template="../obj/collision.tx" x="-32" y="0" width="32" height="320"/>
<object id="2" template="../obj/collision.tx" x="-32" y="320" width="544" height="32"/>
<object id="3" template="../obj/collision.tx" x="480" y="0" width="32" height="320"/>
<object id="4" template="../obj/collision.tx" x="-32" y="-32" width="544" height="32"/>
<object id="5" template="../obj/waypoint.tx" name="5" x="224" y="32"/>
<object id="6" x="-64" y="-96" width="608" height="64">
<text wrap="1">See https://github.com/Card-Forge/forge/wiki/Create-new-Maps for instructions &amp; tips</text>
</object>
<object id="7" template="../obj/entry_up.tx" x="96" y="320"/>
</objectgroup>
</map>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="60" height="61" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="184"> <map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="60" height="61" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="188">
<properties> <properties>
<property name="canFailDungeon" type="bool" value="true"/> <property name="canFailDungeon" type="bool" value="true"/>
<property name="dungeonEffect">{ <property name="dungeonEffect">{
@@ -937,7 +937,7 @@
</property> </property>
<property name="spawn.Easy" type="bool" value="true"/> <property name="spawn.Easy" type="bool" value="true"/>
<property name="threatRange" type="int" value="30"/> <property name="threatRange" type="int" value="30"/>
<property name="waypoints" value=""/> <property name="waypoints" value="184,r185-186,186"/>
</properties> </properties>
</object> </object>
<object id="54" template="../../obj/manashards.tx" x="568.5" y="927.5"/> <object id="54" template="../../obj/manashards.tx" x="568.5" y="927.5"/>
@@ -1560,6 +1560,10 @@
<object id="120" template="../../obj/waypoint.tx" x="263.667" y="463"/> <object id="120" template="../../obj/waypoint.tx" x="263.667" y="463"/>
<object id="166" template="../../obj/waypoint.tx" x="784" y="887.45"/> <object id="166" template="../../obj/waypoint.tx" x="784" y="887.45"/>
<object id="167" template="../../obj/waypoint.tx" x="734.545" y="794.73"/> <object id="167" template="../../obj/waypoint.tx" x="734.545" y="794.73"/>
<object id="168" template="../../obj/waypoint.tx" x="751.273" y="886.55"/> <object id="168" template="../../obj/waypoint.tx" x="751.273" y="886.55" visible="0"/>
<object id="184" template="../../obj/waypoint.tx" x="256" y="512"/>
<object id="185" template="../../obj/waypoint.tx" x="224" y="480"/>
<object id="186" template="../../obj/waypoint.tx" x="192" y="512"/>
<object id="187" template="../../obj/waypoint.tx" x="224" y="576"/>
</objectgroup> </objectgroup>
</map> </map>

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="40" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="20">
<tileset firstgid="1" source="../../tileset/main.tsx"/>
<tileset firstgid="10113" source="../../tileset/rivers.tsx"/>
<tileset firstgid="27393" source="../../tileset/main-nocollide.tsx"/>
<tileset firstgid="37505" source="../../tileset/dungeon.tsx"/>
<tileset firstgid="41985" source="../../tileset/FarmFood.tsx"/>
<layer id="1" name="Background" width="30" height="40">
<data encoding="base64" compression="zlib">
eJyLZWFgiB3Fo3gUj+JRjBMnWgyMvaKWDAwSQHzecuDsPz1AdjdZDVy4g+yGhfm0Wdgxuh5i1RHCT61xm4dLD6X2ngP6NXkAwhrmV2ScxomK8cmhyxPr12YrwvYSwqTaa4jFrzB7J7ATh4m1F5Zvkiyw+5VW9oLKC0lLhgZ8aYkW9hKDR+2ln720zEejeBSP4lE8ikcxAMAE71k=
</data>
</layer>
<layer id="2" name="Ground" width="30" height="40">
<data encoding="base64" compression="zlib">
eJztlktOwzAQhr2gaQVsOQewQ+otWPJq98AdvOEAOE1bNSycBQgOE9hUPMqGozDFNh3cOH61pUj80q82ztjfTOyMQshMbGR2Cu6BM3AfPAAPwTu5+GVoPJPxfc0uYoLDhx45ZHK858nSlQluV2cqqxoxJ0VxoYJ1+EDjMlEbhXEK9ynkRoFLgUOZGPt+1qGC+bwvuc8Vz1sxXuX1ANXbi+BCXRzy7gbk+8UOlcw7mnvrMAfHyPPzcQ3vCgOnudg3dX6H8r8yk3vMIuvFUvup9vEF8VJxrqxnuBXJNnkVOi0I6RRxa1xtE3JACF01N0Q+3GZNTROPeid8xn3nrrP8dMzn7RJzEpBPOzDHGI3R897dIGRPs9JhIaykx+2jWBe1EbeEuY+aTdLjngyxdxVjY+1acR8SQi4awlNNip+/U6n790k910Ulyv2IV58f/SyVlnp9uWfcHt/5Y9y3X+LqWgbXpbWuQ70jWDO3+KaG27Sw8H21xlYy349M3lzg+xviGO55Y9aHsFVP0nsZ9mUjnGtS6VmXy3esSbif+3IXJRM39Ds1lrtItSq+u+q4MXtp07rtr60nLYu7bP1z/fUJ3KlgJA==
</data>
</layer>
<layer id="3" name="Walls" width="30" height="40">
<properties>
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJzdld9OE0EUxmcv4a6F/qH/0Cvju5gYjZqAAone+AAlQRSF6iOgie2WtvRWo2JpLTa+g6RaLDwDGn0Bv+OeyR7b2W67hV7wJV92ujszvznnzExjJaVmikp9g492lTquOn6JNilfVSPL5jHbYr422m0wwnCs5PbV7MOCY1oDsYNyiUksPZ9mmkTssO2Y1kDMoFxyW8znxdTSOZg5A65mybwOI2J2RI2kk3DK8J58uOvWOIj+cbHmDurzHf7KpnqlK0rNw23xnnxE34vjc2M7MOoThUPCS2CuVJzahWy3jnGuZRCurke32n+uBkmem5Pq/3ONoo7Iqz5Xg5jy3FC+B+lySG16fYuJHPbeK73W90JI5NtPYPtK3ysU/y3b3Vd3bP97IYh67zSKZ1twX+mcFM1jhtUVxH51QPx6Ts0NytnLKlXLur8fhJV6GPYft1NWqlQenad1uqrUn1Xzt0XDvF28SyDOm+wkfByAT/HW4Ua2/9sy5nsRc9pb/FwAZ05wT9DnHuc7x32e47nis5b34N1AvLcNMS8bxhJzES6UHd9FO1Xp7+fHvQbedfjXkNzXYOThBM5rEi6gbQfgUqwfevaWvktM3B9gVKmm8H24S7kOwP0J7m+PvWXiDis/LumNYU9Nguuli8p9O6XUu6nJc0+ncY6mJ8/10nlzvf77L2q8XsqX3XtYeknMueDRxx6D6yWZh6BxbcHr3F6DH8HPzoG7yXMTLwfPW0o94XYG7Rh8yRqfuyEYpCjmnIUP4C/8jMB7cAPehz+fAZdiSMCfLIf/WDmMddGnhd9zQ8Q5CrfGcTU5tn1mxJmha5nkfPvVdlguiXJNNdUxNjn+p8qtbY3XQ7nIeU00ApeYlFvaL2nL+U28j5z/CMdO7xvMTlvuWFP8ftw1Zs6KWOh3StQ8yhz9zHCdN3htppoP4kom1bfGtY1YbuwZfk/7l/ZW3XLqHOen15kaxE1aLlPmKyq4LcOceh1NXmd9RG6LmaQmj9cxH4hvfwG8OnNz
</data>
</layer>
<layer id="7" name="Shading" width="30" height="40" opacity="0.55">
<data encoding="base64" compression="zlib">
eJztw0EJADAMBLCanLsam5s9TsKgj5JAqgB2O53TbicAAAB/HhT3BM0=
</data>
</layer>
<layer id="4" name="Overlay" width="30" height="40">
<data encoding="base64" compression="zlib">
eJxjYBgcYPq8gTHzOpqasqkMDOVTibcDm3p0M4kB24BmbCfBXlLVDwZwg4pxTEp6ISc+6GHWQAD09EpqeicWoMc1enrFln6pUQagxw85ZQA1ADYzqZn+6Ql8lg20C+gPWFcMtAvwg8AFDAyzFwy0K4YfeLSQPDliALH5SGQBZfbAgDuRabgHj7uo5RZ0ILiAoQGZL0Qje4gFwnSwH58dnTQqY0Px2AkDYUSoGQWjYBSMglEwCgYTAADYSDkG
</data>
</layer>
<objectgroup id="5" name="Objects">
<object id="13" template="../../obj/entry_down.tx" x="224" y="184" width="32" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_1.tmx"/>
<property name="teleportObjectId" value="7"/>
</properties>
</object>
<object id="14" template="../../obj/portal.tx" x="144" y="256">
<properties>
<property name="portalState" value="inactive"/>
<property name="sprite" value="sprites/portal3.atlas"/>
<property name="teleport" value="../common/maps/map/main_story/main_story_explore/library_of_varsil_1.tmx"/>
</properties>
</object>
</objectgroup>
<objectgroup id="6" name="Waypoints">
<object id="1" template="../../obj/collision.tx" x="-32" y="0" width="144" height="640"/>
<object id="2" template="../../obj/collision.tx" x="-32" y="640" width="544" height="32"/>
<object id="3" template="../../obj/collision.tx" x="368" y="0" width="144" height="640"/>
<object id="4" template="../../obj/collision.tx" x="-32" y="-32" width="544" height="32"/>
<object id="5" template="../../obj/waypoint.tx" name="5" x="336" y="288"/>
<object id="7" template="../../obj/entry_up.tx" x="176" y="640" width="128" height="16"/>
<object id="8" template="../../obj/waypoint.tx" name="8" x="128" y="288"/>
<object id="9" template="../../obj/waypoint.tx" name="9" x="232" y="448"/>
<object id="10" template="../../obj/enemy.tx" x="128" y="336">
<properties>
<property name="enemy" value="Archivist"/>
<property name="pursueRange" type="int" value="60"/>
<property name="threatRange" type="int" value="30"/>
<property name="waypoints" value="r5-8-9"/>
</properties>
</object>
<object id="11" template="../../obj/enemy.tx" x="336" y="336">
<properties>
<property name="enemy" value="Archivist"/>
<property name="pursueRange" type="int" value="60"/>
<property name="threatRange" type="int" value="30"/>
<property name="waypoints" value="r5-8-9"/>
</properties>
</object>
<object id="12" template="../../obj/enemy.tx" x="232" y="232">
<properties>
<property name="enemy" value="Archivist"/>
</properties>
</object>
<object id="15" template="../../obj/collision.tx" x="112" y="0" width="256" height="160"/>
<object id="16" template="../../obj/collision.tx" x="112" y="464" width="96" height="176"/>
<object id="17" template="../../obj/collision.tx" x="256" y="160" width="112" height="64"/>
<object id="18" template="../../obj/collision.tx" x="112" y="160" width="112" height="64"/>
<object id="19" template="../../obj/collision.tx" x="272" y="464" width="96" height="176"/>
</objectgroup>
</map>

View File

@@ -0,0 +1,244 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="45">
<tileset firstgid="1" source="../../tileset/main.tsx"/>
<tileset firstgid="10113" source="../../tileset/buildings.tsx"/>
<layer id="1" name="Background" width="30" height="20">
<data encoding="base64" compression="zlib">
eJzjVWdg4B3Fo3gUj+JRPIpH8SgexVTDAA08eeE=
</data>
</layer>
<layer id="2" name="Ground" width="30" height="20">
<data encoding="base64" compression="zlib">
eJxjYBhaYIsEBNMbbJeAYHoDeQEGBkUB6ph1EGjOISJxOz9DgqoAQwKx6g9TyY3U9O9A2dvIz8DQRCRWAdqpLEC8+mZ+/H5QoBHGFzZ7gXL7iMRtQPe38hOvfv9ouoKD0fxLH3tHwxk74FVH2Ati0wvA6t6BqoMpAQDikGk6
</data>
</layer>
<layer id="3" name="Walls" width="30" height="20">
<properties>
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJwz4WNgMBlCGAYoNccciC8BsbIKA4OqCoQG4Ut82DEMXELShwurQs0xw2KvKdSsgyr4MQgsF0XY2yJOvD5jPPbSEuCzFxQuSmjhhM5HB+jy6HGlRIR/QWFyAC2M0PnoAF0eHR8exOHMooZQh4uNCxDSOxj9i56WVCHsBnzpCkeexdCDz15GKfyYSQrTXmL1DMZwVsUSZugYHaDn9WRJzDxMyF5c+Q9f/sWV1w+rDP5ykhBoFMcUa8YiRqy9M3kZGKYRidEBsfpm8FKnzoeBgW6DkIIBPQOT6g==
</data>
</layer>
<layer id="4" name="Overlay" width="30" height="20">
<data encoding="base64" compression="zlib">
eJztw7ENAAAIA6D+ov/f6NrVHRIS2m4yFQDg6wBDvwEe
</data>
</layer>
<objectgroup id="5" name="Objects">
<object id="25" template="../../obj/dialog.tx" x="192" y="200">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Anatomical studies of various mundane animals are found on this shelf. Open on top is a text on snakes. Why did it have to be snakes?&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="26" template="../../obj/dialog.tx" x="128" y="200">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;This shelf contains books of recipes, but many of the ingredients are unfamiliar to you.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="27" template="../../obj/dialog.tx" x="352" y="200">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Here you find not so much books as collections of paperwork. It appears to consist of a series of caravan manifests and shipping ledgers affixed to maps. Comments and questions scribbled onto a set of calendars indicate that whomever collected these records suspected that the associated merchants were frequently conducting additional undocumented business along their routes.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="28" template="../../obj/dialog.tx" x="400" y="200">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;'The Impact of Personal Teleportation on Intercity Commerce' - Doesn't exactly sound like a page turner.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="29" template="../../obj/dialog.tx" x="64" y="240">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Here you find a collection of copied plans for various goblin inventions. The transcriber added notes alongside each rating each on a scale between 'Will definitely explode' to 'Won't work well enough to explode'.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="30" template="../../obj/dialog.tx" x="144" y="240">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Whatever knowledge was once contained in these texts is lost, their pages long ago having been soaked in the same dark liquid that appears to have stained the shelves below them.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="31" template="../../obj/dialog.tx" x="368" y="240">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Dust covered tomes of nondescript subjects fill this shelf. You have a feeling these have been untouched for quite some time.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="32" template="../../obj/dialog.tx" x="352" y="112">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;This shelf holds a journal with a sturdy cover which stands apart from the faded scrolls around it. The entries are a mystery to you, however, as they are written in some form of code.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="33" template="../../obj/dialog.tx" x="400" y="112">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;'Azgothian poems from a Midsummer Morning'. This book gives you a creepy feeling and you wisely leave the cover closed.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="34" template="../../obj/dialog.tx" x="400" y="64">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Here you find a collection of biographies of wizards you've never heard of, and a quick skim of the contents makes you wonder who would bother to write these.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="35" template="../../obj/dialog.tx" x="128" y="64">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;A collection of children's drawings fill this bookcase, although unlike most similar collections these all seem to depict arcane subjects.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="36" template="../../obj/dialog.tx" x="80" y="64">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Here you find a blueprint of the library itself. Unfortunately, there doesn't seem to be any list of secret passages to take advantage of.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="39" template="../../obj/enemy.tx" x="232" y="112">
<properties>
<property name="enemy" value="Spirit"/>
<property name="pursueRange" type="int" value="100"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="r5-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-37-38"/>
</properties>
</object>
<object id="40" template="../../obj/enemy.tx" x="88" y="144">
<properties>
<property name="enemy" value="Golem"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="r5-8-15,r18-21,r11-12-13-16-18-21"/>
</properties>
</object>
<object id="41" template="../../obj/enemy.tx" x="384" y="144">
<properties>
<property name="enemy" value="Golem"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="r5-8-15,r19-20,r19-20-37-38-14-17"/>
</properties>
</object>
<object id="42" template="../../obj/enemy.tx" x="384" y="256">
<properties>
<property name="enemy" value="Archivist"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="r10-14-23,r19-20,r19-20-37-38,r8-10-14-18-21"/>
</properties>
</object>
<object id="43" template="../../obj/enemy.tx" x="80" y="256">
<properties>
<property name="enemy" value="Archivist"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="22, r11-12, r18-21, 5, r8-15, r18-21, r11-12, r9-13"/>
</properties>
</object>
<object id="44" template="../../obj/enemy.tx" x="232" y="56">
<properties>
<property name="enemy" value="Archivist"/>
</properties>
</object>
</objectgroup>
<objectgroup id="6" name="Waypoints">
<object id="1" template="../../obj/collision.tx" x="-32" y="0" width="32" height="320"/>
<object id="2" template="../../obj/collision.tx" x="-32" y="320" width="544" height="32"/>
<object id="3" template="../../obj/collision.tx" x="480" y="0" width="32" height="320"/>
<object id="4" template="../../obj/collision.tx" x="-32" y="-32" width="544" height="32"/>
<object id="5" template="../../obj/waypoint.tx" name="5" x="232" y="144"/>
<object id="7" template="../../obj/entry_up.tx" x="208" y="304" width="64" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_0.tmx"/>
<property name="teleportObjectId" value="13"/>
</properties>
</object>
<object id="8" template="../../obj/waypoint.tx" name="8" x="232" y="208"/>
<object id="9" template="../../obj/waypoint.tx" name="9" x="48" y="208"/>
<object id="10" template="../../obj/waypoint.tx" name="10" x="416" y="208"/>
<object id="11" template="../../obj/waypoint.tx" name="11" x="48" y="128"/>
<object id="12" template="../../obj/waypoint.tx" name="12" x="48" y="160"/>
<object id="13" template="../../obj/waypoint.tx" name="13" x="48" y="80"/>
<object id="14" template="../../obj/waypoint.tx" name="14" x="416" y="80"/>
<object id="15" template="../../obj/waypoint.tx" name="15" x="232" y="80"/>
<object id="16" template="../../obj/waypoint.tx" name="16" x="48" y="256"/>
<object id="17" template="../../obj/waypoint.tx" name="17" x="416" y="256"/>
<object id="18" template="../../obj/waypoint.tx" name="18" x="130" y="158"/>
<object id="19" template="../../obj/waypoint.tx" name="19" x="334" y="158"/>
<object id="20" template="../../obj/waypoint.tx" name="20" x="334" y="128"/>
<object id="21" template="../../obj/waypoint.tx" name="21" x="130" y="128"/>
<object id="22" template="../../obj/waypoint.tx" name="22" x="128" y="256">
<properties>
<property name="type" value="waypoint"/>
</properties>
</object>
<object id="23" template="../../obj/waypoint.tx" name="23" x="336" y="256"/>
<object id="24" template="../../obj/entry_down.tx" x="224" y="32" width="32" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_2.tmx"/>
<property name="teleportObjectId" value="7"/>
</properties>
</object>
<object id="37" template="../../obj/waypoint.tx" name="37" x="416" y="128"/>
<object id="38" template="../../obj/waypoint.tx" name="38" x="416" y="160"/>
</objectgroup>
</map>

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="48">
<tileset firstgid="1" source="../../tileset/main.tsx"/>
<tileset firstgid="10113" source="../../tileset/buildings.tsx"/>
<layer id="1" name="Background" width="30" height="20">
<data encoding="base64" compression="zlib">
eJzjVWdg4B3Fo3gUj+JRPIpH8SgexVTDAA08eeE=
</data>
</layer>
<layer id="2" name="Ground" width="30" height="20">
<data encoding="base64" compression="zlib">
eJxjYBhaYIsEBNMbbJeAYHoDeQEGBkUB6ph1EGjOISJxOz9DgqoAQwKx6g9TyY3U9O9A2dvIz8DQRCRWAdqpLEC8+mZ+/H5QoBHGFzZ7gXL7iMRtQPe38hOvfv9ouoKD0fxLH3tHwxk74FVH2Ati0wvA6t6BqoMpAQDikGk6
</data>
</layer>
<layer id="3" name="Walls" width="30" height="20">
<properties>
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJwz4WNgMBlCGAYoNccciC8BsbIKAquqoPJB+BJUHQxcwqIPHatC9ZlhsdcUatZBFfwYBJrEEfY2ihOvzxiPvbQE+OxVUmFowBdmyiqY5mGLD2QxJSL8e4BAeB3EYi8h9YM5nFnUEOr+SSLYyOK4AC69q0QJ20tLgM9e9PQBSmfo6QYdoKWnBmxsQvYySjEwMElBaHQME0cH2NSiY0L20hIQE87Y8iQx4QzKq8mSmPoJ2YsvDx4mIv+i539kPYMtnHNEGBiyRRDqkNkw0CKOKdaIJJYjgspGNgObvTN5GRimEYnRAbH6ZvBSp86HgYFug5CCARwpl9Q=
</data>
</layer>
<layer id="4" name="Overlay" width="30" height="20">
<data encoding="base64" compression="zlib">
eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==
</data>
</layer>
<objectgroup id="5" name="Objects">
<object id="25" template="../../obj/enemy.tx" x="80" y="272">
<properties>
<property name="enemy" value="Mimic"/>
</properties>
</object>
<object id="26" template="../../obj/dialog.tx" x="272" y="112"/>
<object id="27" template="../../obj/dialog.tx" x="352" y="112"/>
<object id="28" template="../../obj/dialog.tx" x="320" y="200">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;'Magical Meteorology', 'The Destructive Power of the Heavens', these books all seem to focus on things falling from the sky.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="29" template="../../obj/dialog.tx" x="368" y="200"/>
<object id="30" template="../../obj/dialog.tx" x="80" y="200"/>
<object id="31" template="../../obj/dialog.tx" x="144" y="200">
<properties>
<property name="dialog">[
{
&quot;text&quot;: &quot;Here you find a collection of biographies of wizards you've never heard of. None of them seem to have done anything significant.&quot;,
&quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
}
]</property>
</properties>
</object>
<object id="32" template="../../obj/dialog.tx" x="176" y="200"/>
<object id="34" template="../../obj/dialog.tx" x="400" y="64"/>
<object id="35" template="../../obj/dialog.tx" x="112" y="64"/>
<object id="36" template="../../obj/dialog.tx" x="96" y="240"/>
<object id="37" template="../../obj/dialog.tx" x="176" y="240"/>
<object id="38" template="../../obj/dialog.tx" x="352" y="240"/>
<object id="39" template="../../obj/dialog.tx" x="384" y="240"/>
<object id="40" template="../../obj/enemy.tx" x="232" y="160">
<properties>
<property name="enemy" value="Golem Sentinel"/>
<property name="pursueRange" type="int" value="60"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="r5-8-15-18-19-20-21,r5-8-15-18-19-20-21-22-23"/>
</properties>
</object>
<object id="41" template="../../obj/enemy.tx" x="48" y="224">
<properties>
<property name="enemy" value="Black Golem"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="22,r11-12,r18-21,5,r8-15, r18-21, r11-12,r9-13"/>
</properties>
</object>
<object id="42" template="../../obj/enemy.tx" x="416" y="224">
<properties>
<property name="enemy" value="Black Golem"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="23,r45-46,r19-20,r8-15,r19-20,r45-46,r10-14"/>
</properties>
</object>
<object id="43" template="../../obj/enemy.tx" x="232" y="48">
<properties>
<property name="enemy" value="Spirit"/>
<property name="spawn.Easy" type="bool" value="true"/>
</properties>
</object>
<object id="44" template="../../obj/enemy.tx" x="232" y="112">
<properties>
<property name="enemy" value="Spirit"/>
<property name="pursueRange" type="int" value="100"/>
<property name="spawn.Easy" type="bool" value="false"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="r5-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-45-46,w2"/>
</properties>
</object>
<object id="47" template="../../obj/treasure.tx" x="304" y="272"/>
</objectgroup>
<objectgroup id="6" name="Waypoints">
<object id="1" template="../../obj/collision.tx" x="-32" y="0" width="32" height="320"/>
<object id="2" template="../../obj/collision.tx" x="-32" y="320" width="544" height="32"/>
<object id="3" template="../../obj/collision.tx" x="480" y="0" width="32" height="320"/>
<object id="4" template="../../obj/collision.tx" x="-32" y="-32" width="544" height="32"/>
<object id="5" template="../../obj/waypoint.tx" name="5" x="232" y="144"/>
<object id="7" template="../../obj/entry_up.tx" x="208" y="304" width="64" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_1.tmx"/>
<property name="teleportObjectId" value="24"/>
</properties>
</object>
<object id="8" template="../../obj/waypoint.tx" name="8" x="232" y="208"/>
<object id="9" template="../../obj/waypoint.tx" name="9" x="48" y="208"/>
<object id="10" template="../../obj/waypoint.tx" name="10" x="416" y="208"/>
<object id="11" template="../../obj/waypoint.tx" name="11" x="48" y="128"/>
<object id="12" template="../../obj/waypoint.tx" name="12" x="48" y="160"/>
<object id="13" template="../../obj/waypoint.tx" name="13" x="48" y="80"/>
<object id="14" template="../../obj/waypoint.tx" name="14" x="416" y="80"/>
<object id="15" template="../../obj/waypoint.tx" name="15" x="232" y="80"/>
<object id="16" template="../../obj/waypoint.tx" name="16" x="48" y="256"/>
<object id="17" template="../../obj/waypoint.tx" name="17" x="416" y="256"/>
<object id="18" template="../../obj/waypoint.tx" name="18" x="130" y="158"/>
<object id="19" template="../../obj/waypoint.tx" name="19" x="334" y="158"/>
<object id="20" template="../../obj/waypoint.tx" name="20" x="334" y="128"/>
<object id="21" template="../../obj/waypoint.tx" name="21" x="130" y="128"/>
<object id="22" template="../../obj/waypoint.tx" name="22" x="128" y="256"/>
<object id="23" template="../../obj/waypoint.tx" name="23" x="336" y="256"/>
<object id="24" template="../../obj/entry_down.tx" x="224" y="32" width="32" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_3.tmx"/>
<property name="teleportObjectId" value="7"/>
</properties>
</object>
<object id="45" template="../../obj/waypoint.tx" name="45" x="416" y="160"/>
<object id="46" template="../../obj/waypoint.tx" name="46" x="416" y="128"/>
</objectgroup>
</map>

View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="49">
<tileset firstgid="1" source="../../tileset/main.tsx"/>
<tileset firstgid="10113" source="../../tileset/buildings.tsx"/>
<layer id="1" name="Background" width="30" height="20">
<data encoding="base64" compression="zlib">
eJzjVWdg4B3Fo3gUj+JRPIpH8SgexVTDAA08eeE=
</data>
</layer>
<layer id="2" name="Ground" width="30" height="20">
<data encoding="base64" compression="zlib">
eJxjYBhaYIsEBNMbbJeAYHoDeQEGBkUB6ph1EGjOISJxOz9DgqoAQwKx6g9TyY3U9O9A2dvIz8DQRCRWAdqpLEC8+mZ+/H5QoBHGFzZ7gXL7iMRtQPe38hOvfv9ouoKD0fxLH3tHwxk74FVH2Ati0wvA6t6BqoMpAQDikGk6
</data>
</layer>
<layer id="3" name="Walls" width="30" height="20">
<properties>
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJzVVlsOwiAQXI2/9c806WelXsCq10PjGYyewGtU7Qk4kRAgJSsL+Io4yQSa7DDphAXaKUD7R7R4d52NpJCcM4CG6dFHYeoshKOj2Bjd2uO7MmtdZE3P9OijwrYEbn335aALUWEZ8P0mQr4ql5oBx1nVzhyDytbVxnxVJh3KCH9jxDK+ZZzzZDHUUXMKMW2O/4v3ka+XMYh9xbEm5DuqwhxXj76pmhxzps5It6cxQudjas5U/6X2b+fRXZk+V3PMOYadcydYnGev+54KgEMiMVJ1x+Izd77Fr98gz/AOyNmTVw==
</data>
</layer>
<layer id="4" name="Overlay" width="30" height="20">
<data encoding="base64" compression="zlib">
eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ==
</data>
</layer>
<objectgroup id="5" name="Objects">
<object id="38" template="../../obj/enemy.tx" x="232" y="58">
<properties>
<property name="enemy" value="Golem Sentinel"/>
</properties>
</object>
<object id="39" template="../../obj/enemy.tx" x="192" y="144">
<properties>
<property name="enemy" value="Golem Sentinel"/>
<property name="pursueRange" type="int" value="60"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="r11-12-15-18-21, r8-19-20-43-44"/>
</properties>
</object>
<object id="40" template="../../obj/enemy.tx" x="272" y="144">
<properties>
<property name="enemy" value="Golem Sentinel"/>
<property name="pursueRange" type="int" value="60"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="r8-19-20-43-44, r11-12-15-18-21"/>
</properties>
</object>
<object id="41" template="../../obj/enemy.tx" x="424" y="144">
<properties>
<property name="enemy" value="Spirit"/>
<property name="pursueRange" type="int" value="100"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="r10-14-17-43-44,w10"/>
</properties>
</object>
<object id="42" template="../../obj/enemy.tx" x="40" y="144">
<properties>
<property name="enemy" value="Spirit"/>
<property name="pursueRange" type="int" value="100"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="r9-11-12-13-16,w10"/>
</properties>
</object>
<object id="45" template="../../obj/enemy.tx" x="232" y="184">
<properties>
<property name="enemy" value="Black Golem"/>
<property name="pursueRange" type="int" value="60"/>
<property name="threatRange" type="int" value="40"/>
<property name="waypoints" value="r8-15,w5,5,w5,r12-19-20-21,w5"/>
</properties>
</object>
</objectgroup>
<objectgroup id="6" name="Waypoints">
<object id="1" template="../../obj/collision.tx" x="-32" y="0" width="32" height="320"/>
<object id="2" template="../../obj/collision.tx" x="-32" y="320" width="544" height="32"/>
<object id="3" template="../../obj/collision.tx" x="480" y="0" width="32" height="320"/>
<object id="4" template="../../obj/collision.tx" x="-32" y="-32" width="544" height="32"/>
<object id="5" template="../../obj/waypoint.tx" name="5" x="232" y="144"/>
<object id="7" template="../../obj/entry_up.tx" x="208" y="304" width="64" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_2.tmx"/>
<property name="teleportObjectId" value="24"/>
</properties>
</object>
<object id="8" template="../../obj/waypoint.tx" name="8" x="232" y="208"/>
<object id="9" template="../../obj/waypoint.tx" name="9" x="48" y="208"/>
<object id="10" template="../../obj/waypoint.tx" name="10" x="416" y="208"/>
<object id="11" template="../../obj/waypoint.tx" name="11" x="48" y="128"/>
<object id="12" template="../../obj/waypoint.tx" name="12" x="48" y="160"/>
<object id="13" template="../../obj/waypoint.tx" name="13" x="48" y="80"/>
<object id="14" template="../../obj/waypoint.tx" name="14" x="416" y="80"/>
<object id="15" template="../../obj/waypoint.tx" name="15" x="232" y="80"/>
<object id="16" template="../../obj/waypoint.tx" name="16" x="48" y="256"/>
<object id="17" template="../../obj/waypoint.tx" name="17" x="416" y="256"/>
<object id="18" template="../../obj/waypoint.tx" name="18" x="130" y="158"/>
<object id="19" template="../../obj/waypoint.tx" name="19" x="334" y="158"/>
<object id="20" template="../../obj/waypoint.tx" name="20" x="334" y="128"/>
<object id="21" template="../../obj/waypoint.tx" name="21" x="130" y="128"/>
<object id="22" template="../../obj/waypoint.tx" name="22" x="128" y="256"/>
<object id="23" template="../../obj/waypoint.tx" name="23" x="336" y="256"/>
<object id="24" template="../../obj/entry_down.tx" x="224" y="32" width="32" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_4.tmx"/>
<property name="teleportObjectId" value="40"/>
</properties>
</object>
<object id="26" template="../../obj/dialog.tx" x="144" y="240"/>
<object id="27" template="../../obj/dialog.tx" x="64" y="240"/>
<object id="28" template="../../obj/dialog.tx" x="128" y="200"/>
<object id="29" template="../../obj/dialog.tx" x="192" y="200"/>
<object id="30" template="../../obj/dialog.tx" x="368" y="240"/>
<object id="31" template="../../obj/dialog.tx" x="400" y="199"/>
<object id="32" template="../../obj/dialog.tx" x="352" y="200"/>
<object id="33" template="../../obj/dialog.tx" x="400" y="112"/>
<object id="34" template="../../obj/dialog.tx" x="352" y="112"/>
<object id="35" template="../../obj/dialog.tx" x="80" y="64"/>
<object id="36" template="../../obj/dialog.tx" x="400" y="64"/>
<object id="37" template="../../obj/dialog.tx" x="400" y="240"/>
<object id="43" template="../../obj/waypoint.tx" name="43" x="416" y="128"/>
<object id="44" template="../../obj/waypoint.tx" name="44" x="416" y="160"/>
<object id="47" template="../../obj/enemy.tx" x="80" y="256">
<properties>
<property name="enemy" value="Construct"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="r16-22,r11-12,r18-21,5,r8-15, r18-21, r11-12,r9-13"/>
</properties>
</object>
<object id="48" template="../../obj/enemy.tx" x="384" y="256">
<properties>
<property name="enemy" value="Construct"/>
<property name="threatRange" type="int" value="50"/>
<property name="waypoints" value="23,10, r5-19-20"/>
</properties>
</object>
</objectgroup>
</map>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="43">
<tileset firstgid="1" source="../../tileset/main.tsx"/>
<tileset firstgid="10113" source="../../tileset/buildings.tsx"/>
<tileset firstgid="11905" source="../../tileset/dungeon.tsx"/>
<tileset firstgid="16385" source="../../tileset/dungeon-nocollide.tsx"/>
<tileset firstgid="20865" source="../../tileset/buildings-nocollide.tsx"/>
<layer id="1" name="Background" width="30" height="20">
<data encoding="base64" compression="zlib">
eJzjVWdg4B1CGAbQxQbaXaN4FI/iUTyKR/EoHsUwDADG93lF
</data>
</layer>
<layer id="2" name="Ground" width="30" height="20">
<data encoding="base64" compression="zlib">
eJxjYBgFowATNPIzMDTz099eeQEGBkUB+toJ8qsK0E5lAfr6GeRXBSim1M/RNgNjLymA1vaK+FLX3hBrPHI+hPWTau91oH038NhJLKBlOE/B4296patQtHimhb1XaBC/lIIwX4aGgbAXBgbK3u0SqHgoAQAY1RaZ
</data>
</layer>
<layer id="3" name="Walls" width="30" height="20">
<properties>
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJwz4WNgMBmB2ByIL9EYm2Gx1xSIaQ2MR+0dlvZetMZvb7QNcTjWBmFmJJC9wAbTrms+mGKU+jcOyZ54LHbiArSw9yowLEWh4XnRioEhBMq+Ys3QQIm94ta47b1mjapOEMq/DKXDfRkYInxJszfcjzj/ooMr1phitIxfYHpKwKWPVvai+3GSNaoYOfZKIKUPfP6VwBK+lNiLDKiZj2byMjBMIxKj20usvhm8lNXV6GCg2w6kYACuCn5U
</data>
</layer>
<layer id="4" name="Overlay" width="30" height="20">
<data encoding="base64" compression="zlib">
eJxjYBgFo2AUjALaAHEf6qojFoQTaR6x6kYBdkDteCMWEBtvEaPxOwoGEAAAArYDtw==
</data>
</layer>
<objectgroup id="5" name="Objects">
<object id="26" template="../../obj/scroll.tx" x="272" y="80">
<properties>
<property name="reward" value="[ { &quot;type&quot;: &quot;card&quot;, &quot;cardName&quot;: &quot;Myr Convert&quot;, &quot;count&quot;: 1 }, { &quot;type&quot;: &quot;card&quot;, &quot;cardName&quot;: &quot;Coretapper&quot;, &quot;count&quot;: 1 }, { &quot;type&quot;: &quot;card&quot;, &quot;cardName&quot;: &quot;Myr Custodian&quot;, &quot;count&quot;: 1 }, { &quot;type&quot;: &quot;card&quot;, &quot;cardName&quot;: &quot;Myr Adapter&quot;, &quot;count&quot;: 1 } ]"/>
</properties>
</object>
<object id="27" template="../../obj/enemy.tx" x="232" y="172">
<properties>
<property name="enemy" value="Myr Superion"/>
<property name="pursueRange" type="int" value="200"/>
<property name="spawn.Hard" type="bool" value="false"/>
<property name="spawn.Normal" type="bool" value="false"/>
<property name="threatRange" type="int" value="50"/>
</properties>
</object>
<object id="28" template="../../obj/enemy.tx" x="232" y="162">
<properties>
<property name="effect" value="{ &quot;startBattleWithCard&quot;: [ &quot;Alpha Myr&quot;, &quot;Omega Myr&quot;]}"/>
<property name="enemy" value="Myr Superion"/>
<property name="pursueRange" type="int" value="200"/>
<property name="spawn.Easy" type="bool" value="false"/>
<property name="spawn.Hard" type="bool" value="false"/>
<property name="spawn.Normal" type="bool" value="true"/>
<property name="threatRange" type="int" value="50"/>
</properties>
</object>
<object id="29" template="../../obj/enemy.tx" x="232" y="152">
<properties>
<property name="effect" value="{ &quot;startBattleWithCard&quot;: [ &quot;Myr Turbine&quot;, &quot;Alpha Myr&quot;, &quot;Omega Myr&quot;]}"/>
<property name="enemy" value="Myr Superion"/>
<property name="pursueRange" type="int" value="200"/>
<property name="spawn.Easy" type="bool" value="false"/>
<property name="spawn.Hard" type="bool" value="true"/>
<property name="spawn.Normal" type="bool" value="false"/>
<property name="threatRange" type="int" value="50"/>
</properties>
</object>
<object id="33" template="../../obj/dialog.tx" x="176" y="112" width="128" height="64"/>
<object id="34" template="../../obj/portal.tx" x="232" y="88">
<properties>
<property name="portalState" value="open"/>
<property name="sprite" value="sprites/portal3.atlas"/>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_0.tmx"/>
<property name="teleportObjectId" value="14"/>
</properties>
</object>
<object id="35" template="../../obj/dialog.tx" x="176" y="272" width="128" height="64"/>
<object id="36" template="../../obj/collision.tx" x="-32" y="0" width="32" height="320"/>
<object id="37" template="../../obj/collision.tx" x="272" y="176" width="176" height="112"/>
<object id="38" template="../../obj/collision.tx" x="480" y="0" width="32" height="320"/>
<object id="39" template="../../obj/collision.tx" x="-32" y="-32" width="544" height="32"/>
<object id="40" template="../../obj/entry_up.tx" x="208" y="304" width="64" height="16">
<properties>
<property name="teleport" value="../common/maps/map/main_story_explore/library_of_varsil_3.tmx"/>
<property name="teleportObjectId" value="24"/>
</properties>
</object>
<object id="41" template="../../obj/collision.tx" x="92" y="309" width="544" height="32"/>
<object id="42" template="../../obj/collision.tx" x="32" y="144" width="176" height="144"/>
</objectgroup>
<objectgroup id="6" name="Waypoints">
<object id="5" template="../../obj/waypoint.tx" name="WP1" x="232" y="144"/>
</objectgroup>
</map>

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="50" height="40" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="104"> <map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="50" height="40" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="118">
<editorsettings> <editorsettings>
<export target="wastetown..tmx" format="tmx"/> <export target="waste_town.abandoned.tmx" format="tmx"/>
</editorsettings> </editorsettings>
<properties> <properties>
<property name="preventEscape" type="bool" value="true"/> <property name="preventEscape" type="bool" value="true"/>
@@ -13,12 +13,12 @@
<tileset firstgid="30977" source="../../tileset/rivers-nocolide.tsx"/> <tileset firstgid="30977" source="../../tileset/rivers-nocolide.tsx"/>
<layer id="1" name="Background" width="50" height="40"> <layer id="1" name="Background" width="50" height="40">
<data encoding="base64" compression="zlib"> <data encoding="base64" compression="zlib">
eJztk0EKwjAURLN3p+cQREHrzQqCincQT+E99CAuvIUJrRDEtjHWziTO4pEufmEe/085MqYkZl4Ys1vhc/TBcY3P8C1bu4vJEp9Du6i4F/gMfZBTz53L1XbkVr8O322cWH/Om+p13Z/VTnv7fUqsQ24vF5t98dKZaUYdQmcQQggRx8EjZMaf7fqPhXf5m5yYYfYIzdZ0Qyx3FZIx1AV9VzE7yeGuGF1ib+dfPIZ2yeGmfuWD9PjUqW0Onf+Zrc2ryxudP8YvReTBhTy4kAcX8uBiKI8HlhcNkA== eJztmUsOgjAURTt3pK7D/4e6BHfEx6hxCRrjKlyGiS7EgbuQFyBpCIUCr320YXCDA0zuyX23H/UHjPkd1pIzdvLofWDovqP30FbHOIvxlt5Hn0WiH6f3gCGXeg4sn7gj3/QJEtlGlvXnGSVP6P48ZTrHnx+WdQhyecfeV7nOTBzqELUHLGW9iRxaE/YhY0PLul/G48p6ffDcYIEzjQvnGmCA+bK9K9m+UieTiyCVd8R3q77XVK9tkgVW54v8y5iwBQywp2D0hJIjy2Uh2fNVvclmyMRciZLloeJRlcUEx7SkI00yoZgrkSW/BmMzmGKBPX7D1TjacOpkgPvXLNd3XRy6WK4BY7dAfzd0ZgE55O+QOnl0cdS996p6NckRFnSiiqGMq4pbVxZY53YTa6lMmL8NU3HUnaeucmDfA3uOngMUIP9OQsVRdLa1kQOEOVtUHGuO+7+iKY4/AhwTSA==
</data> </data>
</layer> </layer>
<layer id="2" name="Ground" width="50" height="40"> <layer id="2" name="Ground" width="50" height="40">
<data encoding="base64" compression="zlib"> <data encoding="base64" compression="zlib">
eJzVWc1uEzEQdtpDtT95hg03pPIMRaENPEGFOC+8B6WPwA0O20q0Vd6plVBDzj2gcgKJddejTgaPPf5JGkay4v0bzzf/dpQKo7ZU6n0/DmqldqrVsVsFMgukrud/XedZZ2RkvqkHnjvo+nzNOGD9l/1aH8o0PlonZ9UgM8zhehOU0yZPTWAT7d+pdnlKAj/QdrH5GMTw/4KxY3x6tMF4XSeNmNz5rVBqzzMuCztP17cXAr54PO/HrcdXWlMTYvPAlOHP3fc9iyXO16T0vbTr0KU/H47c8dqi+p0rb/9u3DhgzVzx2pIeJGf9ceHInXOgD4Fhqz9at3+acN4cDtBdjv4D86P9ILXLl4lSXyfh/DkcI2adWKK24HpCV6505VYuB2wCR6i/+nLS2/G/96AX5HDY8ljryGuYH9gB02dGtp9j9VGKgyNXHQC5IFb18PWOkDdiawvg+MHgwfeXaM7pCPRO90E+G3K9ipQ0jqoYho8+CdYBveN61lWPzyCnUpt0xA6LctDha+aXzl8xdd7Wnz0jfsvhAP+gPuKzCaZ5Meit6H8n9eMoi2HAXP9qG2h79PHygOlNOXyrxwxd67k0jqhegbT+Q/oOwHFK3lta5GjqQb7SgRnjtfGQEvY1CY6TahXH3Qv3+xI9A95ZAhacVyU4GqM/jUOyLzkUxseRyQdXgnxgI1zrpH2Z9q3jMa/rAskisccvw2tuwfDO//kDxfQAc2MPTsYZui+N39Q9VcwZ1AnKMff7vT730+Wblv764eo9YghihNsDxsTHrcGB4wPkxmexuc/fGpJDNa4ZqYVLY7MFuo/fgdy0MLWkILWQ1u6U/le6R27q1RoBuKZIRlw/MG4bBryvPYvwfUr4PDXEPxtU36iM8BzXf/p8Hee42L4he825wUFrf8i6uXDY9rYh+TkFB9dLxfKCM+HQPWEqjpxE/38K0c824cD28GGge+1UHPeWvXsqdRGybJM9UmjbcMSeF28bDnzOEoJFikPHVWos/AX21dQX eJzVWUtOG0EQ7SELNB+fYcwOCc5A5IDDCRBiPXAPPkdgRxZDpJDIdwIpwnjNAsEKJKaZLlEudfXfYEoquedX3a/+3RbCj5pCiMOOtyohVsp5/lZ6CvOktpN/XaWZJ1Nrvql6mSvo+veCccD837u5joo4OVInl2W/ZhjD9UdQSpt8NoFNpH/H2uUzCfxA2kXnYxDDXwVjy/h09oHxukjKmNz5Jxdi1cJ/c71M07dXDnIxr3d8a/GVRtWE0DwwYuRz923PQonzNSBb7Pwv9Do06c+GI3W8Nqh+p8rbz7UZB8yZKl4b0oOkrD8mHKlzDvQhwLr6I3X7UvvL5nCA7lL0H1ge7QepXS6GQvwa+svncGTMPKFEbQH9IM0Hplxpyq1cDkiNA3qymN7clpP2B+Z5dfPp8lhjyWtZyffm58w3DwNx7IqDI1MdAJwQq5JtvWNsbw447hg8+P4MjTkdgd7pPshmw1iSOMq8ZxudOswPesf1rC3fn0FOteW5adHr8CfzS8c/mDqv68/WKjcckGuoj/jYZJL3esu732H1zkXeM4zlr7SBtEcXL2+Ydov+W8ljdC3HrnHUMv4t9e/TdwCOM/LeTLOOuurXVxgwY7w6Ga6Efc0Fx0k5j+N+0/y+i54B7zgCi299qJX+JA6Xfcm2Y3zsqHzwzyEf6MhUHziSvrU34HWdo7W42ONJyZpoMBzYP3+jkB5gouzBrXGM7rvGb+yeKqTOnaAc87jR6XMjfn2jwl4/bL2HL0GMcHvAkPi4VThwfMC68Vls6lpdkxwqcY1JLZwpm03RffwO5KapqiU5qYW0dsf0HK575LqarxGAa4TWiOsHxq3DgPe1lwG+Twmfp/r4Z43qG10jPMf1nz5fxDkutq/PXnOicNDa7zNvKhy6va1Pfo7BwfVSobLgTBhwuMqOxZGS6P9PPvpZJhzYHjYMdK8di+NRs3ePpTZgLctkjxhaNhyh58XLhgOfs/hgccUh4yo2Fl4BWmjU8A==
</data> </data>
</layer> </layer>
<layer id="3" name="Walls" width="50" height="40"> <layer id="3" name="Walls" width="50" height="40">
@@ -26,7 +26,7 @@
<property name="spriteLayer" type="bool" value="true"/> <property name="spriteLayer" type="bool" value="true"/>
</properties> </properties>
<data encoding="base64" compression="zlib"> <data encoding="base64" compression="zlib">
eJztmE1OAjEUx58JRDCBlQvDmDCyQnBjNG5040ov4MLLmLkDRghH0Avgx4KNew8gxngGFRea2AITam2nr18zY+SXNAxD6fTf/2tfpwALFuDohRBl3YcsGBQAgj9abgpzHSdFsb7zkv8x3C7b/Z/tO3vdKWUTkztbALukBNXk54+aAE/N+XeZDh9chLh6e0v6bevoOFwBqFUnY/WDOvO9S8q4DfDRVj/7KgC4rarrJUHWsgkYHY9kDo0KUx0uiOfbkGi4I/HTD+3bxOg4q0w/XelgITEUfdbt21Hp6DBrFdURz0OX8H50DNZH3fnhC53xEeUBVzpInEMPIML2x5WvL6vTT5EOma/PRMdyOblkRZr5wyc6Oi4Du2f53N8k6YhzjIyGZQ5zCcYPfr6sW/ZfNMdDpk0T32Q64nXAFFlfXORuEXmZ53XO46Gm5zo6agbxpJubTfcorvzgY36/BXDQUtej0H0vf3/Dox8Y6DtGN9T/zRZTHdT/r1kM9BXnGaez/fK4gmtbladE7ywmOt5w1Sak8W5PcR1X12SsjjYBjkm5X/v9OzsPZNcm5GXdZXlo4OqRHBfF10k6sGcx2PODWd1IdL8ruY9dt239wMS/qi8DB/s0l3GFGTvbPbMMtu+vRb/nlRRfZ6/vmh7wY66KK95jE89Nzh1c40JHHtDRkVbONOE/+sGSN2/+ux95I20d3yTbck0= eJztWElOwzAU/UitaJHaFQvUIDV01YENArGBDSu4AAsug3KHIlr1CHCBMiy6Yc8BKEKcASgLkHAm1Zg4/p6SoPZJVibH+c/vD44BllgCh6ELXt425IFxCcD5p+22NOdxWk7md1GxP4c7Vb33advp834lH5/c3QbYI82pp39/2gZ4bs+veTxs4NLF9dtfkR9bhsfRGkCjHszVLzSp6wFpsx7AZ0/87WsH4K4u7pcGkssCYHg8kRialkIeJhDH24RwuCf+M3L1x8TwOK+FR1M8aBAf8r6a+uOIePSpXOXziOPQJFg9+gr5MYlHh5MDbegRQ2Z+kuqAbJzzQPwchgAe1h5Tur6uh0eWR5qmL4THajW95YUs64dNyPC4cvS+ZXN9k8YjrjE8tDRrmElg9GBjZlPT/qQYd6kxVXTj8YjzgCp4tpio3UkoSpw3GY0nkprL8Ggo+JNsbVZdo5jSg/X5gy7AYVfcz4e/7mXvb1nUAwP/H2Pgyj/ThSoPX//vyAdGgv2Ms2i9PKvhxhbVqaR/FhUe77huAbL4t/fB48HGdLsaNhFuyHvHHYAT0h42/j6n44B3roKi5F0ajy1cP1LjvPg8jQd2Lwa7fxD19ZLuDzj3sXlbVw+M/4tsGRtYp5n0K8zcYdfMstxo29/KdvcrA/ss7Yt+SGrAzrnIr1iNi5JLZLGIPLKqmSpYRD1oFE2bRdejaMiaxw9yRHM0
</data> </data>
</layer> </layer>
<layer id="5" name="Overlay" width="50" height="40"> <layer id="5" name="Overlay" width="50" height="40">
@@ -35,7 +35,7 @@
</data> </data>
</layer> </layer>
<objectgroup id="4" name="Objects"> <objectgroup id="4" name="Objects">
<object id="38" template="../../obj/entry_up.tx" x="364" y="592" width="56" height="16"/> <object id="38" template="../../obj/entry_up.tx" x="364" y="624" width="56" height="16"/>
<object id="55" template="../../obj/treasure.tx" x="432" y="304" visible="0"> <object id="55" template="../../obj/treasure.tx" x="432" y="304" visible="0">
<properties> <properties>
<property name="reward">[{ <property name="reward">[{
@@ -110,48 +110,60 @@
<object id="67" template="../../obj/enemy.tx" x="495.333" y="236.667"> <object id="67" template="../../obj/enemy.tx" x="495.333" y="236.667">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="speedModifier" type="float" value="5"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="68" template="../../obj/enemy.tx" x="473.333" y="350.667"> <object id="68" template="../../obj/enemy.tx" x="473.333" y="350.667">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="69" template="../../obj/enemy.tx" x="219.333" y="212.667"> <object id="69" template="../../obj/enemy.tx" x="219.333" y="212.667">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="70" template="../../obj/enemy.tx" x="240.667" y="396.667"> <object id="70" template="../../obj/enemy.tx" x="240.667" y="396.667">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="71" template="../../obj/enemy.tx" x="658" y="303.333"> <object id="71" template="../../obj/enemy.tx" x="658" y="303.333">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="speedModifier" type="float" value="2"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="72" template="../../obj/enemy.tx" x="534.667" y="482"> <object id="72" template="../../obj/enemy.tx" x="534.667" y="482">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="threatRange" type="int" value="5000"/> <property name="fleeRange" value="5000"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="2000"/>
</properties> </properties>
</object> </object>
<object id="73" template="../../obj/enemy.tx" x="354.667" y="291.333"> <object id="73" template="../../obj/enemy.tx" x="354.667" y="291.333">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="74" template="../../obj/enemy.tx" x="79.3333" y="398.667"> <object id="74" template="../../obj/enemy.tx" x="48" y="272">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="speedModifier" type="float" value="5"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
@@ -159,42 +171,46 @@
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}]
&quot;text&quot;: &quot;A fountain borders the walkway here. The stonework is chipped and dirty, but the water appears to be surprisingly clean and clear.&quot;, &quot;text&quot;: &quot;A fountain borders the walkway here. The stonework is chipped and dirty, but the water appears to be surprisingly clean and clear.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;}] &quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
} }
]</property> ]</property>
</properties> </properties>
</object> </object>
<object id="76" template="../../obj/dialog.tx" x="688" y="160" width="64" height="64"> <object id="76" template="../../obj/dialog.tx" x="656" y="175" width="117" height="98">
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}],
&quot;text&quot;: &quot;One of the few sounds in the area, the water wheel on this old mill creaks as it spins gradually in the slowly moving and oddly colored water.&quot;, &quot;text&quot;: &quot;One of the few sounds in the area, the water wheel on this old mill creaks as it spins gradually in the slowly moving and oddly colored water.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;}] &quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
} }
]</property> ]</property>
</properties> </properties>
</object> </object>
<object id="77" template="../../obj/dialog.tx" x="400" y="448" width="160" height="128" visible="1"> <object id="77" template="../../obj/dialog.tx" x="400" y="448" width="160" height="190" visible="1">
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}],
&quot;text&quot;: &quot;These shops are as lifeless as the rest of the town, but it looks as though there might be some items left behind, suspiciously untouched after however long.&quot;, &quot;text&quot;: &quot;These shops are as lifeless as the rest of the town, but it looks as though there might be some items left behind, suspiciously untouched after however long.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;}] &quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
} }
]</property> ]</property>
</properties> </properties>
</object> </object>
<object id="78" template="../../obj/dialog.tx" x="240" y="448" width="128" height="128"> <object id="78" template="../../obj/dialog.tx" x="202" y="448" width="166" height="159">
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}],
&quot;text&quot;: &quot;The inn is locked up tightly. The town hall in contrast is missing its' front door, but you find nothing out of the ordinary inside, just well organized crumbling papers coveered in illegibly smeared ink.&quot;, &quot;text&quot;: &quot;The inn is locked up tightly. The town hall in contrast is missing its' front door, but you find nothing out of the ordinary inside, just well organized crumbling papers coveered in illegibly smeared ink.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;}] &quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
} }
]</property> ]</property>
</properties> </properties>
@@ -203,9 +219,10 @@
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}],
&quot;text&quot;: &quot;The vines covering these houses look sickly, as if they are searching for sustenance not found in the soil here.&quot;, &quot;text&quot;: &quot;The vines covering these houses look sickly, as if they are searching for sustenance not found in the soil here.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;}] &quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
} }
]</property> ]</property>
</properties> </properties>
@@ -224,32 +241,31 @@
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}],
&quot;text&quot;: &quot;'Deathly still' seems a fitting description for the graveyard. You find yourself almost wishing for the dead to rise just to have something happen.&quot;, &quot;text&quot;: &quot;'Deathly still' seems a fitting description for the graveyard. You find yourself almost wishing for the dead to rise just to have something happen.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;}] &quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
} }
]</property> ]</property>
</properties> </properties>
</object> </object>
<object id="83" template="../../obj/dialog.tx" x="336" y="544" width="112" height="16"> <object id="83" template="../../obj/dialog.tx" x="336" y="560" width="112" height="16">
<properties> <properties>
<property name="dialog">[ <property name="dialog">[{
{ &quot;condition&quot;: [{&quot;checkMapFlag&quot;:&quot;demonsActive&quot;},{&quot;checkMapFlag&quot;: &quot;escaped&quot;, &quot;not&quot;: true}],
&quot;condition&quot;: [{&quot;checkQuestFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}},{&quot;checkQuestFlag&quot;: {&quot;key&quot;:&quot;escaped&quot;, &quot;not&quot;: true}}], &quot;action&quot;: [{&quot;setMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;, &quot;val&quot;: 0}}]
&quot;action&quot;: [&quot;setQuestFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;, &quot;val&quot;: 0}]
}, },
{ {
&quot;condition&quot;: [{&quot;checkQuestFlag&quot;: {&quot;key&quot;:&quot;introShown&quot;, &quot;not&quot;: true}}], &quot;condition&quot;: [{&quot;checkMapFlag&quot;: &quot;introShown&quot;, &quot;not&quot;: true}],
&quot;action&quot;: [&quot;setQuestFlag&quot;: {&quot;key&quot;:&quot;introShown&quot;, &quot;val&quot;: 1}], &quot;action&quot;: [{&quot;advanceMapFlag&quot;: &quot;introShown&quot;}],
&quot;text&quot;: &quot;Just as the merchant described, an empty town lies behind these gates. Even the wind seems more still here as if afraid to disturb anything.&quot;, &quot;text&quot;: &quot;Just as the merchant described, an empty town lies behind these gates. Even the wind seems more still here as if afraid to disturb anything.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{&quot;name&quot;: &quot;(Continue)&quot;,
&quot;name&quot;: &quot;(Continue)&quot;,
&quot;text&quot;: &quot;As you move closer, it becomes obvious that most of the buildings are intact but in a state of disrepair or are outright overgrown. It is doubtful that anyone has resided here in quite some time.&quot;, &quot;text&quot;: &quot;As you move closer, it becomes obvious that most of the buildings are intact but in a state of disrepair or are outright overgrown. It is doubtful that anyone has resided here in quite some time.&quot;,
&quot;options&quot;: [{&quot;name&quot;: &quot;Time to go portal hunting. I don't guess I can ask anyone for directions...&quot;}] &quot;options&quot;: [{&quot;name&quot;: &quot;Time to go portal hunting. I don't guess I can ask anyone for directions...&quot;}]
} }]
] }]
} </property>
]</property> <property name="hidden" type="bool" value="true"/>
</properties> </properties>
</object> </object>
<object id="84" template="../../obj/manashards.tx" x="560" y="448" visible="0"/> <object id="84" template="../../obj/manashards.tx" x="560" y="448" visible="0"/>
@@ -293,14 +309,21 @@
<property name="reward" value="[ { &quot;type&quot;: &quot;card&quot;, &quot;cardName&quot;: &quot;Abandoned Outpost&quot;, &quot;count&quot;: 1 } ]"/> <property name="reward" value="[ { &quot;type&quot;: &quot;card&quot;, &quot;cardName&quot;: &quot;Abandoned Outpost&quot;, &quot;count&quot;: 1 } ]"/>
</properties> </properties>
</object> </object>
<object id="93" template="../../obj/enemy.tx" x="370.667" y="147.333"/> <object id="93" template="../../obj/enemy.tx" x="370.667" y="147.333">
<properties>
<property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="5000"/>
</properties>
</object>
<object id="94" template="../../obj/dialog.tx" x="240" y="272" width="80" height="80" visible="1"> <object id="94" template="../../obj/dialog.tx" x="240" y="272" width="80" height="80" visible="1">
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}],
&quot;text&quot;: &quot;Perhaps just a trick of the mind, you can't shake the feeling that you're being watched as you explore the area.&quot;, &quot;text&quot;: &quot;Perhaps just a trick of the mind, you can't shake the feeling that you're being watched as you explore the area.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;}] &quot;name&quot;: &quot;(Continue)&quot;, &quot;action&quot;: [{&quot;deleteMapObject&quot;:-1}]}]
} }
]</property> ]</property>
</properties> </properties>
@@ -309,13 +332,14 @@
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}, &quot;not&quot;: true}]
&quot;text&quot;: &quot;And here lies gruesome confirmation of the merchant's tale, by way of the remains of a merfolk tail. It appears that there was indeed one of the aquatic people here far from their normal territory which it will never return to.&quot;, &quot;text&quot;: &quot;And here lies gruesome confirmation of the merchant's tale, by way of the remains of a merfolk tail. It appears that there was indeed one of the aquatic people here far from their normal territory which it will never return to.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;(Continue)&quot;, &quot;name&quot;: &quot;(Continue)&quot;,
&quot;text&quot;: &quot;Investigating the scene will have to wait, however, as it appears you have determined the cause of death. Seemingly out of nowhere, demons come running at you from all directions.&quot;, &quot;text&quot;: &quot;Investigating the scene will have to wait, however, as it appears you have determined the cause of death. Seemingly out of nowhere, demons come running at you from all directions.&quot;,
&quot;options&quot;: [{ &quot;options&quot;: [{
&quot;name&quot;: &quot;RUN!!!&quot;, &quot;name&quot;: &quot;RUN!!!&quot;,
&quot;action&quot;: [&quot;setQuestFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;, &quot;val&quot;: 1},{&quot;deleteMapObject&quot;: 258}, {&quot;activateMapObject&quot;: 230},{&quot;activateMapObject&quot;: 248},{&quot;activateMapObject&quot;: 246},{&quot;activateMapObject&quot;: 247},{&quot;deleteMapObject&quot;: -1},{&quot;setMapFlag&quot;: 258}] &quot;action&quot;: [{&quot;setMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;, &quot;val&quot;: 1}},{&quot;deleteMapObject&quot;: -1}, {&quot;activateMapObject&quot;: 67},{&quot;activateMapObject&quot;: 68},{&quot;activateMapObject&quot;: 69},{&quot;activateMapObject&quot;: 70},{&quot;activateMapObject&quot;: 71},{&quot;activateMapObject&quot;: 72},{&quot;activateMapObject&quot;: 73},{&quot;activateMapObject&quot;: 74},{&quot;activateMapObject&quot;: 93},{&quot;activateMapObject&quot;: 98},{&quot;activateMapObject&quot;: 99},{&quot;activateMapObject&quot;: 100},{&quot;activateMapObject&quot;: 101},{&quot;activateMapObject&quot;: 102},{&quot;activateMapObject&quot;: 103}]
}] }]
}] }]
} }
@@ -326,8 +350,8 @@
<properties> <properties>
<property name="dialog">[ <property name="dialog">[
{ {
&quot;condition&quot;: [{&quot;checkQuestFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}},{&quot;checkQuestFlag&quot;: {&quot;key&quot;:&quot;escaped&quot;}, &quot;not&quot;: true}] &quot;condition&quot;: [{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;demonsActive&quot;}},{&quot;checkMapFlag&quot;: {&quot;key&quot;:&quot;escaped&quot;}, &quot;not&quot;: true}]
&quot;action&quot;: [&quot;setQuestFlag&quot;: {&quot;key&quot;:&quot;escaped&quot;, &quot;val&quot;: 1}] &quot;action&quot;: [{&quot;setMapFlag&quot;: {&quot;key&quot;:&quot;escaped&quot;, &quot;val&quot;: 1}}]
} }
]</property> ]</property>
</properties> </properties>
@@ -353,38 +377,51 @@
<object id="98" template="../../obj/enemy.tx" x="32" y="96"> <object id="98" template="../../obj/enemy.tx" x="32" y="96">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="speedModifier" type="float" value="8"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="99" template="../../obj/enemy.tx" x="657" y="133"> <object id="99" template="../../obj/enemy.tx" x="657" y="133">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="100" template="../../obj/enemy.tx" x="359" y="496"> <object id="100" template="../../obj/enemy.tx" x="200" y="488">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="threatRange" type="int" value="5000"/> <property name="fleeRange" value="5000"/>
<property name="inactive" type="bool" value="true"/>
<property name="speedModifier" type="float" value="7.5"/>
<property name="threatRange" type="int" value="1000"/>
</properties> </properties>
</object> </object>
<object id="101" template="../../obj/enemy.tx" x="383" y="383"> <object id="101" template="../../obj/enemy.tx" x="383" y="383">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="speedModifier" type="float" value="2.5"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="102" template="../../obj/enemy.tx" x="32" y="480"> <object id="102" template="../../obj/enemy.tx" x="32" y="480">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="speedModifier" type="float" value="1"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="103" template="../../obj/enemy.tx" x="736" y="368"> <object id="103" template="../../obj/enemy.tx" x="624" y="416">
<properties> <properties>
<property name="enemy" value="Demon"/> <property name="enemy" value="Demon"/>
<property name="inactive" type="bool" value="true"/>
<property name="threatRange" type="int" value="5000"/> <property name="threatRange" type="int" value="5000"/>
</properties> </properties>
</object> </object>
<object id="116" template="../../obj/collision.tx" x="330" y="640" width="126" height="29"/>
<object id="117" template="../../obj/waypoint.tx" x="384" y="239"/>
</objectgroup> </objectgroup>
</map> </map>

View File

@@ -4697,9 +4697,8 @@
<objectgroup draworder="index" id="3"> <objectgroup draworder="index" id="3">
<object id="2" x="0" y="0" width="4" height="16"/> <object id="2" x="0" y="0" width="4" height="16"/>
<object id="3" x="4" y="0" width="12" height="8"/> <object id="3" x="4" y="0" width="12" height="8"/>
<object id="4" x="4" y="8" width="4" height="4"/> <object id="5" x="6" y="8" width="4" height="3"/>
<object id="5" x="8" y="8" width="2" height="2"/> <object id="6" x="4" y="8" width="2" height="6"/>
<object id="6" x="4" y="12" width="2" height="2"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="2218"> <tile id="2218">
@@ -4711,9 +4710,8 @@
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="0" y="0" width="16" height="7"/> <object id="1" x="0" y="0" width="16" height="7"/>
<object id="2" x="11" y="7" width="5" height="9"/> <object id="2" x="11" y="7" width="5" height="9"/>
<object id="3" x="5" y="7" width="6" height="4"/> <object id="4" x="2" y="7" width="9" height="2"/>
<object id="4" x="2" y="7" width="3" height="2"/> <object id="6" x="6" y="9" width="5" height="3"/>
<object id="6" x="8" y="11" width="3" height="2"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="2316"> <tile id="2316">
@@ -5020,9 +5018,8 @@
<tile id="2533"> <tile id="2533">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="0" y="13" width="16" height="3"/> <object id="1" x="0" y="13" width="16" height="3"/>
<object id="3" x="0" y="11" width="14" height="2"/> <object id="4" x="0" y="5" width="10" height="8"/>
<object id="4" x="0" y="5" width="10" height="6"/> <object id="6" x="0" y="0" width="7" height="5"/>
<object id="6" x="0" y="-1" width="7" height="6"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="2534"> <tile id="2534">
@@ -5036,7 +5033,6 @@
<object id="3" x="0" y="12" width="12" height="4"/> <object id="3" x="0" y="12" width="12" height="4"/>
<object id="6" x="4" y="9" width="8" height="3"/> <object id="6" x="4" y="9" width="8" height="3"/>
<object id="7" x="7" y="6" width="5" height="3"/> <object id="7" x="7" y="6" width="5" height="3"/>
<object id="8" x="11" y="0" width="1" height="6"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="2632"> <tile id="2632">
@@ -5743,8 +5739,7 @@
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="0" y="0" width="16" height="8"/> <object id="1" x="0" y="0" width="16" height="8"/>
<object id="2" x="4" y="8" width="12" height="3"/> <object id="2" x="4" y="8" width="12" height="3"/>
<object id="3" x="8" y="11" width="8" height="1"/> <object id="4" x="11" y="11" width="5" height="5"/>
<object id="4" x="11" y="12" width="5" height="4"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="3330"> <tile id="3330">

View File

@@ -837,17 +837,17 @@
<object id="4" x="12" y="7" width="4" height="3"/> <object id="4" x="12" y="7" width="4" height="3"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="325"> <tile id="325" probability="0.2">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="0" y="3" width="16" height="10"/> <object id="1" x="0" y="3" width="16" height="10"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="326"> <tile id="326" probability="0.2">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="0" y="4" width="16" height="9"/> <object id="1" x="0" y="4" width="16" height="9"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="327"> <tile id="327" probability="0.2">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="0" y="3" width="16" height="10"/> <object id="1" x="0" y="3" width="16" height="10"/>
</objectgroup> </objectgroup>
@@ -962,7 +962,7 @@
<object id="1" x="0" y="0" width="16" height="16"/> <object id="1" x="0" y="0" width="16" height="16"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="504"> <tile id="504" probability="0.2">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="3" y="0" width="10" height="16"/> <object id="1" x="3" y="0" width="10" height="16"/>
</objectgroup> </objectgroup>
@@ -1003,7 +1003,7 @@
<object id="4" x="12" y="12" width="1" height="4"/> <object id="4" x="12" y="12" width="1" height="4"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="511"> <tile id="511" probability="0.05">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="0" y="3" width="16" height="10"/> <object id="1" x="0" y="3" width="16" height="10"/>
</objectgroup> </objectgroup>
@@ -1064,7 +1064,7 @@
<object id="1" x="0" y="3" width="7" height="10"/> <object id="1" x="0" y="3" width="7" height="10"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="684"> <tile id="684" probability="0.2">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="3" y="0" width="10" height="16"/> <object id="1" x="3" y="0" width="10" height="16"/>
</objectgroup> </objectgroup>
@@ -1160,7 +1160,7 @@
<object id="1" x="3" y="0" width="10" height="16"/> <object id="1" x="3" y="0" width="10" height="16"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="864"> <tile id="864" probability="0.2">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="3" y="0" width="10" height="16"/> <object id="1" x="3" y="0" width="10" height="16"/>
</objectgroup> </objectgroup>
@@ -1200,7 +1200,7 @@
<object id="1" x="0" y="8" width="16" height="8"/> <object id="1" x="0" y="8" width="16" height="8"/>
</objectgroup> </objectgroup>
</tile> </tile>
<tile id="872"> <tile id="872" probability="0.05">
<objectgroup draworder="index" id="2"> <objectgroup draworder="index" id="2">
<object id="1" x="3" y="0" width="10" height="16"/> <object id="1" x="3" y="0" width="10" height="16"/>
</objectgroup> </objectgroup>
@@ -11242,4 +11242,57 @@
<object id="1" x="3" y="3" width="10" height="10"/> <object id="1" x="3" y="3" width="10" height="10"/>
</objectgroup> </objectgroup>
</tile> </tile>
<wangsets>
<wangset name="Grey, Green Water" type="edge" tile="328">
<wangcolor name="" color="#ff0000" tile="-1" probability="1"/>
<wangcolor name="" color="#00ff00" tile="-1" probability="1"/>
<wangtile tileid="144" wangid="0,0,0,0,1,0,1,0"/>
<wangtile tileid="145" wangid="0,0,1,0,1,0,0,0"/>
<wangtile tileid="146" wangid="1,0,0,0,0,0,1,0"/>
<wangtile tileid="151" wangid="1,0,2,0,2,0,1,0"/>
<wangtile tileid="152" wangid="1,0,1,0,2,0,2,0"/>
<wangtile tileid="324" wangid="1,0,1,0,0,0,0,0"/>
<wangtile tileid="325" wangid="0,0,1,0,0,0,1,0"/>
<wangtile tileid="326" wangid="0,0,1,0,0,0,1,0"/>
<wangtile tileid="327" wangid="0,0,1,0,0,0,1,0"/>
<wangtile tileid="328" wangid="0,0,1,0,0,0,1,0"/>
<wangtile tileid="331" wangid="2,0,2,0,1,0,1,0"/>
<wangtile tileid="332" wangid="2,0,1,0,1,0,2,0"/>
<wangtile tileid="504" wangid="1,0,0,0,1,0,0,0"/>
<wangtile tileid="505" wangid="0,0,1,0,1,0,0,0"/>
<wangtile tileid="506" wangid="0,0,0,0,1,0,1,0"/>
<wangtile tileid="507" wangid="1,0,1,0,1,0,0,0"/>
<wangtile tileid="508" wangid="0,0,1,0,1,0,1,0"/>
<wangtile tileid="509" wangid="0,0,1,0,1,0,0,0"/>
<wangtile tileid="510" wangid="0,0,0,0,1,0,1,0"/>
<wangtile tileid="511" wangid="0,0,1,0,0,0,1,0"/>
<wangtile tileid="512" wangid="1,0,1,0,1,0,1,0"/>
<wangtile tileid="684" wangid="1,0,0,0,1,0,0,0"/>
<wangtile tileid="685" wangid="1,0,1,0,0,0,0,0"/>
<wangtile tileid="686" wangid="1,0,0,0,0,0,1,0"/>
<wangtile tileid="687" wangid="1,0,1,0,0,0,1,0"/>
<wangtile tileid="688" wangid="1,0,0,0,1,0,1,0"/>
<wangtile tileid="689" wangid="1,0,1,0,0,0,0,0"/>
<wangtile tileid="690" wangid="1,0,0,0,0,0,1,0"/>
<wangtile tileid="691" wangid="0,0,1,0,0,0,0,0"/>
<wangtile tileid="692" wangid="0,0,0,0,0,0,1,0"/>
<wangtile tileid="864" wangid="1,0,0,0,1,0,0,0"/>
<wangtile tileid="865" wangid="1,0,1,0,1,0,0,0"/>
<wangtile tileid="866" wangid="0,0,1,0,1,0,1,0"/>
<wangtile tileid="867" wangid="0,0,1,0,2,0,2,0"/>
<wangtile tileid="868" wangid="0,0,2,0,2,0,1,0"/>
<wangtile tileid="869" wangid="2,0,2,0,1,0,0,0"/>
<wangtile tileid="870" wangid="2,0,0,0,1,0,2,0"/>
<wangtile tileid="871" wangid="0,0,0,0,1,0,0,0"/>
<wangtile tileid="872" wangid="1,0,0,0,1,0,0,0"/>
<wangtile tileid="1044" wangid="1,0,0,0,1,0,0,0"/>
<wangtile tileid="1045" wangid="1,0,1,0,0,0,1,0"/>
<wangtile tileid="1046" wangid="1,0,0,0,1,0,1,0"/>
<wangtile tileid="1047" wangid="2,0,1,0,0,0,2,0"/>
<wangtile tileid="1048" wangid="2,0,2,0,0,0,1,0"/>
<wangtile tileid="1049" wangid="1,0,2,0,2,0,0,0"/>
<wangtile tileid="1050" wangid="1,0,0,0,2,0,2,0"/>
<wangtile tileid="1051" wangid="1,0,0,0,0,0,0,0"/>
</wangset>
</wangsets>
</tileset> </tileset>

View File

@@ -0,0 +1,68 @@
golem_4.png
size: 64,96
format: RGBA8888
filter: Nearest,Nearest
repeat: none
Avatar
xy: 0, 0
size: 16, 16
Idle
xy: 0, 16
size: 16, 16
Idle
xy: 16, 16
size: 16, 16
Idle
xy: 32, 16
size: 16, 16
Idle
xy: 48, 16
size: 16, 16
Walk
xy: 0, 32
size: 16, 16
Walk
xy: 16, 32
size: 16, 16
Walk
xy: 32, 32
size: 16, 16
Walk
xy: 48, 32
size: 16, 16
Attack
xy: 0, 48
size: 16, 16
Attack
xy: 16, 48
size: 16, 16
Attack
xy: 32, 48
size: 16, 16
Attack
xy: 48, 48
size: 16, 16
Hit
xy: 0, 64
size: 16, 16
Hit
xy: 16, 64
size: 16, 16
Hit
xy: 32, 64
size: 16, 16
Hit
xy: 48, 64
size: 16, 16
Death
xy: 0, 80
size: 16, 16
Death
xy: 16, 80
size: 16, 16
Death
xy: 32, 80
size: 16, 16
Death
xy: 48, 80
size: 16, 16

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,68 @@
myr.png
size: 64,96
format: RGBA8888
filter: Nearest,Nearest
repeat: none
Avatar
xy: 0, 0
size: 16, 16
Idle
xy: 0, 16
size: 16, 16
Idle
xy: 16, 16
size: 16, 16
Idle
xy: 32, 16
size: 16, 16
Idle
xy: 48, 16
size: 16, 16
Walk
xy: 0, 32
size: 16, 16
Walk
xy: 16, 32
size: 16, 16
Walk
xy: 32, 32
size: 16, 16
Walk
xy: 48, 32
size: 16, 16
Attack
xy: 0, 48
size: 16, 16
Attack
xy: 16, 48
size: 16, 16
Attack
xy: 32, 48
size: 16, 16
Attack
xy: 48, 48
size: 16, 16
Hit
xy: 0, 64
size: 16, 16
Hit
xy: 16, 64
size: 16, 16
Hit
xy: 32, 64
size: 16, 16
Hit
xy: 48, 64
size: 16, 16
Death
xy: 0, 80
size: 16, 16
Death
xy: 16, 80
size: 16, 16
Death
xy: 32, 80
size: 16, 16
Death
xy: 48, 80
size: 16, 16

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,68 @@
pirate2.png
size: 64,96
format: RGBA8888
filter: Nearest,Nearest
repeat: none
Avatar
xy: 0, 0
size: 16, 16
Idle
xy: 0, 16
size: 16, 16
Idle
xy: 16, 16
size: 16, 16
Idle
xy: 32, 16
size: 16, 16
Idle
xy: 48, 16
size: 16, 16
Walk
xy: 0, 32
size: 16, 16
Walk
xy: 16, 32
size: 16, 16
Walk
xy: 32, 32
size: 16, 16
Walk
xy: 48, 32
size: 16, 16
Attack
xy: 0, 48
size: 16, 16
Attack
xy: 16, 48
size: 16, 16
Attack
xy: 32, 48
size: 16, 16
Attack
xy: 48, 48
size: 16, 16
Hit
xy: 0, 64
size: 16, 16
Hit
xy: 16, 64
size: 16, 16
Hit
xy: 32, 64
size: 16, 16
Hit
xy: 48, 64
size: 16, 16
Death
xy: 0, 80
size: 16, 16
Death
xy: 16, 80
size: 16, 16
Death
xy: 32, 80
size: 16, 16
Death
xy: 48, 80
size: 16, 16

View File

@@ -0,0 +1,68 @@
archivist.png
size: 64,96
format: RGBA8888
filter: Nearest,Nearest
repeat: none
Avatar
xy: 0, 0
size: 16, 16
Idle
xy: 0, 16
size: 16, 16
Idle
xy: 16, 16
size: 16, 16
Idle
xy: 32, 16
size: 16, 16
Idle
xy: 48, 16
size: 16, 16
Walk
xy: 0, 32
size: 16, 16
Walk
xy: 16, 32
size: 16, 16
Walk
xy: 32, 32
size: 16, 16
Walk
xy: 48, 32
size: 16, 16
Attack
xy: 0, 48
size: 16, 16
Attack
xy: 16, 48
size: 16, 16
Attack
xy: 32, 48
size: 16, 16
Attack
xy: 48, 48
size: 16, 16
Hit
xy: 0, 64
size: 16, 16
Hit
xy: 16, 64
size: 16, 16
Hit
xy: 32, 64
size: 16, 16
Hit
xy: 48, 64
size: 16, 16
Death
xy: 0, 80
size: 16, 16
Death
xy: 16, 80
size: 16, 16
Death
xy: 32, 80
size: 16, 16
Death
xy: 48, 80
size: 16, 16

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -190,6 +190,13 @@
"Mythic Rare" "Mythic Rare"
] ]
} }
],
"questTags": [
"Wizard",
"Human",
"IdentityBlue",
"BiomeBlue",
"BiomeColorless"
] ]
}, },
{ {
@@ -1743,6 +1750,57 @@
"BiomeBlack" "BiomeBlack"
] ]
}, },
{
"name": "Archivist",
"sprite": "sprites/enemy/humanoid/human/wizard/archivist.atlas",
"deck": [
"decks/standard/archivist.dck"
],
"life": 20,
"rewards": [
{
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "deckCard",
"probability": 0.5,
"count": 2,
"addMaxCount": 4,
"rarity": [
"uncommon",
"rare"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "gold",
"probability": 0.3,
"count": 100,
"addMaxCount": 200
}
],
"colors": "U",
"questTags": [
"Human",
"Wizard",
"Mystic",
"IdentityBlue",
"Dungeon"
]
},
{ {
"name": "Armored Knight", "name": "Armored Knight",
"sprite": "sprites/enemy/humanoid/knight.atlas", "sprite": "sprites/enemy/humanoid/knight.atlas",
@@ -3238,11 +3296,7 @@
"Furnace", "Furnace",
"IdentityColorless", "IdentityColorless",
"Construct", "Construct",
"Robot", "Robot"
null,
null,
null,
null
] ]
}, },
{ {
@@ -4080,7 +4134,7 @@
"difficulty": 0.1, "difficulty": 0.1,
"speed": 15, "speed": 15,
"scale": 0.4, "scale": 0.4,
"flying":true "flying":true,
"life": 25, "life": 25,
"rewards": [ "rewards": [
{ {
@@ -12930,65 +12984,54 @@
] ]
}, },
{ {
"name": "Golem that is Generous", "name": "Golem Sentinel",
"nameOverride": "Shiny Golem", "sprite": "sprites/enemy/construct/golem_4.atlas",
"sprite": "sprites/enemy/construct/golem.atlas",
"deck": [ "deck": [
"decks/standard/golem.json" "decks/standard/golem_sentinel.dck",
"decks/standard/golem_sentinel_2.dck",
"decks/standard/golem_sentinel_3.dck"
], ],
"ai": "", "ai": "",
"spawnRate": 0.1, "spawnRate": 1,
"difficulty": 0.1, "difficulty": 0.1,
"speed": 20, "speed": 20,
"life": 13, "life": 20,
"rewards": [ "rewards": [
{
"type": "card",
"probability": 1,
"count": 1,
"editions": [
"M22",
"M21"
],
"colors": [
"red"
],
"rarity": [
"rare"
]
},
{
"type": "life",
"probability": 1,
"count": 1,
"addMaxCount": 3
},
{ {
"type": "deckCard", "type": "deckCard",
"probability": 1, "probability": 1,
"count": 3, "count": 1,
"addMaxCount": 3 "addMaxCount": 4
},
{
"type": "gold",
"probability": 0.7,
"count": 100,
"addMaxCount": 200
}, },
{ {
"type": "card", "type": "card",
"probability": 1, "probability": 1,
"count": 2, "count": 3,
"cardName": "Black Lotus" "rarity": [
"Uncommon"
]
}, },
{ {
"type": "gold", "type": "card",
"probability": 1, "probability": 0.5,
"count": 10, "count": 3,
"addMaxCount": 90 "rarity": [
"Rare"
]
} }
], ],
"colors": "GW", "colors": "C",
"questTags": [ "questTags": [
"Golem", "Golem",
"IdentityColorless",
"Construct", "Construct",
"IdentityGreen", "Dungeon"
"IdentityWhite",
"IdentitySelesnya"
] ]
}, },
{ {
@@ -18726,6 +18769,58 @@
"BiomeBlack" "BiomeBlack"
] ]
}, },
{
"name": "Myr Superion",
"sprite": "sprites/enemy/construct/myr.atlas",
"deck": [
"decks/standard/Myr_Superion.dck"
],
"boss": true,
"ai": "",
"spawnRate": 1,
"difficulty": 0.5,
"speed": 20,
"life": 25,
"rewards": [
{
"type": "deckCard",
"probability": 1,
"count": 1,
"addMaxCount": 4
},
{
"type": "gold",
"probability": 0.7,
"count": 100,
"addMaxCount": 200
},
{
"type": "card",
"probability": 1,
"count": 3,
"rarity": [
"Uncommon"
]
},
{
"type": "card",
"probability": 0.5,
"count": 3,
"rarity": [
"Rare"
]
}
],
"colors": "C",
"questTags": [
"Myr",
"IdentityColorless",
"Construct",
"Robot",
"Dungeon",
"Boss"
]
},
{ {
"name": "Naga Warrior", "name": "Naga Warrior",
"sprite": "sprites/enemy/humanoid/naga/nagawarrior.atlas", "sprite": "sprites/enemy/humanoid/naga/nagawarrior.atlas",
@@ -19678,6 +19773,168 @@
"Pirate" "Pirate"
] ]
}, },
{
"name": "Pirate 2",
"nameOverride": "Buccaneer",
"sprite": "sprites/enemy/humanoid/human/rogue/pirate2.atlas",
"deck": [
"decks/standard/pirate2.dck"
],
"ai": "",
"spawnRate": 0.8,
"difficulty": 0.1,
"speed": 24,
"life": 16,
"rewards": [
{
"type": "deckCard",
"probability": 1,
"count": 1,
"rarity": [
"Common"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "deckCard",
"probability": 0.25,
"count": 1,
"rarity": [
"Rare"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "gold",
"probability": 1,
"count": 30,
"addMaxCount": 100
},
{
"type": "card",
"probability": 0.5,
"count": 2,
"colors": [
"Red"
]
},
{
"type": "card",
"probability": 0.5,
"count": 1,
"colors": [
"Blue"
]
}
],
"colors": "BRU",
"questTags": [
"Aggressive",
"Human",
"Warrior",
"Water",
"Bandit",
"Thief",
"IdentityBlack",
"IdentityBlue",
"IdentityRed",
"IdentityGrixis",
"Dungeon"
]
},
{
"name": "Pirate 3",
"nameOverride": "Plunderer",
"sprite": "sprites/enemy/humanoid/human/rogue/pirate3.atlas",
"deck": [
"decks/standard/pirate3.dck"
],
"ai": "",
"spawnRate": 0.8,
"difficulty": 0.1,
"speed": 24,
"life": 16,
"rewards": [
{
"type": "deckCard",
"probability": 1,
"count": 1,
"rarity": [
"Common"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "deckCard",
"probability": 0.25,
"count": 1,
"rarity": [
"Rare"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "gold",
"probability": 1,
"count": 30,
"addMaxCount": 100
},
{
"type": "card",
"probability": 0.5,
"count": 2,
"colors": [
"Red"
]
},
{
"type": "card",
"probability": 0.5,
"count": 1,
"colors": [
"Blue"
]
}
],
"colors": "BRU",
"questTags": [
"Aggressive",
"Human",
"Warrior",
"Water",
"Bandit",
"Thief",
"IdentityBlack",
"IdentityBlue",
"IdentityRed",
"IdentityGrixis",
"Dungeon"
]
},
{ {
"name": "Pirate Captain", "name": "Pirate Captain",
"nameOverride": "", "nameOverride": "",
@@ -19752,6 +20009,85 @@
"Pirate" "Pirate"
] ]
}, },
{
"name": "Pirate Captain 2",
"nameOverride": "Pirate Captain",
"sprite": "sprites/enemy/humanoid/human/rogue/pirate3.atlas",
"deck": [
"decks/standard/pirate_captain_2.dck"
],
"ai": "",
"spawnRate": 0.8,
"difficulty": 0.1,
"speed": 24,
"life": 20,
"rewards": [
{
"type": "deckCard",
"probability": 1,
"count": 1,
"rarity": [
"Common"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "deckCard",
"probability": 0.25,
"count": 1,
"rarity": [
"Rare"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "gold",
"probability": 1,
"count": 30,
"addMaxCount": 100
},
{
"type": "card",
"probability": 0.5,
"count": 2,
"colors": [
"Black"
]
},
{
"type": "card",
"probability": 0.5,
"count": 1,
"colors": [
"Black"
]
}
],
"colors": "B",
"questTags": [
"Leader",
"Aggressive",
"Human",
"Warrior",
"Water",
"Bandit",
"Thief",
"IdentityBlack",
"Dungeon"
]
},
{ {
"name": "Plant", "name": "Plant",
"sprite": "sprites/enemy/plant/plant.atlas", "sprite": "sprites/enemy/plant/plant.atlas",
@@ -23086,6 +23422,63 @@
"BiomeBlack" "BiomeBlack"
] ]
}, },
{
"name": "Spirit",
"sprite": "sprites/enemy/undead/ghost_3.atlas",
"deck": [
"decks/standard/spirit.dck"
],
"ai": "",
"flying": true,
"spawnRate": 1,
"difficulty": 0.1,
"speed": 31,
"life": 20,
"rewards": [
{
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "deckCard",
"probability": 0.5,
"count": 2,
"addMaxCount": 4,
"rarity": [
"uncommon",
"rare"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{
"type": "gold",
"probability": 0.3,
"count": 100,
"addMaxCount": 200
}
],
"colors": "U",
"questTags": [
"Undead",
"Spirit",
"Flying",
"Ghost",
"IdentityBlue",
"Dungeon"
]
},
{ {
"name": "Stegosaurus", "name": "Stegosaurus",
"sprite": "sprites/enemy/beast/dinosaur/dinosaur_stegosaurus.atlas", "sprite": "sprites/enemy/beast/dinosaur/dinosaur_stegosaurus.atlas",

View File

@@ -2867,6 +2867,18 @@
"map": "../common/maps/map/towns/plains_town.tmx", "map": "../common/maps/map/towns/plains_town.tmx",
"radiusFactor": 0.8 "radiusFactor": 0.8
}, },
{
"name": "Quest_APortalToNowhere",
"type": "dungeon",
"count": 1,
"spriteAtlas": "../common/maps/tileset/buildings.atlas",
"sprite": "WasteTown",
"map": "../common/maps/map/main_story/waste_town_abandoned.tmx",
"radiusFactor": 0.8,
"questTags": [
"Quest_APortalToNowhere"
]
},
{ {
"name": "Red Castle", "name": "Red Castle",
"type": "castle", "type": "castle",