20 package net.sf.gridarta.model.mapmodel;
22 import java.awt.Point;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Objects;
31 import java.util.concurrent.CopyOnWriteArrayList;
48 import org.apache.log4j.Category;
49 import org.apache.log4j.Logger;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
76 private final transient Object
syncLock =
new Object();
212 public void mapMetaChanged() {
217 public void mapSizeChanged(@NotNull
final Size2D mapSize) {
233 public DefaultMapModel(@NotNull
final AutojoinLists<G, A, R> autojoinLists, @NotNull
final A
mapArchObject, @NotNull
final ArchetypeChooserModel<G, A, R> archetypeChooserModel, @NotNull
final GameObjectFactory<G, A, R> gameObjectFactory, @NotNull
final GameObjectMatchers gameObjectMatchers, @NotNull
final InsertionMode topmostInsertionMode) {
270 for (
final G gameObject : objects) {
271 if (!gameObject.isInContainer()) {
293 if (newSize.equals(mapSize)) {
304 final Collection<GameObject<G, A, R>> objectsToDelete =
new HashSet<>();
305 if (mapSize.
getWidth() > newSize.getWidth()) {
310 if (mapSize.
getHeight() > newSize.getHeight()) {
334 final Iterator<MapSquare<G, A, R>>
it = mapSquares.iterator();
335 while (
it.hasNext()) {
337 if (mapSquare.
getMapX() >= mapSize.getWidth() || mapSquare.
getMapY() >= mapSize.getHeight()) {
349 final Iterator<G> it2 = gameObjects.iterator();
350 while (it2.hasNext()) {
353 if (topGameObject.getContainer() ==
null || topGameObject.getMapX() >= mapSize.getWidth() || topGameObject.getMapY() >= mapSize.getHeight()) {
387 LOG.error(
"beginSquareChange: square (" + mapSquare +
") is about to change outside a transaction");
397 LOG.error(
"endSquareChange: square (" + mapSquare +
") was changed outside a transaction");
398 final Set<MapSquare<G, A, R>> mapSquares =
new HashSet<>(1);
399 mapSquares.add(mapSquare);
413 LOG.error(
"beginGameObjectChange: game object (" + gameObject.getBestName() +
"@" + mapSquare +
") is about to change outside a transaction");
423 LOG.error(
"endGameObjectChange: game object (" + gameObject.getBestName() +
"@" + gameObject.getMapSquare() +
") was changed outside a transaction");
424 final Set<G> gameObjects =
new HashSet<>(1);
425 gameObjects.add(gameObject);
437 LOG.error(
"transientGameObjectChange: game object (" + gameObject.getBestName() +
"@" + gameObject.getMapSquare() +
") was changed outside a transaction");
438 final Set<G> gameObjects =
new HashSet<>(1);
439 gameObjects.add(gameObject);
459 throw new IllegalStateException(
"A transaction must only be used by one thread. " +
transactionThread +
" " + Thread.currentThread());
473 throw new IllegalStateException(
"Tried to end a transaction but no transaction was open.");
492 final Collection<MapSquare<G, A, R>> mapSquares =
new HashSet<>(
changedSquares);
495 if (mapSquare !=
null) {
496 mapSquares.add(mapSquare);
508 if (gameObject.isHead()) {
509 for (G env = gameObject; env !=
null; env = env.getContainerGameObject()) {
562 listener.mapSquaresChanged(mapSquares);
572 if (!gameObjects.isEmpty()) {
576 listener.mapObjectsChanged(gameObjects, transientGameObjects);
587 listener.mapSizeChanged(newSize);
596 listener.preBeginTransaction();
607 listener.beginTransaction(
name);
626 listener.postEndTransaction();
636 listener.mapFileChanged(oldMapFile);
645 listener.modifiedChanged();
652 if (gameObject.isInContainer()) {
666 final Point point =
new Point(part.getMultiX(), part.getMultiY());
667 point.translate(pos.x, pos.y);
674 final String temp = part.getArchetypeName();
676 if (node.getArchetype().getArchetypeName().equals(temp)) {
690 listener.errorsChanged(
errors);
711 final G newGameObject;
713 if (nextGameObject ==
null || nextGameObjectEnv ==
null) {
715 if (newGameObject ==
null) {
723 if (gameObject == nextGameObject) {
728 for (
int i = 0; i < position - 1; i++) {
729 newGameObject.moveDown();
733 nextGameObjectEnv.
addLast(newGameObject);
734 if (templateBaseObject instanceof
Archetype) {
739 return newGameObject;
749 final R realArchetype = baseObject.getArchetype();
750 final R effectiveArchetype;
754 if (gameObject !=
null) {
759 if (effectiveArchetype ==
null) {
763 effectiveArchetype = realArchetype;
772 final List<G> parts =
new ArrayList<>();
773 for (R archetypePart = effectiveArchetype; archetypePart !=
null; archetypePart = archetypePart.getMultiNext()) {
775 if (archetypePart == effectiveArchetype) {
777 part.setArchetype(archetypePart);
781 if (direction !=
null) {
784 if (!parts.isEmpty()) {
785 parts.get(0).addTailPart(part);
790 for (
final G part : parts) {
791 final int mapX = pos.x + part.getArchetype().getMultiX();
792 final int mapY = pos.y + part.getArchetype().getMultiY();
796 final G head = parts.get(0);
807 LOG.error(
"addGameObjectToMap: trying to insert game object out of map bounds at " + pos.x +
"/" + pos.y +
", map bounds is " +
mapArchObject.getMapSize());
815 public void moveEnv(@NotNull
final G gameObject, @NotNull
final Point pos, @NotNull
final G nextGameObject) {
816 if (!nextGameObject.isHead()) {
817 throw new IllegalArgumentException(
"can't move tail part of a multi-part object");
819 assert !gameObject.isMulti();
821 if (nextGameObject.isInContainer()) {
822 nextGameObjectContainer = nextGameObject.getContainer();
823 if (nextGameObjectContainer ==
null) {
824 throw new IllegalArgumentException(
"can't move into container object");
831 throw new IllegalArgumentException(
"can't move into a game object in another map");
834 nextGameObjectContainer.
insertAfter(nextGameObject, gameObject);
837 if (!nextGameObject.isInContainer() && gameObject.getArchetype().isMulti()) {
838 final Point tmp =
new Point();
839 for (R archetypeTail = gameObject.getArchetype().getMultiNext(); archetypeTail !=
null; archetypeTail = archetypeTail.getMultiNext()) {
841 gameObject.addTailPart(gameObjectTail);
842 tmp.x = pos.x + archetypeTail.getMultiX();
843 tmp.y = pos.y + archetypeTail.getMultiY();
851 if (!gameObject.isHead() || !prevGameObject.isHead()) {
852 throw new IllegalArgumentException(
"can't move tail part of a multi-part object");
856 gameObject.removeTailParts();
857 prevGameObject.addFirst(gameObject);
861 public boolean isAreaEmpty(
final int left,
final int top,
final int width,
final int height) {
862 final Point point =
new Point();
863 for (
int x = left; x < left + width; x++) {
865 for (
int y = top; y < top + height; y++) {
881 for (
final Iterable<G> mapSquare :
this) {
896 if (Objects.equals(
this.mapFile,
mapFile)) {
915 if (thisMapFile ==
null) {
919 if (newMapFile.
equals(thisMapFile)) {
928 final List<G> gameObjects =
new ArrayList<>();
929 for (
final Iterable<G> mapSquare :
this) {
930 for (
final G gameObject : mapSquare) {
931 if (gameObject.isHead()) {
932 gameObjects.add(gameObject);
958 for (
final Iterable<G> mapSquare :
this) {
959 for (
final G gameObject : mapSquare) {
960 gameObject.facesReloaded();
969 public void nextPoint(
final Point point,
final int direction) {
973 if (point.x >= mapSize.
getWidth()) {
1011 assert gameObject.isHead();
1012 if (checkType == 0) {
1018 final int editType = gameObject.getEditType();
1020 final int newEditType = retainedEditType |
calculateEditType(gameObject, checkType);
1021 gameObject.setEditType(newEditType);
1033 final int matcherEditType = matcher.getEditType();
1034 if ((matcherEditType & checkType) != 0 && matcher.isMatching(gameObject)) {
1035 editType |= matcherEditType;
1053 if (archetype.isMulti()) {
1058 if (autojoinList ==
null) {
1063 final boolean isMainIndex = autojoinList.
isMainIndex(archetype);
1065 if (gameObject !=
null) {
1066 final R gameObjectArchetype = gameObject.getArchetype();
1067 final boolean isExistingMainIndex = autojoinList.
isMainIndex(gameObjectArchetype);
1070 if (isExistingMainIndex) {
1077 if (isExistingMainIndex) {
1081 if (gameObjectArchetype == archetype) {
1095 final R newArchetype = isMainIndex ? autojoinList.
getArchetype(newIndex) : archetype;
1096 if (gameObject !=
null) {
1097 gameObject.setArchetype(newArchetype);
1103 private int joinInsert(@NotNull
final Point point, @NotNull
final AutojoinList<G, A, R> autojoinList,
final int dx,
final int dy,
final int dir,
final int reverseDir,
final int altIndex) {
1104 final Point tmp =
new Point(point.x + dx, point.y + dy);
1110 if (gameObject ==
null) {
1115 final int index = autojoinList.getAlternativeIndex(archetype);
1117 return (index & reverseDir) == 0 ? 0 : dir;
1120 final int archetypeIndex = autojoinList.getIndex(archetype);
1122 if ((altIndex & dir) == 0) {
1123 newIndex = archetypeIndex & ~reverseDir;
1125 newIndex = archetypeIndex | reverseDir;
1127 gameObject.
setArchetype(autojoinList.getArchetype(newIndex));
1139 private void joinDelete(@NotNull
final Point point, @NotNull
final R archetype) {
1140 if (archetype.isMulti()) {
1145 if (autojoinList ==
null) {
1149 final boolean isMainIndex = autojoinList.
isMainIndex(archetype);
1158 private void joinDelete(@NotNull
final Point point, @NotNull
final AutojoinList<G, A, R> autojoinList,
final int dx,
final int dy,
final int reverseDir,
final int dir,
final int altIndex) {
1159 if ((altIndex & reverseDir) == 0) {
1163 final Point tmp =
new Point(point.x + dx, point.y + dy);
1169 if (gameObject ==
null) {
1186 for (
final G gameObject :
getMapSquare(point).reverse()) {
1206 final R archetype = gameObject.getArchetype();