Gridarta Editor
AbstractGameObjectParser.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2023 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.model.io;
21 
22 import java.awt.Point;
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
37 import org.apache.log4j.Category;
38 import org.apache.log4j.Logger;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 
49 public abstract class AbstractGameObjectParser<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> implements GameObjectParser<G, A, R> {
50 
54  private static final Category LOG = Logger.getLogger(AbstractGameObjectParser.class);
55 
59  @NotNull
61 
65  @NotNull
67 
75  this.gameObjectFactory = gameObjectFactory;
76  this.archetypeSet = archetypeSet;
77  }
78 
79  @Nullable
80  @Override
81  public G load(@NotNull final BufferedReader reader, @Nullable final Collection<G> objects) throws IOException {
82  final String line = reader.readLine();
83  return line == null ? null : load(reader, line, objects);
84  }
85 
90  @Nullable
91  @Override
92  public G load(@NotNull final BufferedReader reader, @NotNull final String firstLine, @Nullable final Collection<G> objects) throws IOException {
93  final String archName = readArchName(reader, firstLine);
94  if (archName == null) {
95  return null;
96  }
97 
98  final R archetype = archetypeSet.getOrCreateArchetype(archName);
99  final G gameObject = gameObjectFactory.createGameObject(archetype);
100 
101  while (true) {
102  final String thisLine2 = reader.readLine();
103  if (thisLine2 == null) {
104  throw new IOException("unexpected end of file while reading 'arch " + archName + "'");
105  }
106  final String thisLine = thisLine2.trim();
107 
108  if (thisLine.startsWith("arch ")) {
109  final G invItem = load(reader, thisLine, objects);
110  assert invItem != null : "invItem must not be null here.";
111  gameObject.addLast(invItem);
112  } else if (thisLine.equals("end")) {
113  if (objects != null) {
114  objects.add(gameObject);
115  }
116  return gameObject;
117  } else if (thisLine.equals("msg")) {
118  gameObject.setMsgText("");
119  while (true) {
120  final String thisLine3 = reader.readLine();
121  if (thisLine3 == null) {
122  throw new IOException("unexpected end of file while reading 'arch " + archName + "'");
123  }
124 
125  if (thisLine3.trim().equals("endmsg")) {
126  break;
127  }
128 
129  gameObject.addMsgTextLine(thisLine3);
130  }
131  } else if (parseLine(thisLine, gameObject)) {
132  // ignore
133  } else if (thisLine.startsWith("x ")) {
134  gameObject.setMapX(Integer.parseInt(thisLine.substring(2)));
135  } else if (thisLine.startsWith("y ")) {
136  gameObject.setMapY(Integer.parseInt(thisLine.substring(2)));
137  } else {
138  gameObject.addObjectText(thisLine);
139  }
140  }
141  }
142 
151  private boolean parseLine(@NotNull final String line, @NotNull final G gameObject) {
152  return false;
153  }
154 
164  @Nullable
165  private static String readArchName(@NotNull final BufferedReader reader, @NotNull final String firstLine) throws IOException {
166  for (String thisLine2 = firstLine; thisLine2 != null; thisLine2 = reader.readLine()) {
167  final String thisLine = thisLine2.trim();
168  if (thisLine.startsWith("arch ")) {
169  return thisLine.substring(5).trim();
170  }
171  if (!thisLine.isEmpty() && !thisLine.startsWith("#")) {
172  throw new IOException("unexpected first line of game object: '" + thisLine + "', expected 'arch ...'");
173  }
174  }
175  return null;
176  }
177 
178  @Override
179  public void collectTempList(@NotNull final List<G> objects) {
180  final Collection<G> tailList = new ArrayList<>();
181 
182  for (final G gameObject : objects) {
183  if (!gameObject.isInContainer()) {
184  expandMulti(gameObject, tailList);
185  }
186  }
187 
188  objects.addAll(tailList);
189  }
190 
191  @Override
192  public void save(@NotNull final Appendable appendable, @NotNull final G gameObject) throws IOException {
193  final Map<String, String> fields = getModifiedFields(gameObject);
194 
195  appendable.append("arch ");
196  appendable.append(gameObject.getArchetype().getArchetypeName());
197  appendable.append("\n");
198  for (final Entry<String, String> entry : fields.entrySet()) {
199  final String key = entry.getKey();
200  appendable.append(key);
201  if (key.equals("msg")) {
202  appendable.append("\n");
203  appendable.append(entry.getValue());
204  appendable.append("endmsg\n");
205  } else {
206  appendable.append(" ");
207  appendable.append(entry.getValue());
208  appendable.append("\n");
209  }
210  }
211 
212  for (final G inventoryItem : gameObject) {
213  save(appendable, inventoryItem);
214  }
215 
216  appendable.append("end\n");
217  }
218 
219  @Override
220  public void addModifiedFields(@NotNull final G gameObject, @NotNull final Map<String, String> fields) {
221  final BaseObject<G, A, R, ?> archetype = gameObject.getArchetype();
222 
223  final String msgText = gameObject.getMsgText(false);
224  final String archMsgText = archetype.getMsgText(false);
225  if (msgText != null && !msgText.equals(archMsgText == null ? "" : archMsgText)) {
226  fields.put("msg", msgText);
227  }
228 
229  final CharSequence objText = gameObject.getObjectText();
230  if (objText.length() != 0) {
231  for (final String aTmp : StringUtils.PATTERN_END_OF_LINE.split(objText, 0)) {
232  final String[] line = StringUtils.PATTERN_SPACES.split(aTmp, 2);
233  if (line.length == 2) {
234  fields.put(line[0], line[1]);
235  } else {
236  LOG.warn("writeMapArch: ignoring invalid arch line: " + aTmp);
237  }
238  }
239  }
240 
241  // map coordinates only belong into map arches (not inventory arches)
242  if (gameObject.getMapX() != 0) {
243  fields.put("x", Integer.toString(gameObject.getMapX()));
244  }
245  if (gameObject.getMapY() != 0) {
246  fields.put("y", Integer.toString(gameObject.getMapY()));
247  }
248  }
249 
261  private void expandMulti(@NotNull final G gameObject, @NotNull final Collection<G> objects) {
262  final Archetype<G, A, R> archetype = gameObject.getArchetype();
263 
264  if (!archetype.isMulti() || gameObject.getMultiRefCount() > 1) {
265  return;
266  }
267 
268  if (gameObject.isInContainer()) {
269  LOG.warn("Multi-part expansion for a GameObject inside a container requested.");
270  return;
271  }
272 
273  // do insertion for all non-head parts of the multi
274  final Point pos = gameObject.getMapLocation();
275  for (R oldPart = archetype.getMultiNext(); oldPart != null; oldPart = oldPart.getMultiNext()) {
276  final G tailGameObject = gameObjectFactory.createGameObjectPart(oldPart, gameObject);
277  tailGameObject.setMapX(pos.x + oldPart.getMultiX());
278  tailGameObject.setMapY(pos.y + oldPart.getMultiY());
279  objects.add(tailGameObject);
280  }
281  }
282 
283 }
net.sf.gridarta.model.gameobject.GameObjectFactory
Abstract factory for creating GameObject instances.
Definition: GameObjectFactory.java:31
net.sf.gridarta.model.io.AbstractGameObjectParser.readArchName
static String readArchName(@NotNull final BufferedReader reader, @NotNull final String firstLine)
Reads an archetype name.
Definition: AbstractGameObjectParser.java:165
net.sf.gridarta.model.baseobject.BaseObject.isMulti
boolean isMulti()
Returns whether this Archetype is a multi-part object.
net.sf.gridarta.model.io.AbstractGameObjectParser.parseLine
boolean parseLine(@NotNull final String line, @NotNull final G gameObject)
Parses one line.
Definition: AbstractGameObjectParser.java:151
net.sf.gridarta.model.io.AbstractGameObjectParser.LOG
static final Category LOG
The Logger for printing log messages.
Definition: AbstractGameObjectParser.java:54
net.sf.gridarta.model.gameobject.GameObjectFactory.createGameObjectPart
G createGameObjectPart(@NotNull R archetype, @Nullable G head)
Creates a new GameObject from an Archetype.
net.sf.gridarta
Base package of all Gridarta classes.
net.sf.gridarta.model.baseobject.BaseObject.getMsgText
String getMsgText(boolean queryArchetype)
Returns the message bound to this object.
net.sf.gridarta.model.io.AbstractGameObjectParser.load
G load(@NotNull final BufferedReader reader, @Nullable final Collection< G > objects)
Definition: AbstractGameObjectParser.java:81
net.sf
net.sf.gridarta.model.archetype
Definition: AbstractArchetype.java:20
net.sf.gridarta.model.gameobject.GameObject
Reflects a game object (object on a map).
Definition: GameObject.java:36
net.sf.gridarta.model.io.GameObjectParser< G, A, R >::getModifiedFields
Map< String, String > getModifiedFields( @NotNull G gameObject)
Returns the modified fields of a GameObject.
net.sf.gridarta.model.io.AbstractGameObjectParser.addModifiedFields
void addModifiedFields(@NotNull final G gameObject, @NotNull final Map< String, String > fields)
Definition: AbstractGameObjectParser.java:220
net.sf.gridarta.model.baseobject.BaseObject.getArchetype
R getArchetype()
Returns the Archetype this GameObject is based on.
net.sf.gridarta.model.io.AbstractGameObjectParser
Abstract base class for classes implementing GameObjectParsers.
Definition: AbstractGameObjectParser.java:49
net.sf.gridarta.model.gameobject
GameObjects are the objects based on Archetypes found on maps.
Definition: AbstractGameObject.java:20
net
net.sf.gridarta.utils.StringUtils.PATTERN_END_OF_LINE
static final Pattern PATTERN_END_OF_LINE
The pattern to match end of line characters separating lines.
Definition: StringUtils.java:61
net.sf.gridarta.model.maparchobject.MapArchObject
Interface for MapArchObjects.
Definition: MapArchObject.java:40
net.sf.gridarta.model.gameobject.GameObjectFactory.createGameObject
G createGameObject(@NotNull R archetype)
Creates a new GameObject from an Archetype.
net.sf.gridarta.model.archetypeset.ArchetypeSet
Interface that captures similarities between different ArchetypeSet implementations.
Definition: ArchetypeSet.java:37
net.sf.gridarta.model.baseobject.BaseObject
Definition: BaseObject.java:34
net.sf.gridarta.utils.StringUtils
Utility class for string manipulation.
Definition: StringUtils.java:31
net.sf.gridarta.model.io.AbstractGameObjectParser.archetypeSet
final ArchetypeSet< G, A, R > archetypeSet
The ArchetypeSet for looking up archetypes.
Definition: AbstractGameObjectParser.java:66
net.sf.gridarta.model.io.AbstractGameObjectParser.gameObjectFactory
final GameObjectFactory< G, A, R > gameObjectFactory
The game object factory for creating new game object instances.
Definition: AbstractGameObjectParser.java:60
net.sf.gridarta.model
net.sf.gridarta.model.archetype.Archetype
Reflects an Archetype.
Definition: Archetype.java:41
net.sf.gridarta.model.baseobject
Definition: AbstractBaseObject.java:20
net.sf.gridarta.model.io.AbstractGameObjectParser.save
void save(@NotNull final Appendable appendable, @NotNull final G gameObject)
Definition: AbstractGameObjectParser.java:192
net.sf.gridarta.utils.StringUtils.PATTERN_SPACES
static final Pattern PATTERN_SPACES
The pattern that matches a non-empty sequence of spaces.
Definition: StringUtils.java:73
net.sf.gridarta.model.archetypeset.ArchetypeSet.getOrCreateArchetype
R getOrCreateArchetype(@NotNull String archetypeName)
Returns an archetype by its name.
net.sf.gridarta.model.io.AbstractGameObjectParser.load
G load(@NotNull final BufferedReader reader, @NotNull final String firstLine, @Nullable final Collection< G > objects)
Definition: AbstractGameObjectParser.java:92
net.sf.gridarta.model.maparchobject
Definition: AbstractMapArchObject.java:20
net.sf.gridarta.model.io.AbstractGameObjectParser.expandMulti
void expandMulti(@NotNull final G gameObject, @NotNull final Collection< G > objects)
If the given gameObject is a multi-part head, we generate the appropriate tail (from the archetype st...
Definition: AbstractGameObjectParser.java:261
net.sf.gridarta.model.io.AbstractGameObjectParser.AbstractGameObjectParser
AbstractGameObjectParser(@NotNull final GameObjectFactory< G, A, R > gameObjectFactory, @NotNull final ArchetypeSet< G, A, R > archetypeSet)
Creates a new instance.
Definition: AbstractGameObjectParser.java:74
net.sf.gridarta.model.baseobject.BaseObject.getMultiNext
T getMultiNext()
Returns the next of this multi-part object.
net.sf.gridarta.model.io.GameObjectParser
Interface for classes that read or write GameObject instances.
Definition: GameObjectParser.java:37
net.sf.gridarta.model.archetypeset
Definition: ArchetypeSet.java:20
net.sf.gridarta.utils
Definition: ActionBuilderUtils.java:20
net.sf.gridarta.model.io.AbstractGameObjectParser.collectTempList
void collectTempList(@NotNull final List< G > objects)
Definition: AbstractGameObjectParser.java:179