Spawn areas now allow a boolean property "ignoreAreas", false by

default, that allows monsters from this area to be spawned and moved
over other game objects, like key areas, mapchange areas and rest areas.
This commit is contained in:
Zukero
2018-02-22 16:19:26 +01:00
parent 2ade99eed0
commit da59c7143d
8 changed files with 52 additions and 38 deletions

View File

@@ -52,21 +52,23 @@ public final class MonsterMovementController implements EvaluateWalkable {
}
}
public static boolean monsterCanMoveTo(final Monster movingMonster, final PredefinedMap map, final LayeredTileMap tilemap, final CoordRect p) {
public static boolean monsterCanMoveTo(final Monster movingMonster, final PredefinedMap map, final LayeredTileMap tilemap, final CoordRect p, boolean ignoreAreas) {
if (tilemap != null) {
if (!tilemap.isWalkable(p)) return false;
}
if (map.getMonsterAt(p, movingMonster) != null) return false;
for (MapObject mObj : map.eventObjects) {
if (mObj == null) continue;
if (!mObj.isActive) continue;
if (!mObj.position.intersects(p)) continue;
switch (mObj.type) {
if (!ignoreAreas) {
for (MapObject mObj : map.eventObjects) {
if (mObj == null) continue;
if (!mObj.isActive) continue;
if (!mObj.position.intersects(p)) continue;
switch (mObj.type) {
case newmap:
case keyarea:
case rest:
return false;
}
}
}
return true;
@@ -82,7 +84,7 @@ public final class MonsterMovementController implements EvaluateWalkable {
} else {
determineMonsterNextPosition(m, area, world.model.player.position);
if (!monsterCanMoveTo(m, map, tileMap, m.nextPosition)) {
if (!monsterCanMoveTo(m, map, tileMap, m.nextPosition, area.ignoreAreas)) {
cancelCurrentMonsterMovement(m);
return;
}
@@ -152,12 +154,12 @@ public final class MonsterMovementController implements EvaluateWalkable {
private final PathFinder pathfinder = new PathFinder(Constants.MAX_MAP_WIDTH, Constants.MAX_MAP_HEIGHT, this);
public boolean findPathFor(Monster m, Coord to) {
return pathfinder.findPathBetween(m.rectPosition, to, m.nextPosition);
return pathfinder.findPathBetween(m.rectPosition, to, m.nextPosition, m);
}
@Override
public boolean isWalkable(CoordRect r) {
return monsterCanMoveTo(null, world.model.currentMap, world.model.currentTileMap, r);
public boolean isWalkable(CoordRect r, Monster m) {
return monsterCanMoveTo(null, world.model.currentMap, world.model.currentTileMap, r, m.area.ignoreAreas);
}
public void moveMonsterToNextPosition(final Monster m, final PredefinedMap map) {

View File

@@ -51,20 +51,20 @@ public final class MonsterSpawningController {
}
public boolean TEST_spawnInArea(PredefinedMap map, LayeredTileMap tileMap, MonsterSpawnArea a, MonsterType type) { return spawnInArea(map, tileMap, a, type, null); }
private boolean spawnInArea(PredefinedMap map, LayeredTileMap tileMap, MonsterSpawnArea a, MonsterType type, Coord playerPosition) {
Coord p = getRandomFreePosition(map, tileMap, a.area, type.tileSize, playerPosition);
Coord p = getRandomFreePosition(map, tileMap, a, type.tileSize, playerPosition);
if (p == null) return false;
Monster m = a.spawn(p, type);
monsterSpawnListeners.onMonsterSpawned(map, m);
return true;
}
public static Coord getRandomFreePosition(PredefinedMap map, LayeredTileMap tileMap, CoordRect area, Size requiredSize, Coord playerPosition) {
public static Coord getRandomFreePosition(PredefinedMap map, LayeredTileMap tileMap, MonsterSpawnArea a, Size requiredSize, Coord playerPosition) {
CoordRect p = new CoordRect(requiredSize);
for(int i = 0; i < 100; ++i) {
p.topLeft.set(
area.topLeft.x + Constants.rnd.nextInt(area.size.width)
,area.topLeft.y + Constants.rnd.nextInt(area.size.height));
if (!MonsterMovementController.monsterCanMoveTo(null, map, tileMap, p)) continue;
a.area.topLeft.x + Constants.rnd.nextInt(a.area.size.width)
,a.area.topLeft.y + Constants.rnd.nextInt(a.area.size.height));
if (!MonsterMovementController.monsterCanMoveTo(null, map, tileMap, p, a.ignoreAreas)) continue;
if (playerPosition != null && p.contains(playerPosition)) continue;
return p.topLeft;
}

View File

@@ -283,7 +283,7 @@ public final class MovementController implements TimedMessageTask.Callback {
for (MonsterSpawnArea a : map.spawnAreas) {
for (Monster m : a.monsters) {
if (tileMap.isWalkable(m.rectPosition)) continue;
Coord p = MonsterSpawningController.getRandomFreePosition(map, tileMap, a.area, m.tileSize, playerPosition);
Coord p = MonsterSpawningController.getRandomFreePosition(map, tileMap, a, m.tileSize, playerPosition);
if (p == null) continue;
m.position.set(p);
}

View File

@@ -1,5 +1,6 @@
package com.gpl.rpg.AndorsTrail.controller;
import com.gpl.rpg.AndorsTrail.model.actor.Monster;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
@@ -21,10 +22,10 @@ public class PathFinder {
}
public interface EvaluateWalkable {
public boolean isWalkable(CoordRect r);
public boolean isWalkable(CoordRect r, Monster m);
}
public boolean findPathBetween(final CoordRect from, final Coord to, CoordRect nextStep) {
public boolean findPathBetween(final CoordRect from, final Coord to, CoordRect nextStep, Monster m) {
int iterations = 0;
if (from.contains(to)) return false;
@@ -44,19 +45,19 @@ public class PathFinder {
if (from.isAdjacentTo(p)) return true;
p.x -= 1; visit(nextStep, measureDistanceTo);
p.x += 2; visit(nextStep, measureDistanceTo);
p.x -= 1; p.y -= 1; visit(nextStep, measureDistanceTo);
p.y += 2; visit(nextStep, measureDistanceTo);
p.x -= 1; visit(nextStep, measureDistanceTo);
p.x += 2; visit(nextStep, measureDistanceTo);
p.y -= 2; visit(nextStep, measureDistanceTo);
p.x -= 2; visit(nextStep, measureDistanceTo);
p.x -= 1; visit(nextStep, measureDistanceTo, m);
p.x += 2; visit(nextStep, measureDistanceTo, m);
p.x -= 1; p.y -= 1; visit(nextStep, measureDistanceTo, m);
p.y += 2; visit(nextStep, measureDistanceTo, m);
p.x -= 1; visit(nextStep, measureDistanceTo, m);
p.x += 2; visit(nextStep, measureDistanceTo, m);
p.y -= 2; visit(nextStep, measureDistanceTo, m);
p.x -= 2; visit(nextStep, measureDistanceTo, m);
}
return false;
}
private void visit(CoordRect r, Coord measureDistanceTo) {
private void visit(CoordRect r, Coord measureDistanceTo, Monster m) {
final int x = r.topLeft.x;
final int y = r.topLeft.y;
@@ -68,7 +69,7 @@ public class PathFinder {
final int i = (y * maxWidth) + x;
if (visited[i]) return;
visited[i] = true;
if (!map.isWalkable(r)) return;
if (!map.isWalkable(r, m)) return;
int dx = (measureDistanceTo.x - x);
int dy = (measureDistanceTo.y - y);

View File

@@ -6,6 +6,7 @@ import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection;
import com.gpl.rpg.AndorsTrail.model.item.DropList;
import com.gpl.rpg.AndorsTrail.model.item.ItemContainer;
import com.gpl.rpg.AndorsTrail.model.item.Loot;
import com.gpl.rpg.AndorsTrail.model.map.MonsterSpawnArea;
import com.gpl.rpg.AndorsTrail.savegames.LegacySavegameFormatReaderForMonster;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
@@ -25,10 +26,12 @@ public final class Monster extends Actor {
private ItemContainer shopItems = null;
private final MonsterType monsterType;
public final MonsterSpawnArea area;
public Monster(MonsterType monsterType) {
public Monster(MonsterType monsterType, MonsterSpawnArea area) {
super(monsterType.tileSize, false, monsterType.isImmuneToCriticalHits());
this.monsterType = monsterType;
this.area = area;
this.iconID = monsterType.iconID;
this.nextPosition = new CoordRect(new Coord(), monsterType.tileSize);
resetStatsToBaseTraits();
@@ -95,20 +98,20 @@ public final class Monster extends Actor {
// ====== PARCELABLE ===================================================================
public static Monster newFromParcel(DataInputStream src, WorldContext world, int fileversion) throws IOException {
public static Monster newFromParcel(DataInputStream src, WorldContext world, int fileversion, MonsterSpawnArea area) throws IOException {
String monsterTypeId = src.readUTF();
if (fileversion < 20) {
monsterTypeId = monsterTypeId.replace(' ', '_').replace("\\'", "").toLowerCase();
}
MonsterType monsterType = world.monsterTypes.getMonsterType(monsterTypeId);
if (fileversion < 25) return LegacySavegameFormatReaderForMonster.newFromParcel_pre_v25(src, fileversion, monsterType);
if (fileversion < 25) return LegacySavegameFormatReaderForMonster.newFromParcel_pre_v25(src, fileversion, monsterType, area);
return new Monster(src, world, fileversion, monsterType);
return new Monster(src, world, fileversion, monsterType, area);
}
private Monster(DataInputStream src, WorldContext world, int fileversion, MonsterType monsterType) throws IOException {
this(monsterType);
private Monster(DataInputStream src, WorldContext world, int fileversion, MonsterType monsterType, MonsterSpawnArea area) throws IOException {
this(monsterType, area);
boolean readCombatTraits = true;
if (fileversion >= 25) readCombatTraits = src.readBoolean();

View File

@@ -21,6 +21,7 @@ public final class MonsterSpawnArea {
public final String[] monsterTypeIDs;
public final ArrayList<Monster> monsters = new ArrayList<Monster>();
public final boolean isUnique; // unique == non-respawnable
public final boolean ignoreAreas; //Can spawn on other game objects area.
private final String group;
public boolean isSpawning;
public final boolean isSpawningForNewGame;
@@ -32,6 +33,7 @@ public final class MonsterSpawnArea {
, String areaID
, String[] monsterTypeIDs
, boolean isUnique
, boolean ignoreAreas
, String group
, boolean isSpawningForNewGame
) {
@@ -41,6 +43,7 @@ public final class MonsterSpawnArea {
this.areaID = areaID;
this.monsterTypeIDs = monsterTypeIDs;
this.isUnique = isUnique;
this.ignoreAreas = ignoreAreas;
this.group = group;
this.isSpawningForNewGame = isSpawningForNewGame;
this.isSpawning = isSpawningForNewGame;
@@ -79,7 +82,7 @@ public final class MonsterSpawnArea {
spawn(p, context.monsterTypes.getMonsterType(monsterTypeID));
}
public Monster spawn(Coord p, MonsterType type) {
Monster m = new Monster(type);
Monster m = new Monster(type, this);
m.position.set(p);
monsters.add(m);
quantity.current++;
@@ -125,7 +128,7 @@ public final class MonsterSpawnArea {
if (fileversion >= 41) isSpawning = src.readBoolean();
quantity.current = src.readInt();
for(int i = 0; i < quantity.current; ++i) {
monsters.add(Monster.newFromParcel(src, world, fileversion));
monsters.add(Monster.newFromParcel(src, world, fileversion, this));
}
}

View File

@@ -92,6 +92,7 @@ public final class TMXMapTranslator {
mapObjects.add(MapObject.createMapChangeArea(position, object.name, map, place, group.name));
} else if (object.type.equalsIgnoreCase("spawn")) {
boolean isActiveForNewGame = true;
boolean ignoreAreas = false;
int maxQuantity = 1;
int spawnChance = 10;
String spawnGroup = object.name;
@@ -108,6 +109,8 @@ public final class TMXMapTranslator {
spawnChance = Integer.parseInt(p.value);
} else if (p.name.equalsIgnoreCase("active")) {
isActiveForNewGame = Boolean.parseBoolean(p.value);
} else if (p.name.equalsIgnoreCase("ignoreAreas")) {
ignoreAreas = Boolean.parseBoolean(p.value);
} else if (p.name.equalsIgnoreCase("spawngroup")) {
spawnGroup = p.value;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
@@ -135,6 +138,7 @@ public final class TMXMapTranslator {
,object.name
,monsterTypeIDs
,isUnique
,ignoreAreas
,group.name
,isActiveForNewGame
);

View File

@@ -2,14 +2,15 @@ package com.gpl.rpg.AndorsTrail.savegames;
import com.gpl.rpg.AndorsTrail.model.actor.Monster;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterType;
import com.gpl.rpg.AndorsTrail.model.map.MonsterSpawnArea;
import com.gpl.rpg.AndorsTrail.util.Coord;
import java.io.DataInputStream;
import java.io.IOException;
public final class LegacySavegameFormatReaderForMonster {
public static Monster newFromParcel_pre_v25(DataInputStream src, int fileversion, MonsterType monsterType) throws IOException {
Monster m = new Monster(monsterType);
public static Monster newFromParcel_pre_v25(DataInputStream src, int fileversion, MonsterType monsterType, MonsterSpawnArea area) throws IOException {
Monster m = new Monster(monsterType, area);
m.position.set(new Coord(src, fileversion));
m.ap.current = src.readInt();
m.health.current = src.readInt();