items = uidStore.getPlayer().getCurrentZone().getItems(p);
for (long uid : items) {
- Entity i = Engine.getStore().getEntity(uid);
+ Entity i = uidStore.getEntity(uid);
if (i instanceof Door) {
if (((Door) i).lock.getState() != Lock.OPEN) {
return DOOR;
@@ -147,16 +89,12 @@ public static byte move(Creature actor, Point p) {
}
}
- switch (mov) {
- case NONE:
- return walk(actor, p);
- case SWIM:
- return swim(actor, p);
- case CLIMB:
- return climb(actor, p);
- default:
- return BLOCKED;
- }
+ return switch (mov) {
+ case NONE -> walk(actor, p);
+ case SWIM -> swim(actor, p);
+ case CLIMB -> climb(actor, p);
+ default -> BLOCKED;
+ };
}
/**
@@ -167,11 +105,11 @@ public static byte move(Creature actor, Point p) {
* @param y
* @return the result of the movement
*/
- public static byte move(Creature creature, int x, int y) {
+ public byte move(Creature creature, int x, int y) {
return move(creature, new Point(x, y));
}
- private static byte swim(Creature swimmer, Point p) {
+ public byte swim(Creature swimmer, Point p) {
if (swimmer.species.habitat == Habitat.WATER) {
return OK;
} else if (SkillHandler.check(swimmer, Skill.SWIMMING) > 20) {
@@ -189,7 +127,7 @@ private static byte swim(Creature swimmer, Point p) {
*
* @param tile
*/
- private static byte climb(Creature climber, Point p) {
+ public static byte climb(Creature climber, Point p) {
if (climber.species.habitat == Habitat.WATER) {
return HABITAT;
}
@@ -202,7 +140,7 @@ private static byte climb(Creature climber, Point p) {
}
}
- private static byte walk(Creature walker, Point p) {
+ public static byte walk(Creature walker, Point p) {
Rectangle bounds = walker.getShapeComponent();
if (walker.species.habitat == Habitat.WATER) {
return HABITAT;
diff --git a/src/main/java/neon/core/handlers/TeleportHandler.java b/src/main/java/neon/core/handlers/TeleportHandler.java
new file mode 100644
index 0000000..e53f8d0
--- /dev/null
+++ b/src/main/java/neon/core/handlers/TeleportHandler.java
@@ -0,0 +1,97 @@
+package neon.core.handlers;
+
+import java.awt.*;
+import javax.swing.*;
+import neon.core.Engine;
+import neon.core.GameContext;
+import neon.core.GameStores;
+import neon.core.event.MessageEvent;
+import neon.entities.Door;
+import neon.entities.Entity;
+import neon.entities.Player;
+import neon.maps.*;
+import org.graalvm.polyglot.Context;
+
+public class TeleportHandler {
+ private final GameStores gameStores;
+ private final GameContext gameContext;
+ private final AtlasPosition atlasPosition;
+ private final Context scriptEngine;
+ private final MapLoader mapLoader;
+ private final MotionHandler motionHandler;
+
+ public TeleportHandler(
+ GameStores gameStores,
+ GameContext gameContext,
+ AtlasPosition atlasPosition,
+ Context scriptEngine) {
+ this.gameStores = gameStores;
+ this.gameContext = gameContext;
+ this.atlasPosition = atlasPosition;
+ this.scriptEngine = scriptEngine;
+ this.mapLoader =
+ new MapLoader(
+ gameStores.getFileSystem(),
+ gameStores.getStore(),
+ gameStores.getResources(),
+ gameStores.getZoneFactory());
+ this.motionHandler = new MotionHandler(gameStores.getStore());
+ }
+
+ /**
+ * Teleports a creature. Two results are possible:
+ *
+ *
+ * - OK - creature was teleported
+ *
- DOOR - this portal is just a door and does not support teleporting
+ *
+ *
+ * @param creature the creature to teleport.
+ * @param door the portal that the creature used
+ * @return the result
+ */
+ public byte teleport(Player creature, Door door) {
+ if (door.portal.isPortal()) {
+ Zone previous = atlasPosition.getCurrentZone(); // briefly buffer current zone
+ if (door.portal.getDestMap() != 0) {
+ // load map and have door refer back
+ Map map = gameStores.getAtlas().getMap(door.portal.getDestMap());
+ Zone zone = map.getZone(door.portal.getDestZone());
+ for (long uid : zone.getItems(door.portal.getDestPos())) {
+ Entity i = gameStores.getStore().getEntity(uid);
+ if (i instanceof Door) {
+ ((Door) i).portal.setDestMap(atlasPosition.getCurrentMap());
+ }
+ }
+ atlasPosition.setMap(map);
+ scriptEngine.getBindings("js").putMember("map", map);
+ door.portal.setDestMap(atlasPosition.getCurrentMap());
+ } else if (door.portal.getDestTheme() != null) {
+ Dungeon dungeon =
+ mapLoader.loadThemedDungeon(
+ door.portal.getDestTheme(), door.portal.getDestTheme(), door.portal.getDestZone());
+ atlasPosition.setMap(dungeon);
+ door.portal.setDestMap(atlasPosition.getCurrentMap());
+ }
+
+ atlasPosition.enterZone(door, previous, creature);
+
+ MotionHandler.walk(creature, door.portal.getDestPos());
+ // check if there is a door at the destination, if so, unlock and open this door
+ Rectangle bounds = creature.getShapeComponent();
+ for (long uid : atlasPosition.getCurrentZone().getItems(bounds)) {
+ Entity i = gameStores.getStore().getEntity(uid);
+ if (i instanceof Door) {
+ ((Door) i).lock.open();
+ }
+ }
+
+ // if there is a sign on the door, show it now
+ if (door.hasSign()) {
+ Engine.post(new MessageEvent(door, door.toString(), 3, SwingConstants.BOTTOM));
+ }
+ return MotionHandler.OK;
+ }
+ return MotionHandler.DOOR;
+ }
+}
diff --git a/src/main/java/neon/core/handlers/TurnHandler.java b/src/main/java/neon/core/handlers/TurnHandler.java
index b091352..a133615 100644
--- a/src/main/java/neon/core/handlers/TurnHandler.java
+++ b/src/main/java/neon/core/handlers/TurnHandler.java
@@ -22,6 +22,8 @@
import java.util.Collection;
import lombok.extern.slf4j.Slf4j;
import neon.core.Configuration;
+import neon.core.GameContext;
+import neon.core.GameStores;
import neon.core.event.TurnEvent;
import neon.core.event.UpdateEvent;
import neon.entities.Creature;
@@ -33,10 +35,6 @@
import neon.maps.Region.Modifier;
import neon.maps.generators.TownGenerator;
import neon.maps.generators.WildernessGenerator;
-import neon.maps.services.EntityStore;
-import neon.maps.services.GameContextEntityStore;
-import neon.maps.services.GameContextResourceProvider;
-import neon.maps.services.ResourceProvider;
import neon.resources.CServer;
import neon.resources.RRegionTheme;
import neon.ui.GamePanel;
@@ -47,18 +45,21 @@
@Listener(references = References.Strong) // strong, om gc te vermijden
@Slf4j
public class TurnHandler {
- private GamePanel panel;
+ private final GamePanel panel;
private Generator generator;
- private int range;
- private final EntityStore entityStore;
- private final ResourceProvider resourceProvider;
+ private final int range;
- public TurnHandler(GamePanel panel) {
+ private final GameStores gameStores;
+ private final InventoryHandler inventoryHandler;
+ private final GameContext gameContext;
+
+ public TurnHandler(GamePanel panel, GameStores gameStores, GameContext gameContext) {
this.panel = panel;
- this.entityStore = new GameContextEntityStore(panel.getContext());
- this.resourceProvider = new GameContextResourceProvider(panel.getContext());
- CServer ini = (CServer) panel.getContext().getResources().getResource("ini", "config");
+ this.gameStores = gameStores;
+ inventoryHandler = new InventoryHandler(gameStores.getStore());
+ this.gameContext = gameContext;
+ CServer ini = (CServer) gameStores.getResources().getResource("ini", "config");
range = ini.getAIRange();
}
@@ -81,9 +82,9 @@ public void tick(TurnEvent te) {
}
// monsters controleren
- Player player = panel.getContext().getPlayer();
- for (long uid : panel.getContext().getAtlas().getCurrentZone().getCreatures()) {
- Creature creature = (Creature) panel.getContext().getStore().getEntity(uid);
+ Player player = panel.getUidStore().getPlayer();
+ for (long uid : panel.getGameContext().getAtlasPosition().getCurrentZone().getCreatures()) {
+ Creature creature = (Creature) gameStores.getStore().getEntity(uid);
if (!creature.hasCondition(Condition.DEAD)) {
HealthComponent health = creature.getHealthComponent();
health.heal(creature.getStatsComponent().getCon() / 100f);
@@ -93,7 +94,11 @@ public void tick(TurnEvent te) {
if (pBounds.getLocation().distance(cBounds.getLocation()) < range) {
int spd = getSpeed(creature);
Region region =
- panel.getContext().getAtlas().getCurrentZone().getRegion(cBounds.getLocation());
+ panel
+ .getGameContext()
+ .getAtlasPosition()
+ .getCurrentZone()
+ .getRegion(cBounds.getLocation());
if (creature.species.habitat == Habitat.LAND && region.getMovMod() == Modifier.SWIM) {
spd = spd / 4; // zwemmende creatures hebben penalty
}
@@ -115,8 +120,8 @@ public void tick(TurnEvent te) {
player.getMagicComponent().addMana(player.getStatsComponent().getWis() / 100f);
// en systems updaten
- panel.getContext().getPhysicsEngine().update();
- panel.getContext().post(new UpdateEvent(this));
+ panel.getGameContext().getPhysicsEngine().update();
+ panel.getGameContext().post(new UpdateEvent(this));
}
private class Generator extends Thread {
@@ -124,7 +129,7 @@ private class Generator extends Thread {
public void run() {
// enkel repainten nadat er iets gegenereerd is
if (checkRegions()) {
- panel.getContext().post(new UpdateEvent(this));
+ panel.getGameContext().post(new UpdateEvent(this));
}
}
}
@@ -134,7 +139,7 @@ public void run() {
*/
private boolean checkRegions() { // die boolean is eigenlijk maar louche
Rectangle window = panel.getVisibleRectangle();
- Zone zone = panel.getContext().getAtlas().getCurrentZone();
+ Zone zone = panel.getGameContext().getAtlasPosition().getCurrentZone();
boolean fixed = true;
boolean generated = false; // om aan te geven dat er iets gegenereerd werd
@@ -148,14 +153,14 @@ private boolean checkRegions() { // die boolean is eigenlijk maar louche
RRegionTheme theme = r.getTheme();
r.fix(); // vanaf hier wordt theme null
if (theme.id.startsWith("town")) {
- new TownGenerator(zone, entityStore, resourceProvider)
+ new TownGenerator(zone, gameStores.getStore(), gameStores.getResources())
.generate(r.getX(), r.getY(), r.getWidth(), r.getHeight(), theme, r.getZ());
} else {
- new WildernessGenerator(zone, entityStore, resourceProvider).generate(r, theme);
+ new WildernessGenerator(zone, gameStores).generate(r, theme);
}
}
}
- } while (fixed == false);
+ } while (!fixed);
return generated;
}
@@ -163,13 +168,13 @@ private boolean checkRegions() { // die boolean is eigenlijk maar louche
/*
* @return a creature's speed
*/
- private static int getSpeed(Creature creature) {
+ private int getSpeed(Creature creature) {
int penalty = 3;
- if (InventoryHandler.getWeight(creature) > 9 * creature.species.str) {
+ if (inventoryHandler.getWeight(creature) > 9 * creature.species.str) {
return 0;
- } else if (InventoryHandler.getWeight(creature) > 6 * creature.species.str) {
+ } else if (inventoryHandler.getWeight(creature) > 6 * creature.species.str) {
penalty = 1;
- } else if (InventoryHandler.getWeight(creature) > 3 * creature.species.str) {
+ } else if (inventoryHandler.getWeight(creature) > 3 * creature.species.str) {
penalty = 2;
}
return (creature.getStatsComponent().getSpd()) * penalty / 3;
diff --git a/src/main/java/neon/core/model/NeonConfig.java b/src/main/java/neon/core/model/NeonConfig.java
new file mode 100644
index 0000000..ca8e266
--- /dev/null
+++ b/src/main/java/neon/core/model/NeonConfig.java
@@ -0,0 +1,60 @@
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2026 - Peter Riewe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.core.model;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+
+/**
+ * Jackson model for neon.ini.xml configuration file.
+ *
+ * @author priewe
+ */
+@JacksonXmlRootElement(localName = "root")
+public class NeonConfig {
+
+ @JacksonXmlProperty(localName = "files")
+ public FilesElement files = new FilesElement();
+
+ @JacksonXmlProperty(localName = "threads")
+ public ThreadsElement threads = new ThreadsElement();
+
+ @JacksonXmlProperty(localName = "ai")
+ public String ai;
+
+ @JacksonXmlProperty(localName = "log")
+ public String log;
+
+ @JacksonXmlProperty(localName = "lang")
+ public String lang;
+
+ @JacksonXmlProperty(localName = "keys")
+ public String keys;
+
+ /** Empty files element */
+ public static class FilesElement {
+ // Empty element placeholder
+ }
+
+ /** Threads configuration */
+ public static class ThreadsElement {
+ @JacksonXmlProperty(isAttribute = true, localName = "generate")
+ public String generate;
+ }
+}
diff --git a/src/main/java/neon/core/model/SaveGameModel.java b/src/main/java/neon/core/model/SaveGameModel.java
new file mode 100644
index 0000000..68d234a
--- /dev/null
+++ b/src/main/java/neon/core/model/SaveGameModel.java
@@ -0,0 +1,337 @@
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2026 - Peter Riewe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.core.model;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Jackson model for save game XML structure.
+ *
+ * This class represents the parsed XML structure of a save game file. It is designed to separate
+ * XML parsing (Jackson's responsibility) from game object construction (GameLoader's
+ * responsibility).
+ *
+ * @author priewe
+ */
+@JacksonXmlRootElement(localName = "save")
+public class SaveGameModel {
+
+ @JacksonXmlProperty(isAttribute = true, localName = "version")
+ public String version = "2.0"; // Add versioning for future compatibility
+
+ @JacksonXmlProperty(localName = "player")
+ public PlayerSaveData player;
+
+ @JacksonXmlProperty(localName = "journal")
+ public JournalData journal = new JournalData();
+
+ @JacksonXmlProperty(localName = "events")
+ public EventsData events = new EventsData();
+
+ @JacksonXmlProperty(localName = "timer")
+ public TimerData timer;
+
+ @JacksonXmlProperty(localName = "quests")
+ public QuestsData quests; // Optional - null if no random quests
+
+ /** Player save data */
+ public static class PlayerSaveData {
+ @JacksonXmlProperty(isAttribute = true, localName = "name")
+ public String name;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "race")
+ public String race;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "gender")
+ public String gender;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "spec")
+ public String specialisation;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "prof")
+ public String profession;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "sign")
+ public String sign;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "map")
+ public int map;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "l")
+ public int level;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "x")
+ public int x;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "y")
+ public int y;
+
+ @JacksonXmlProperty(localName = "skills")
+ public SkillsData skills;
+
+ @JacksonXmlProperty(localName = "stats")
+ public StatsData stats;
+
+ @JacksonXmlProperty(localName = "money")
+ public MoneyData money;
+
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "item")
+ public List items = new ArrayList<>();
+
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "spell")
+ public List spells = new ArrayList<>();
+
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "feat")
+ public List feats = new ArrayList<>();
+ }
+
+ /** Skills data stored as XML attributes */
+ public static class SkillsData {
+ // Skills are stored as XML attributes - use @JacksonXmlProperty for each skill
+ // All 38 skills from the Skill enum
+ @JacksonXmlProperty(isAttribute = true, localName = "CREATION")
+ public Float CREATION;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "DESTRUCTION")
+ public Float DESTRUCTION;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "RESTORATION")
+ public Float RESTORATION;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "ALTERATION")
+ public Float ALTERATION;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "ILLUSION")
+ public Float ILLUSION;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "ENCHANT")
+ public Float ENCHANT;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "ALCHEMY")
+ public Float ALCHEMY;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "CONJURATION")
+ public Float CONJURATION;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "ARCHERY")
+ public Float ARCHERY;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "AXE")
+ public Float AXE;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "BLUNT")
+ public Float BLUNT;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "BLADE")
+ public Float BLADE;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "SPEAR")
+ public Float SPEAR;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "UNARMED")
+ public Float UNARMED;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "CLIMBING")
+ public Float CLIMBING;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "SWIMMING")
+ public Float SWIMMING;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "SNEAK")
+ public Float SNEAK;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "HEAVY_ARMOR")
+ public Float HEAVY_ARMOR;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "MEDIUM_ARMOR")
+ public Float MEDIUM_ARMOR;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "LIGHT_ARMOR")
+ public Float LIGHT_ARMOR;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "DODGING")
+ public Float DODGING;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "BLOCK")
+ public Float BLOCK;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "UNARMORED")
+ public Float UNARMORED;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "MERCANTILE")
+ public Float MERCANTILE;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "PICKPOCKET")
+ public Float PICKPOCKET;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "ARMORER")
+ public Float ARMORER;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "LOCKPICKING")
+ public Float LOCKPICKING;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "MEDICAL")
+ public Float MEDICAL;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "DISABLE")
+ public Float DISABLE;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "SPEECHCRAFT")
+ public Float SPEECHCRAFT;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "PERFORM")
+ public Float PERFORM;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "DISGUISE")
+ public Float DISGUISE;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "RIDING")
+ public Float RIDING;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "NONE")
+ public Float NONE;
+ }
+
+ /** Stats data */
+ public static class StatsData {
+ @JacksonXmlProperty(isAttribute = true, localName = "str")
+ public int str;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "con")
+ public int con;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "dex")
+ public int dex;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "int")
+ public int int_;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "wis")
+ public int wis;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "cha")
+ public int cha;
+ }
+
+ /** Money data */
+ public static class MoneyData {
+ @JacksonXmlText public int value;
+ }
+
+ /** Item reference */
+ public static class ItemReference {
+ @JacksonXmlProperty(isAttribute = true, localName = "uid")
+ public long uid;
+ }
+
+ /** Spell reference */
+ public static class SpellReference {
+ @JacksonXmlText public String id;
+ }
+
+ /** Feat reference */
+ public static class FeatReference {
+ @JacksonXmlText public String name;
+ }
+
+ /** Journal data */
+ public static class JournalData {
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "quest")
+ public List quests = new ArrayList<>();
+ }
+
+ /** Quest entry */
+ public static class QuestEntry {
+ @JacksonXmlProperty(isAttribute = true, localName = "id")
+ public String id;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "stage")
+ public int stage;
+
+ @JacksonXmlText public String subject;
+ }
+
+ /** Events data */
+ public static class EventsData {
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "task")
+ public List tasks = new ArrayList<>();
+
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "timer")
+ public List timerEvents = new ArrayList<>();
+ }
+
+ /** Task event */
+ public static class TaskEvent {
+ @JacksonXmlProperty(isAttribute = true, localName = "desc")
+ public String description;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "script")
+ public String script;
+ }
+
+ /** Timer event */
+ public static class TimerEvent {
+ @JacksonXmlProperty(isAttribute = true, localName = "tick")
+ public String tick; // Format: "start:period:stop"
+
+ @JacksonXmlProperty(isAttribute = true, localName = "task")
+ public String taskType; // "script" or "magic"
+
+ @JacksonXmlProperty(isAttribute = true, localName = "script")
+ public String script;
+
+ // Magic task attributes
+ @JacksonXmlProperty(isAttribute = true, localName = "effect")
+ public String effect;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "target")
+ public Long target;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "caster")
+ public Long caster;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "stype")
+ public String spellType;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "mag")
+ public Float magnitude;
+ }
+
+ /** Timer data */
+ public static class TimerData {
+ @JacksonXmlProperty(isAttribute = true, localName = "ticks")
+ public int ticks;
+ }
+
+ /** Quests data (for random quests - currently unused) */
+ public static class QuestsData {
+ // Empty for now, placeholder for future random quest saving
+ }
+}
diff --git a/src/main/java/neon/editor/CCEditor.java b/src/main/java/neon/editor/CCEditor.java
index 2efdaa2..9c0644f 100644
--- a/src/main/java/neon/editor/CCEditor.java
+++ b/src/main/java/neon/editor/CCEditor.java
@@ -1,408 +1,412 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2013 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-import java.util.List;
-import javax.swing.*;
-import javax.swing.border.*;
-import javax.swing.event.*;
-import neon.editor.help.HelpLabels;
-import neon.editor.resources.RMap;
-import neon.editor.resources.RZone;
-import neon.resources.RCreature;
-import neon.resources.RCreature.Type;
-import neon.resources.RItem;
-import neon.resources.RMod;
-import neon.resources.RSpell;
-import neon.resources.RSpell.SpellType;
-
-public class CCEditor
- implements ActionListener, ItemListener, ListSelectionListener, MouseListener {
- private JDialog frame;
- private JCheckBox raceBox;
- private JFormattedTextField xField, yField;
- private JComboBox mapBox;
- private JComboBox zoneBox;
- private HashMap races;
- private JList raceList;
- private JList itemList;
- private JList spellList;
- private RCreature currentRace;
- private DefaultListModel spellListModel;
- private DefaultListModel itemListModel;
- private String[] spells;
- private JPanel raceEditPanel;
-
- public CCEditor(JFrame parent) {
- frame = new JDialog(parent, "Character Creation Editor", true); // modal dialog
- JPanel content = new JPanel(new BorderLayout());
- frame.setPreferredSize(new Dimension(400, 300));
- frame.setContentPane(content);
- JTabbedPane tabs = new JTabbedPane();
- content.add(tabs, BorderLayout.CENTER);
-
- // races
- JPanel racePanel = new JPanel(new GridLayout(0, 2));
- raceList = new JList();
- raceList.addListSelectionListener(this);
- JScrollPane raceScroller = new JScrollPane(raceList);
- raceScroller.setBorder(new TitledBorder("Races"));
- racePanel.add(raceScroller);
- raceEditPanel = new JPanel();
- raceEditPanel.setBorder(new TitledBorder("Edit"));
- raceBox = new JCheckBox("Playable");
- raceBox.addActionListener(this);
- raceEditPanel.add(raceBox);
- racePanel.add(raceEditPanel);
-
- // position
- JPanel mapPanel = new JPanel();
- mapPanel.setBorder(new EmptyBorder(20, 20, 20, 20));
- GroupLayout layout = new GroupLayout(mapPanel);
- mapPanel.setLayout(layout);
- layout.setAutoCreateGaps(true);
-
- JLabel mapLabel = new JLabel("Map: ");
- JLabel zoneLabel = new JLabel("Zone: ");
- JLabel xLabel = new JLabel("x-coordinate: ");
- JLabel yLabel = new JLabel("y-coordinate: ");
- mapBox = new JComboBox();
- mapBox.addItemListener(this);
- xField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- yField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- zoneBox = new JComboBox();
- JLabel mapHelpLabel = HelpLabels.getStartMapHelpLabel();
- JLabel zoneHelpLabel = HelpLabels.getStartZoneLabel();
- JLabel xHelpLabel = HelpLabels.getStartXLabel();
- JLabel yHelpLabel = HelpLabels.getStartYLabel();
- layout.setVerticalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(mapLabel)
- .addComponent(mapBox)
- .addComponent(mapHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(zoneLabel)
- .addComponent(zoneBox)
- .addComponent(zoneHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(xLabel)
- .addComponent(xField)
- .addComponent(xHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(yLabel)
- .addComponent(yField)
- .addComponent(yHelpLabel))
- .addGap(10));
- layout.setHorizontalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING)
- .addComponent(mapLabel)
- .addComponent(zoneLabel)
- .addComponent(xLabel)
- .addComponent(yLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(
- mapBox,
- GroupLayout.PREFERRED_SIZE,
- GroupLayout.DEFAULT_SIZE,
- GroupLayout.PREFERRED_SIZE)
- .addComponent(zoneBox)
- .addComponent(xField)
- .addComponent(yField))
- .addGap(10)
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(mapHelpLabel)
- .addComponent(zoneHelpLabel)
- .addComponent(xHelpLabel)
- .addComponent(yHelpLabel)));
-
- // starting items
- JPanel itemPanel = new JPanel(new BorderLayout());
- itemListModel = new DefaultListModel();
- itemList = new JList(itemListModel);
- itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- itemPanel.add(new JScrollPane(itemList));
-
- // starting spells
- JPanel spellPanel = new JPanel(new BorderLayout());
- spellListModel = new DefaultListModel();
- spellList = new JList(spellListModel);
- spellList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- spellPanel.add(new JScrollPane(spellList));
-
- tabs.add(racePanel, "Playable races");
- tabs.add(mapPanel, "Starting position");
- tabs.add(itemPanel, "Starting items");
- tabs.add(spellPanel, "Starting spells");
-
- JPanel buttons = new JPanel();
- content.add(buttons, BorderLayout.PAGE_END);
- JButton ok = new JButton("Ok");
- ok.addActionListener(this);
- JButton apply = new JButton("Apply");
- apply.addActionListener(this);
- JButton cancel = new JButton("Cancel");
- cancel.addActionListener(this);
- buttons.add(ok);
-
- itemList.addMouseListener(this);
- spellList.addMouseListener(this);
- buttons.add(cancel);
- buttons.add(apply);
- }
-
- public void show() {
- DataStore store = Editor.getStore();
- RMod mod = store.getActive();
- List playableRaces = new ArrayList();
- // if species does not exist, automatically remove it
- for (String race : mod.getList("races")) {
- if (Editor.resources.getResource(race) instanceof RCreature) {
- playableRaces.add((RCreature) Editor.resources.getResource(race));
- } else {
- mod.ccRaces.remove(race);
- }
- }
- itemListModel.clear();
- // if item does not exist, automatically remove it
- for (String item : mod.getList("items")) {
- if (Editor.resources.getResource(item) instanceof RItem) {
- itemListModel.addElement((RItem) Editor.resources.getResource(item));
- } else {
- mod.ccRaces.remove(item);
- }
- }
- spellListModel.clear();
- // if spell does not exist, automatically remove it
- for (String spell : mod.getList("spells")) {
- if (Editor.resources.getResource(spell, "magic") != null) {
- spellListModel.addElement((RSpell) Editor.resources.getResource(spell, "magic"));
- } else {
- mod.ccRaces.remove(spell);
- }
- }
-
- races = new HashMap();
- for (RCreature rc : Editor.resources.getResources(RCreature.class)) {
- // only humanoids or goblins
- if (rc.type == Type.humanoid || rc.type == Type.goblin) {
- races.put(rc, playableRaces.contains(rc));
- }
- }
- raceList.setListData(races.keySet().toArray(new RCreature[0]));
-
- for (RMap map : Editor.resources.getResources(RMap.class)) {
- if (map.theme == null) { // do not allow random maps
- mapBox.addItem(map);
- }
- }
- RMap map = (RMap) Editor.resources.getResource(mod.get("map"), "maps");
- mapBox.setSelectedItem(map);
- for (RZone zone : map.zones.values()) {
- if (zone.theme == null) { // do not allow random zones
- zoneBox.addItem(zone);
- }
- }
- zoneBox.setSelectedItem(map.getZone(Integer.parseInt(mod.get("z"))));
- xField.setValue(Integer.parseInt(mod.get("x")));
- yField.setValue(Integer.parseInt(mod.get("y")));
-
- ArrayList temp = new ArrayList();
- for (RSpell spell : Editor.resources.getResources(RSpell.class)) {
- if (spell.type == SpellType.SPELL) {
- temp.add(spell.id);
- }
- }
- spells = temp.toArray(new String[temp.size()]);
-
- frame.pack();
- frame.setLocationRelativeTo(null);
- frame.setVisible(true);
- }
-
- private void saveAll() {
- RMod mod = Editor.getStore().getActive();
- if (mapBox.getSelectedItem() != null) {
- RMap map = (RMap) mapBox.getSelectedItem();
- mod.set("map", map.id);
- RZone zone = (RZone) zoneBox.getSelectedItem();
- mod.set("z", Integer.toString(map.getZone(zone)));
- mod.set("x", xField.getText());
- mod.set("y", yField.getText());
- } else {
- JOptionPane.showMessageDialog(frame, "No start map selected!");
- }
-
- mod.ccRaces.clear();
- for (RCreature rc : races.keySet()) {
- if (races.get(rc)) {
- mod.ccRaces.add(rc.id);
- }
- }
- mod.ccItems.clear();
- for (Enumeration e = itemListModel.elements(); e.hasMoreElements(); ) {
- RItem ri = e.nextElement();
- mod.ccItems.add(ri.id);
- }
- mod.ccSpells.clear();
- for (Enumeration e = spellListModel.elements(); e.hasMoreElements(); ) {
- RSpell rs = e.nextElement();
- mod.ccSpells.add(rs.id);
- }
- }
-
- public void actionPerformed(ActionEvent e) {
- if ("Playable".equals(e.getActionCommand())) {
- races.put(currentRace, raceBox.isSelected());
- } else if ("Ok".equals(e.getActionCommand())) {
- saveAll();
- frame.dispose();
- } else if ("Cancel".equals(e.getActionCommand())) {
- frame.dispose();
- } else if ("Apply".equals(e.getActionCommand())) {
- saveAll();
- }
- }
-
- public void mouseExited(MouseEvent e) {}
-
- public void mouseEntered(MouseEvent e) {}
-
- public void mouseReleased(MouseEvent e) {}
-
- public void mousePressed(MouseEvent e) {}
-
- public void mouseClicked(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON3) {
- if (e.getComponent() == spellList) {
- JPopupMenu menu = new JPopupMenu();
- menu.add(new SpellListAction("Add spell"));
- menu.add(new SpellListAction("Delete spell"));
- menu.show(e.getComponent(), e.getX(), e.getY());
- spellList.setSelectedIndex(spellList.locationToIndex(e.getPoint()));
- } else if (e.getComponent() == itemList) {
- JPopupMenu menu = new JPopupMenu();
- menu.add(new ItemListAction("Add item"));
- menu.add(new ItemListAction("Delete item"));
- menu.show(e.getComponent(), e.getX(), e.getY());
- itemList.setSelectedIndex(itemList.locationToIndex(e.getPoint()));
- }
- }
- }
-
- public void valueChanged(ListSelectionEvent e) {
- // apparently two events are fired on selection
- if (e.getValueIsAdjusting()) {
- currentRace = raceList.getSelectedValue();
- raceBox.setSelected(races.get(currentRace));
- raceEditPanel.setBorder(new TitledBorder(currentRace.id));
- }
- }
-
- public void itemStateChanged(ItemEvent e) {
- if (mapBox.equals(e.getSource())) {
- // load zones
- zoneBox.setModel(new DefaultComboBoxModel());
- RMap map = (RMap) mapBox.getSelectedItem();
- if (map != null) {
- for (RZone zone : map.zones.values()) {
- if (zone.theme == null) { // do not allow random zones
- zoneBox.addItem(zone);
- }
- }
- }
- frame.pack();
- }
- }
-
- @SuppressWarnings("serial")
- private class SpellListAction extends AbstractAction {
- public SpellListAction(String name) {
- super(name);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("Add spell")) {
- RSpell rs =
- (RSpell)
- JOptionPane.showInputDialog(
- neon.editor.Editor.getFrame(),
- "Add spell:",
- "Add spell",
- JOptionPane.PLAIN_MESSAGE,
- null,
- spells,
- 0);
- if (rs != null) {
- spellListModel.addElement(rs);
- }
- } else if (e.getActionCommand().equals("Delete spell")) {
- spellListModel.remove(spellList.getSelectedIndex());
- }
- }
- }
-
- @SuppressWarnings("serial")
- private class ItemListAction extends AbstractAction {
- public ItemListAction(String name) {
- super(name);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("Add item")) {
- Object[] items = Editor.resources.getResources(RItem.class).toArray();
- RItem ri =
- (RItem)
- JOptionPane.showInputDialog(
- neon.editor.Editor.getFrame(),
- "Add item:",
- "Add item",
- JOptionPane.PLAIN_MESSAGE,
- null,
- items,
- 0);
- if (ri != null) {
- itemListModel.addElement(ri);
- }
- } else if (e.getActionCommand().equals("Delete item")) {
- itemListModel.remove(itemList.getSelectedIndex());
- }
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2013 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import neon.editor.help.HelpLabels;
+import neon.editor.resources.RMap;
+import neon.editor.resources.RZone;
+import neon.resources.RCreature;
+import neon.resources.RCreature.Type;
+import neon.resources.RItem;
+import neon.resources.RMod;
+import neon.resources.RSpell;
+import neon.resources.RSpell.SpellType;
+
+public class CCEditor
+ implements ActionListener, ItemListener, ListSelectionListener, MouseListener {
+ private final JDialog frame;
+ private final JCheckBox raceBox;
+ private final JFormattedTextField xField;
+ private final JFormattedTextField yField;
+ private final JComboBox mapBox;
+ private final JComboBox zoneBox;
+ private HashMap races;
+ private final JList raceList;
+ private final JList itemList;
+ private final JList spellList;
+ private RCreature currentRace;
+ private final DefaultListModel spellListModel;
+ private final DefaultListModel itemListModel;
+ private String[] spells;
+ private final JPanel raceEditPanel;
+ private final DataStore dataStore;
+
+ public CCEditor(JFrame parent, DataStore dataStore) {
+ this.dataStore = dataStore;
+ frame = new JDialog(parent, "Character Creation Editor", true); // modal dialog
+ JPanel content = new JPanel(new BorderLayout());
+ frame.setPreferredSize(new Dimension(400, 300));
+ frame.setContentPane(content);
+ JTabbedPane tabs = new JTabbedPane();
+ content.add(tabs, BorderLayout.CENTER);
+
+ // races
+ JPanel racePanel = new JPanel(new GridLayout(0, 2));
+ raceList = new JList();
+ raceList.addListSelectionListener(this);
+ JScrollPane raceScroller = new JScrollPane(raceList);
+ raceScroller.setBorder(new TitledBorder("Races"));
+ racePanel.add(raceScroller);
+ raceEditPanel = new JPanel();
+ raceEditPanel.setBorder(new TitledBorder("Edit"));
+ raceBox = new JCheckBox("Playable");
+ raceBox.addActionListener(this);
+ raceEditPanel.add(raceBox);
+ racePanel.add(raceEditPanel);
+
+ // position
+ JPanel mapPanel = new JPanel();
+ mapPanel.setBorder(new EmptyBorder(20, 20, 20, 20));
+ GroupLayout layout = new GroupLayout(mapPanel);
+ mapPanel.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+
+ JLabel mapLabel = new JLabel("Map: ");
+ JLabel zoneLabel = new JLabel("Zone: ");
+ JLabel xLabel = new JLabel("x-coordinate: ");
+ JLabel yLabel = new JLabel("y-coordinate: ");
+ mapBox = new JComboBox();
+ mapBox.addItemListener(this);
+ xField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ yField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ zoneBox = new JComboBox();
+ JLabel mapHelpLabel = HelpLabels.getStartMapHelpLabel();
+ JLabel zoneHelpLabel = HelpLabels.getStartZoneLabel();
+ JLabel xHelpLabel = HelpLabels.getStartXLabel();
+ JLabel yHelpLabel = HelpLabels.getStartYLabel();
+ layout.setVerticalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(mapLabel)
+ .addComponent(mapBox)
+ .addComponent(mapHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(zoneLabel)
+ .addComponent(zoneBox)
+ .addComponent(zoneHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(xLabel)
+ .addComponent(xField)
+ .addComponent(xHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(yLabel)
+ .addComponent(yField)
+ .addComponent(yHelpLabel))
+ .addGap(10));
+ layout.setHorizontalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(mapLabel)
+ .addComponent(zoneLabel)
+ .addComponent(xLabel)
+ .addComponent(yLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(
+ mapBox,
+ GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE,
+ GroupLayout.PREFERRED_SIZE)
+ .addComponent(zoneBox)
+ .addComponent(xField)
+ .addComponent(yField))
+ .addGap(10)
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(mapHelpLabel)
+ .addComponent(zoneHelpLabel)
+ .addComponent(xHelpLabel)
+ .addComponent(yHelpLabel)));
+
+ // starting items
+ JPanel itemPanel = new JPanel(new BorderLayout());
+ itemListModel = new DefaultListModel();
+ itemList = new JList(itemListModel);
+ itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ itemPanel.add(new JScrollPane(itemList));
+
+ // starting spells
+ JPanel spellPanel = new JPanel(new BorderLayout());
+ spellListModel = new DefaultListModel();
+ spellList = new JList(spellListModel);
+ spellList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ spellPanel.add(new JScrollPane(spellList));
+
+ tabs.add(racePanel, "Playable races");
+ tabs.add(mapPanel, "Starting position");
+ tabs.add(itemPanel, "Starting items");
+ tabs.add(spellPanel, "Starting spells");
+
+ JPanel buttons = new JPanel();
+ content.add(buttons, BorderLayout.PAGE_END);
+ JButton ok = new JButton("Ok");
+ ok.addActionListener(this);
+ JButton apply = new JButton("Apply");
+ apply.addActionListener(this);
+ JButton cancel = new JButton("Cancel");
+ cancel.addActionListener(this);
+ buttons.add(ok);
+
+ itemList.addMouseListener(this);
+ spellList.addMouseListener(this);
+ buttons.add(cancel);
+ buttons.add(apply);
+ }
+
+ public void show() {
+ DataStore store = dataStore;
+ RMod mod = store.getActive();
+ List playableRaces = new ArrayList();
+ // if species does not exist, automatically remove it
+ for (String race : mod.getList("races")) {
+ if (dataStore.getResourceManager().getResource(race) instanceof RCreature) {
+ playableRaces.add((RCreature) dataStore.getResourceManager().getResource(race));
+ } else {
+ mod.ccRaces.remove(race);
+ }
+ }
+ itemListModel.clear();
+ // if item does not exist, automatically remove it
+ for (String item : mod.getList("items")) {
+ if (dataStore.getResourceManager().getResource(item) instanceof RItem) {
+ itemListModel.addElement((RItem) dataStore.getResourceManager().getResource(item));
+ } else {
+ mod.ccRaces.remove(item);
+ }
+ }
+ spellListModel.clear();
+ // if spell does not exist, automatically remove it
+ for (String spell : mod.getList("spells")) {
+ if (dataStore.getResourceManager().getResource(spell, "magic") != null) {
+ spellListModel.addElement(
+ (RSpell) dataStore.getResourceManager().getResource(spell, "magic"));
+ } else {
+ mod.ccRaces.remove(spell);
+ }
+ }
+
+ races = new HashMap();
+ for (RCreature rc : dataStore.getResourceManager().getResources(RCreature.class)) {
+ // only humanoids or goblins
+ if (rc.type == Type.humanoid || rc.type == Type.goblin) {
+ races.put(rc, playableRaces.contains(rc));
+ }
+ }
+ raceList.setListData(races.keySet().toArray(new RCreature[0]));
+
+ for (RMap map : dataStore.getResourceManager().getResources(RMap.class)) {
+ if (map.theme == null) { // do not allow random maps
+ mapBox.addItem(map);
+ }
+ }
+ RMap map = (RMap) dataStore.getResourceManager().getResource(mod.get("map"), "maps");
+ mapBox.setSelectedItem(map);
+ for (RZone zone : map.zones.values()) {
+ if (zone.theme == null) { // do not allow random zones
+ zoneBox.addItem(zone);
+ }
+ }
+ zoneBox.setSelectedItem(map.getZone(Integer.parseInt(mod.get("z"))));
+ xField.setValue(Integer.parseInt(mod.get("x")));
+ yField.setValue(Integer.parseInt(mod.get("y")));
+
+ ArrayList temp = new ArrayList();
+ for (RSpell spell : dataStore.getResourceManager().getResources(RSpell.class)) {
+ if (spell.type == SpellType.SPELL) {
+ temp.add(spell.id);
+ }
+ }
+ spells = temp.toArray(new String[temp.size()]);
+
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ private void saveAll() {
+ RMod mod = dataStore.getActive();
+ if (mapBox.getSelectedItem() != null) {
+ RMap map = (RMap) mapBox.getSelectedItem();
+ mod.set("map", map.id);
+ RZone zone = (RZone) zoneBox.getSelectedItem();
+ mod.set("z", Integer.toString(map.getZone(zone)));
+ mod.set("x", xField.getText());
+ mod.set("y", yField.getText());
+ } else {
+ JOptionPane.showMessageDialog(frame, "No start map selected!");
+ }
+
+ mod.ccRaces.clear();
+ for (RCreature rc : races.keySet()) {
+ if (races.get(rc)) {
+ mod.ccRaces.add(rc.id);
+ }
+ }
+ mod.ccItems.clear();
+ for (Enumeration e = itemListModel.elements(); e.hasMoreElements(); ) {
+ RItem ri = e.nextElement();
+ mod.ccItems.add(ri.id);
+ }
+ mod.ccSpells.clear();
+ for (Enumeration e = spellListModel.elements(); e.hasMoreElements(); ) {
+ RSpell rs = e.nextElement();
+ mod.ccSpells.add(rs.id);
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if ("Playable".equals(e.getActionCommand())) {
+ races.put(currentRace, raceBox.isSelected());
+ } else if ("Ok".equals(e.getActionCommand())) {
+ saveAll();
+ frame.dispose();
+ } else if ("Cancel".equals(e.getActionCommand())) {
+ frame.dispose();
+ } else if ("Apply".equals(e.getActionCommand())) {
+ saveAll();
+ }
+ }
+
+ public void mouseExited(MouseEvent e) {}
+
+ public void mouseEntered(MouseEvent e) {}
+
+ public void mouseReleased(MouseEvent e) {}
+
+ public void mousePressed(MouseEvent e) {}
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ if (e.getComponent() == spellList) {
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new SpellListAction("Add spell"));
+ menu.add(new SpellListAction("Delete spell"));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ spellList.setSelectedIndex(spellList.locationToIndex(e.getPoint()));
+ } else if (e.getComponent() == itemList) {
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new ItemListAction("Add item"));
+ menu.add(new ItemListAction("Delete item"));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ itemList.setSelectedIndex(itemList.locationToIndex(e.getPoint()));
+ }
+ }
+ }
+
+ public void valueChanged(ListSelectionEvent e) {
+ // apparently two events are fired on selection
+ if (e.getValueIsAdjusting()) {
+ currentRace = raceList.getSelectedValue();
+ raceBox.setSelected(races.get(currentRace));
+ raceEditPanel.setBorder(new TitledBorder(currentRace.id));
+ }
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ if (mapBox.equals(e.getSource())) {
+ // load zones
+ zoneBox.setModel(new DefaultComboBoxModel());
+ RMap map = (RMap) mapBox.getSelectedItem();
+ if (map != null) {
+ for (RZone zone : map.zones.values()) {
+ if (zone.theme == null) { // do not allow random zones
+ zoneBox.addItem(zone);
+ }
+ }
+ }
+ frame.pack();
+ }
+ }
+
+ @SuppressWarnings("serial")
+ private class SpellListAction extends AbstractAction {
+ public SpellListAction(String name) {
+ super(name);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("Add spell")) {
+ RSpell rs =
+ (RSpell)
+ JOptionPane.showInputDialog(
+ neon.editor.Editor.getFrame(),
+ "Add spell:",
+ "Add spell",
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ spells,
+ 0);
+ if (rs != null) {
+ spellListModel.addElement(rs);
+ }
+ } else if (e.getActionCommand().equals("Delete spell")) {
+ spellListModel.remove(spellList.getSelectedIndex());
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ private class ItemListAction extends AbstractAction {
+ public ItemListAction(String name) {
+ super(name);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("Add item")) {
+ Object[] items = dataStore.getResourceManager().getResources(RItem.class).toArray();
+ RItem ri =
+ (RItem)
+ JOptionPane.showInputDialog(
+ neon.editor.Editor.getFrame(),
+ "Add item:",
+ "Add item",
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ items,
+ 0);
+ if (ri != null) {
+ itemListModel.addElement(ri);
+ }
+ } else if (e.getActionCommand().equals("Delete item")) {
+ itemListModel.remove(itemList.getSelectedIndex());
+ }
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/CLAUDE.md b/src/main/java/neon/editor/CLAUDE.md
index 55abd0f..6578ce0 100644
--- a/src/main/java/neon/editor/CLAUDE.md
+++ b/src/main/java/neon/editor/CLAUDE.md
@@ -53,7 +53,8 @@ Specialized editors: `CreatureEditor`, `SpellEditor`, `QuestEditor`, `ItemEditor
- **ResourceManager**: Shared with game engine (`Editor.resources` static field)
- **FileSystem**: Shared file system for reading/writing mod data (`Editor.files`)
-- Uses JDOM2 for XML serialization via `XMLBuilder` and `XMLTranslator`
+- Uses JDOM2 for XML serialization via `XMLBuilder`. Maps use Jackson XML serialization via JacksonMapper.
+- Bridge methods in DataStore and ModFiler provide JDOM loading without XMLTranslator (which has been removed)
## Adding New Resource Editors
diff --git a/src/main/java/neon/editor/ChallengeCalculator.java b/src/main/java/neon/editor/ChallengeCalculator.java
index c696270..f33306c 100644
--- a/src/main/java/neon/editor/ChallengeCalculator.java
+++ b/src/main/java/neon/editor/ChallengeCalculator.java
@@ -7,14 +7,14 @@
import neon.util.Dice;
public class ChallengeCalculator {
- private JLabel uitkomst;
- private JFrame window;
- private JTextField hpField1 = new JTextField(10);
- private JTextField hpField2 = new JTextField(10);
- private JTextField spdField1 = new JTextField(10);
- private JTextField spdField2 = new JTextField(10);
- private JTextField avField1 = new JTextField(10);
- private JTextField avField2 = new JTextField(10);
+ private final JLabel uitkomst;
+ private final JFrame window;
+ private final JTextField hpField1 = new JTextField(10);
+ private final JTextField hpField2 = new JTextField(10);
+ private final JTextField spdField1 = new JTextField(10);
+ private final JTextField spdField2 = new JTextField(10);
+ private final JTextField avField1 = new JTextField(10);
+ private final JTextField avField2 = new JTextField(10);
public ChallengeCalculator() {
window = new JFrame("Calculator");
diff --git a/src/main/java/neon/editor/DataStore.java b/src/main/java/neon/editor/DataStore.java
index 681c8bf..6265e7a 100644
--- a/src/main/java/neon/editor/DataStore.java
+++ b/src/main/java/neon/editor/DataStore.java
@@ -22,37 +22,74 @@
import com.google.common.collect.Multimap;
import java.io.File;
import java.util.*;
-import neon.editor.resources.RFaction;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
import neon.editor.resources.RMap;
import neon.resources.*;
import neon.resources.quest.RQuest;
+import neon.systems.files.FileSystem;
import neon.systems.files.StringTranslator;
-import neon.systems.files.XMLTranslator;
import org.jdom2.Document;
import org.jdom2.Element;
+@Slf4j
public class DataStore {
- private HashMap scripts = new HashMap();
- private Multimap events = ArrayListMultimap.create();
- private HashMap mods = new HashMap();
- private RMod active;
+ @Getter private final HashMap scripts = new HashMap();
+ @Getter private final Multimap events = ArrayListMultimap.create();
+ private final HashMap mods = new HashMap();
+ @Getter private final ResourceManager resourceManager;
+ @Getter private final FileSystem fileSystem;
+ @Getter private RMod active;
+ private final neon.systems.files.JacksonMapper jacksonMapper =
+ new neon.systems.files.JacksonMapper();
- public RMod getActive() {
- return active;
+ public DataStore(ResourceManager resourceManager, FileSystem fileSystem) {
+ this.resourceManager = resourceManager;
+ this.fileSystem = fileSystem;
}
- public RMod getMod(String id) {
- return mods.get(id);
- }
-
- public HashMap getScripts() {
- return scripts;
+ /**
+ * Bridge method for loading XML files as JDOM Documents without using XMLTranslator.
+ *
+ * This method provides backward compatibility for editor code that expects JDOM Documents
+ * while eliminating the deprecated XMLTranslator. The InputStream is read directly and parsed
+ * with JDOM SAXBuilder.
+ *
+ * @param path the path components to the XML file
+ * @return JDOM Document, or null if file doesn't exist
+ */
+ private Document loadAsDocument(String... path) {
+ try {
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream == null) {
+ return null;
+ }
+ org.jdom2.input.SAXBuilder builder = new org.jdom2.input.SAXBuilder();
+ Document doc = builder.build(stream);
+ stream.close();
+ return doc;
+ } catch (Exception e) {
+ log.error("Failed to load XML as Document: {}", java.util.Arrays.toString(path), e);
+ return null;
+ }
}
- public Multimap getEvents() {
- return events;
+ public RMod getMod(String id) {
+ return mods.get(id);
}
+ /**
+ * Loads all data from a mod.
+ *
+ * NOTE: Uses bridge method loadAsDocument() to load XML files with JDOM for backward
+ * compatibility with JDOM-based resource constructors. XMLTranslator has been eliminated. Full
+ * migration to Jackson constructors deferred to future phase when JDOM constructors are removed
+ * from resource classes.
+ *
+ * @param root the mod path
+ * @param active whether this is the active mod
+ * @param extension whether the mod is an extension
+ */
public void loadData(String root, boolean active, boolean extension) {
RMod mod = new RMod(loadInfo(root, "main.xml"), loadCC(root, "cc.xml"), root);
if (active) {
@@ -82,35 +119,44 @@ public void loadData(String root, boolean active, boolean extension) {
private void loadEvents(RMod mod, String... file) {
try {
- for (Element event :
- Editor.files.getFile(new XMLTranslator(), file).getRootElement().getChildren()) {
- events.put(event.getAttributeValue("script"), event.getAttributeValue("tick"));
+ Document doc = loadAsDocument(file);
+ if (doc != null) {
+ for (Element event : doc.getRootElement().getChildren()) {
+ events.put(event.getAttributeValue("script"), event.getAttributeValue("tick"));
+ }
}
} catch (NullPointerException e) {
+ log.error("loadEvents", e);
}
}
private void loadScripts(RMod mod, String... file) {
String[] path = new String[file.length + 1];
try {
- for (String id : Editor.files.listFiles(file)) {
+ for (String id : fileSystem.listFiles(file)) {
System.arraycopy(file, 0, path, 0, file.length);
id = id.substring(id.lastIndexOf("/") + 1);
id = id.substring(id.lastIndexOf(File.separator) + 1);
path[file.length] = id;
- String script = Editor.files.getFile(new StringTranslator(), path);
+ String script = fileSystem.getFile(new StringTranslator(), path);
id = id.replace(".js", "");
scripts.put(id, new RScript(id, script, mod.get("id")));
}
} catch (NullPointerException e) {
+ log.error("loadScripts", e);
}
}
private Element loadInfo(String... file) {
Element info;
try {
- info = Editor.files.getFile(new XMLTranslator(), file).getRootElement();
- info.detach();
+ Document doc = loadAsDocument(file);
+ if (doc != null) {
+ info = doc.getRootElement();
+ info.detach();
+ } else {
+ throw new NullPointerException("File does not exist");
+ }
} catch (NullPointerException e) { // file does not exist
info = new Element("master");
info.setAttribute("id", file[0]);
@@ -123,8 +169,13 @@ private Element loadInfo(String... file) {
private Element loadCC(String... file) {
Element cc;
try {
- cc = Editor.files.getFile(new XMLTranslator(), file).getRootElement();
- cc.detach();
+ Document doc = loadAsDocument(file);
+ if (doc != null) {
+ cc = doc.getRootElement();
+ cc.detach();
+ } else {
+ throw new NullPointerException("File does not exist");
+ }
} catch (NullPointerException e) { // file does not exist
cc = new Element("root");
cc.addContent(new Element("races"));
@@ -138,168 +189,259 @@ private Element loadCC(String... file) {
private void loadMaps(RMod mod, String... file) {
String[] path = new String[file.length + 1];
try {
- for (String s : Editor.files.listFiles(file)) {
- System.arraycopy(file, 0, path, 0, file.length);
- // both substrings must be included for jars
- s = s.substring(s.lastIndexOf("/") + 1);
- s = s.substring(s.lastIndexOf(File.separator) + 1);
- path[file.length] = s;
- Element map = Editor.files.getFile(new XMLTranslator(), path).getRootElement();
- Editor.resources.addResource(new RMap(s.replace(".xml", ""), map, mod.get("id")), "maps");
+ for (String s : fileSystem.listFiles(file)) {
+ try {
+ System.arraycopy(file, 0, path, 0, file.length);
+ // both substrings must be included for jars
+ s = s.substring(s.lastIndexOf("/") + 1);
+ s = s.substring(s.lastIndexOf(File.separator) + 1);
+ path[file.length] = s;
+ Document doc = loadAsDocument(path);
+ if (doc != null) {
+ Element map = doc.getRootElement();
+ resourceManager.addResource(
+ new RMap(s.replace(".xml", ""), map, this, mod.get("id")), "maps");
+ }
+ } catch (RuntimeException re) {
+ log.error("Failed to load map {}", path, re);
+ }
}
} catch (NullPointerException e) {
+ log.error("loadMaps", e);
}
}
private void loadQuests(RMod mod, String... file) {
String[] path = new String[file.length + 1];
try {
- Collection files = Editor.files.listFiles(file);
+ Collection files = fileSystem.listFiles(file);
for (String quest : files) {
System.arraycopy(file, 0, path, 0, file.length);
quest = quest.substring(quest.lastIndexOf("/") + 1);
quest = quest.substring(quest.lastIndexOf(File.separator) + 1);
path[file.length] = quest;
- Element root = Editor.files.getFile(new XMLTranslator(), path).getRootElement();
- String id = quest.replace(".xml", "");
- Editor.resources.addResource(new RQuest(id, root, mod.get("id")), "quest");
+
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream != null) {
+ String id = quest.replace(".xml", "");
+ RQuest rquest = jacksonMapper.fromXml(stream, RQuest.class);
+ if (rquest != null) {
+ // Set ID from filename (RQuest uses filename as ID like JDOM path did)
+ try {
+ java.lang.reflect.Field idField =
+ neon.resources.Resource.class.getDeclaredField("id");
+ idField.setAccessible(true);
+ idField.set(rquest, id);
+ } catch (Exception e) {
+ log.error("Failed to set quest ID", e);
+ }
+ resourceManager.addResource(rquest, "quest");
+ }
+ stream.close();
+ }
}
- } catch (NullPointerException e) {
+ } catch (Exception e) {
+ log.error("Failed to load quests", e);
}
}
private void loadMagic(RMod mod, String... path) {
try {
- Document doc = Editor.files.getFile(new XMLTranslator(), path);
- for (Element e : doc.getRootElement().getChildren()) {
- switch (e.getName()) {
- case "sign":
- Editor.resources.addResource(new RSign(e, mod.get("id")), "magic");
- break;
- case "tattoo":
- Editor.resources.addResource(new RTattoo(e, mod.get("id")), "magic");
- break;
- case "recipe":
- Editor.resources.addResource(new RRecipe(e, mod.get("id")), "magic");
- break;
- case "list":
- Editor.resources.addResource(new LSpell(e, mod.get("id")), "magic");
- break;
- case "power":
- Editor.resources.addResource(new RSpell.Power(e, mod.get("id")), "magic");
- break;
- case "enchant":
- Editor.resources.addResource(new RSpell.Enchantment(e, mod.get("id")), "magic");
- break;
- default:
- Editor.resources.addResource(new RSpell(e, mod.get("id")), "magic");
- break;
- }
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream == null) {
+ return;
}
- } catch (NullPointerException e) {
+
+ jacksonMapper.parseMultiTypeXml(
+ stream,
+ (elementName, elementXml) -> {
+ switch (elementName) {
+ case "sign" -> {
+ RSign sign = jacksonMapper.fromXml(elementXml, RSign.class);
+ if (sign != null) resourceManager.addResource(sign, "magic");
+ }
+ case "tattoo" -> {
+ RTattoo tattoo = jacksonMapper.fromXml(elementXml, RTattoo.class);
+ if (tattoo != null) resourceManager.addResource(tattoo, "magic");
+ }
+ case "recipe" -> {
+ RRecipe recipe = jacksonMapper.fromXml(elementXml, RRecipe.class);
+ if (recipe != null) resourceManager.addResource(recipe, "magic");
+ }
+ case "list" -> {
+ LSpell list = jacksonMapper.fromXml(elementXml, LSpell.class);
+ if (list != null) resourceManager.addResource(list, "magic");
+ }
+ case "power" -> {
+ RSpell.Power power = jacksonMapper.fromXml(elementXml, RSpell.Power.class);
+ if (power != null) resourceManager.addResource(power, "magic");
+ }
+ case "enchant" -> {
+ RSpell.Enchantment enchant =
+ jacksonMapper.fromXml(elementXml, RSpell.Enchantment.class);
+ if (enchant != null) resourceManager.addResource(enchant, "magic");
+ }
+ default -> {
+ RSpell spell = jacksonMapper.fromXml(elementXml, RSpell.class);
+ if (spell != null) resourceManager.addResource(spell, "magic");
+ }
+ }
+ });
+ } catch (Exception e) {
+ log.error("Failed to load magic from " + java.util.Arrays.toString(path), e);
}
}
private void loadCreatures(RMod mod, String... path) {
try {
- Document doc = Editor.files.getFile(new XMLTranslator(), path);
- for (Element e : doc.getRootElement().getChildren()) {
- switch (e.getName()) {
- case "list":
- Editor.resources.addResource(new LCreature(e, mod.get("id")));
- break;
- case "npc":
- Editor.resources.addResource(new RPerson(e, mod.get("id")));
- break;
- case "group":
- break;
- default:
- Editor.resources.addResource(new RCreature(e, mod.get("id")));
- break;
- }
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream == null) {
+ return;
}
- } catch (NullPointerException e) {
- e.printStackTrace();
+
+ jacksonMapper.parseMultiTypeXml(
+ stream,
+ (elementName, elementXml) -> {
+ switch (elementName) {
+ case "list" -> {
+ LCreature list = jacksonMapper.fromXml(elementXml, LCreature.class);
+ if (list != null) resourceManager.addResource(list);
+ }
+ case "npc" -> {
+ RPerson npc = jacksonMapper.fromXml(elementXml, RPerson.class);
+ if (npc != null) resourceManager.addResource(npc);
+ }
+ case "group" -> {} // Groups are ignored
+ default -> {
+ RCreature creature = jacksonMapper.fromXml(elementXml, RCreature.class);
+ if (creature != null) resourceManager.addResource(creature);
+ }
+ }
+ });
+ } catch (Exception e) {
+ log.error("Failed to load creatures from " + java.util.Arrays.toString(path), e);
}
}
private void loadFactions(RMod mod, String... path) {
try {
- Document doc = Editor.files.getFile(new XMLTranslator(), path);
- for (Element e : doc.getRootElement().getChildren()) {
- Editor.resources.addResource(new RFaction(e, mod.get("id")), "faction");
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream == null) {
+ return;
}
- } catch (NullPointerException e) {
+
+ jacksonMapper.parseMultiTypeXml(
+ stream,
+ (elementName, elementXml) -> {
+ neon.editor.resources.RFaction faction =
+ jacksonMapper.fromXml(elementXml, neon.editor.resources.RFaction.class);
+ if (faction != null) resourceManager.addResource(faction, "faction");
+ });
+ } catch (Exception e) {
+ log.error("Failed to load factions from " + java.util.Arrays.toString(path), e);
}
}
private void loadTerrain(RMod mod, String... path) {
try {
- Document doc = Editor.files.getFile(new XMLTranslator(), path);
- for (Element e : doc.getRootElement().getChildren()) {
- Editor.resources.addResource(new RTerrain(e, mod.get("id")), "terrain");
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream == null) {
+ return;
}
- } catch (NullPointerException e) {
+
+ jacksonMapper.parseMultiTypeXml(
+ stream,
+ (elementName, elementXml) -> {
+ RTerrain terrain = jacksonMapper.fromXml(elementXml, RTerrain.class);
+ if (terrain != null) resourceManager.addResource(terrain, "terrain");
+ });
+ } catch (Exception e) {
+ log.error("Failed to load terrain from " + java.util.Arrays.toString(path), e);
}
}
private void loadItems(RMod mod, String... path) {
try {
- Document doc = Editor.files.getFile(new XMLTranslator(), path);
- for (Element e : doc.getRootElement().getChildren()) {
- switch (e.getName()) {
- case "list":
- Editor.resources.addResource(new LItem(e, mod.get("id")));
- break;
- case "book":
- case "scroll":
- Editor.resources.addResource(new RItem.Text(e, mod.get("id")));
- break;
- case "armor":
- case "clothing":
- Editor.resources.addResource(new RClothing(e, mod.get("id")));
- break;
- case "weapon":
- Editor.resources.addResource(new RWeapon(e, mod.get("id")));
- break;
- case "craft":
- Editor.resources.addResource(new RCraft(e, mod.get("id")));
- break;
- case "door":
- Editor.resources.addResource(new RItem.Door(e, mod.get("id")));
- break;
- case "potion":
- Editor.resources.addResource(new RItem.Potion(e, mod.get("id")));
- break;
- case "container":
- Editor.resources.addResource(new RItem.Container(e, mod.get("id")));
- break;
- default:
- Editor.resources.addResource(new RItem(e, mod.get("id")));
- break;
- }
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream == null) {
+ return;
}
- } catch (NullPointerException e) {
+
+ jacksonMapper.parseMultiTypeXml(
+ stream,
+ (elementName, elementXml) -> {
+ switch (elementName) {
+ case "list" -> {
+ LItem list = jacksonMapper.fromXml(elementXml, LItem.class);
+ if (list != null) resourceManager.addResource(list);
+ }
+ case "book", "scroll" -> {
+ RItem.Text text = jacksonMapper.fromXml(elementXml, RItem.Text.class);
+ if (text != null) resourceManager.addResource(text);
+ }
+ case "armor", "clothing" -> {
+ RClothing clothing = jacksonMapper.fromXml(elementXml, RClothing.class);
+ if (clothing != null) resourceManager.addResource(clothing);
+ }
+ case "weapon" -> {
+ RWeapon weapon = jacksonMapper.fromXml(elementXml, RWeapon.class);
+ if (weapon != null) resourceManager.addResource(weapon);
+ }
+ case "craft" -> {
+ RCraft craft = jacksonMapper.fromXml(elementXml, RCraft.class);
+ if (craft != null) resourceManager.addResource(craft);
+ }
+ case "door" -> {
+ RItem.Door door = jacksonMapper.fromXml(elementXml, RItem.Door.class);
+ if (door != null) resourceManager.addResource(door);
+ }
+ case "potion" -> {
+ RItem.Potion potion = jacksonMapper.fromXml(elementXml, RItem.Potion.class);
+ if (potion != null) resourceManager.addResource(potion);
+ }
+ case "container" -> {
+ RItem.Container container =
+ jacksonMapper.fromXml(elementXml, RItem.Container.class);
+ if (container != null) resourceManager.addResource(container);
+ }
+ default -> {
+ RItem item = jacksonMapper.fromXml(elementXml, RItem.class);
+ if (item != null) resourceManager.addResource(item);
+ }
+ }
+ });
+ } catch (Exception e) {
+ log.error("Failed to load items from " + java.util.Arrays.toString(path), e);
}
}
private void loadThemes(RMod mod, String... path) {
try {
- Document doc = Editor.files.getFile(new XMLTranslator(), path);
- for (Element e : doc.getRootElement().getChildren()) {
- switch (e.getName()) {
- case "dungeon":
- Editor.resources.addResource(new RDungeonTheme(e, mod.get("id")), "theme");
- break;
- case "region":
- Editor.resources.addResource(new RRegionTheme(e, mod.get("id")), "theme");
- break;
- case "zone":
- Editor.resources.addResource(new RZoneTheme(e, mod.get("id")), "theme");
- break;
- }
+ java.io.InputStream stream = fileSystem.getStream(path);
+ if (stream == null) {
+ return;
}
- } catch (NullPointerException e) {
+
+ jacksonMapper.parseMultiTypeXml(
+ stream,
+ (elementName, elementXml) -> {
+ switch (elementName) {
+ case "dungeon" -> {
+ RDungeonTheme dungeon = jacksonMapper.fromXml(elementXml, RDungeonTheme.class);
+ if (dungeon != null) resourceManager.addResource(dungeon, "theme");
+ }
+ case "region" -> {
+ RRegionTheme region = jacksonMapper.fromXml(elementXml, RRegionTheme.class);
+ if (region != null) resourceManager.addResource(region, "theme");
+ }
+ case "zone" -> {
+ RZoneTheme zone = jacksonMapper.fromXml(elementXml, RZoneTheme.class);
+ if (zone != null) resourceManager.addResource(zone, "theme");
+ }
+ }
+ });
+ } catch (Exception e) {
+ log.error("Failed to load themes from " + java.util.Arrays.toString(path), e);
}
}
}
diff --git a/src/main/java/neon/editor/DialogEditor.java b/src/main/java/neon/editor/DialogEditor.java
index 582169d..a0fb091 100644
--- a/src/main/java/neon/editor/DialogEditor.java
+++ b/src/main/java/neon/editor/DialogEditor.java
@@ -26,10 +26,10 @@
@SuppressWarnings("serial")
public class DialogEditor extends AbstractCellEditor implements ActionListener, TableCellEditor {
- private JDialog frame;
- private JTextArea pre;
- private JTextArea answer;
- private JTextArea action;
+ private final JDialog frame;
+ private final JTextArea pre;
+ private final JTextArea answer;
+ private final JTextArea action;
private String[] topic;
private int col, row;
private JTable table;
diff --git a/src/main/java/neon/editor/Editor.java b/src/main/java/neon/editor/Editor.java
index f791b7f..5df92fd 100644
--- a/src/main/java/neon/editor/Editor.java
+++ b/src/main/java/neon/editor/Editor.java
@@ -1,646 +1,625 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2013 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Scanner;
-import java.util.jar.JarFile;
-import javax.swing.*;
-import javax.swing.border.TitledBorder;
-import javax.swing.tree.*;
-import neon.editor.help.HelpLabels;
-import neon.editor.maps.*;
-import neon.editor.resources.*;
-import neon.resources.*;
-import neon.resources.quest.RQuest;
-import neon.systems.files.*;
-import neon.ui.HelpWindow;
-
-// TODO: use mbassador for events
-public class Editor implements Runnable, ActionListener {
- public static JCheckBoxMenuItem tShow, tEdit, oShow, oEdit;
- public static FileSystem files;
- public static final ResourceManager resources = new ResourceManager();
- private static JFrame frame;
- private static DataStore store;
- private static JPanel toolPanel;
- private static StatusBar status;
-
- protected MapEditor mapEditor;
- private JTabbedPane mapTabbedPane;
- private JMenuBar menuBar;
- private JMenuItem pack, unpack, newMain, newExt, load, save, export, calculate;
- private JMenu make, edit, tools;
- private JPanel terrainPanel, objectPanel, resourcePanel;
- private JTree objectTree, resourceTree;
- private ModFiler filer;
- private JList terrainList;
- private DefaultListModel terrainListModel;
- private InfoEditor infoEditor;
- private CCEditor ccEditor;
- private ScriptEditor scriptEditor;
- private EventEditor eventEditor;
-
- public static void main(String[] args) throws IOException {
- try { // set directly here to avoid problems
- javax.swing.UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- } catch (ClassNotFoundException
- | InstantiationException
- | IllegalAccessException
- | UnsupportedLookAndFeelException e) {
- e.printStackTrace();
- }
- Editor editor = new Editor();
- javax.swing.SwingUtilities.invokeLater(editor);
- }
-
- public Editor() throws IOException {
- // main window
- frame = new JFrame("Neon Editor");
- frame.getContentPane().setLayout(new BorderLayout());
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.setPreferredSize(new Dimension(1280, 800));
-
- // stuff
- files = new FileSystem();
- store = new DataStore();
-
- // menu bar
- menuBar = new JMenuBar();
- JMenu file = new JMenu("File");
- make = new JMenu("New");
- newMain = new JMenuItem("Master module...");
- newExt = new JMenuItem("Extension module...");
- load = new JMenuItem("Load...");
- save = new JMenuItem("Save");
- JMenuItem quit = new JMenuItem("Exit");
- newMain.setActionCommand("newMain");
- newExt.setActionCommand("newExt");
- save.setActionCommand("save");
- load.setActionCommand("load");
- quit.setActionCommand("quit");
- newMain.addActionListener(this);
- newExt.addActionListener(this);
- quit.addActionListener(this);
- filer = new ModFiler(frame, files, store, this);
- save.addActionListener(this);
- load.addActionListener(this);
- save.setEnabled(false);
- make.add(newMain);
- make.add(newExt);
- file.add(make);
- file.add(load);
- file.addSeparator();
- file.add(save);
- file.addSeparator();
- file.add(quit);
- edit = new JMenu("Edit");
- edit.setEnabled(false);
- JMenuItem script = new JMenuItem("Scripts...");
- JMenuItem events = new JMenuItem("Events...");
- JMenuItem cc = new JMenuItem("Character creation...");
- JMenuItem game = new JMenuItem("Game info...");
- script.setActionCommand("script");
- cc.setActionCommand("cc");
- game.setActionCommand("game");
- events.setActionCommand("events");
- script.addActionListener(this);
- cc.addActionListener(this);
- game.addActionListener(this);
- events.addActionListener(this);
- edit.add(script);
- edit.add(events);
- edit.add(cc);
- edit.add(game);
- JMenu view = new JMenu("View");
- tShow = new JCheckBoxMenuItem("Show terrain");
- oShow = new JCheckBoxMenuItem("Show objects");
- tEdit = new JCheckBoxMenuItem("Edit terrain");
- oEdit = new JCheckBoxMenuItem("Edit objects");
- tShow.setSelected(true);
- oShow.setSelected(true);
- tEdit.setSelected(true);
- oEdit.setSelected(true);
- view.add(tShow);
- view.add(oShow);
- view.addSeparator();
- view.add(tEdit);
- view.add(oEdit);
- tools = new JMenu("Tools");
- tools.setEnabled(false);
- unpack = new JMenuItem("Unpack mod...");
- unpack.setActionCommand("unpack");
- unpack.addActionListener(this);
- pack = new JMenuItem("Pack mod...");
- pack.setActionCommand("pack");
- pack.addActionListener(this);
- tools.add(unpack);
- tools.add(pack);
- tools.addSeparator();
- export = new JMenuItem("Export SVG...");
- export.setActionCommand("svg");
- export.addActionListener(this);
- calculate = new JMenuItem("Challenge calculator...");
- calculate.setActionCommand("calculate");
- calculate.addActionListener(this);
- tools.add(export);
- tools.add(calculate);
- JMenu help = new JMenu("Help");
- JMenuItem introGuide = new JMenuItem("Getting started...");
- introGuide.setActionCommand("intro");
- introGuide.addActionListener(this);
- help.add(introGuide);
- JMenuItem scriptGuide = new JMenuItem("Scripting guide...");
- scriptGuide.setActionCommand("scripting");
- scriptGuide.addActionListener(this);
- help.add(scriptGuide);
- JMenuItem mapGuide = new JMenuItem("Map editing...");
- mapGuide.setActionCommand("mapping");
- mapGuide.addActionListener(this);
- help.add(mapGuide);
- JMenuItem resGuide = new JMenuItem("Resource editing...");
- resGuide.setActionCommand("resources");
- resGuide.addActionListener(this);
- help.add(resGuide);
- menuBar.add(file);
- menuBar.add(edit);
- menuBar.add(view);
- menuBar.add(tools);
- menuBar.add(help);
- frame.setJMenuBar(menuBar);
-
- toolPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- frame.add(toolPanel, BorderLayout.PAGE_START);
-
- // objects
- objectTree = new JTree();
- objectTree.setRootVisible(false);
- objectTree.setShowsRootHandles(true);
- resourceTree = new JTree();
- resourceTree.setRootVisible(false);
- resourceTree.setShowsRootHandles(true);
-
- // panels with maps
- mapTabbedPane = new JTabbedPane();
- JPanel mapPanel = new JPanel(new BorderLayout());
- mapEditor = new MapEditor(mapTabbedPane, mapPanel);
-
- // panel with objects and terrain
- JTabbedPane editPanel = new JTabbedPane();
- objectPanel = new JPanel(new BorderLayout());
- objectPanel.setBorder(new TitledBorder("Objects"));
- editPanel.add(objectPanel, "Objects");
- terrainPanel = new JPanel(new BorderLayout());
- terrainPanel.setBorder(new TitledBorder("Terrain"));
- editPanel.add(terrainPanel, "Terrain");
- resourcePanel = new JPanel(new BorderLayout());
- resourcePanel.setBorder(new TitledBorder("Resources"));
- editPanel.add(resourcePanel, "Resources");
-
- // fiddling with JSplitPanes to get three columns
- JSplitPane bigSplitPane = new JSplitPane();
- JSplitPane smallSplitPane = new JSplitPane();
- smallSplitPane.setLeftComponent(mapPanel);
- smallSplitPane.setRightComponent(mapTabbedPane);
- bigSplitPane.setLeftComponent(smallSplitPane);
- bigSplitPane.setRightComponent(editPanel);
-
- // this could be better
- smallSplitPane.setDividerLocation(200);
- bigSplitPane.setDividerLocation(1000);
- frame.add(bigSplitPane, BorderLayout.CENTER);
-
- // status bar
- status = new StatusBar();
- frame.add(status, BorderLayout.SOUTH);
- }
-
- public void run() {
- frame.pack();
- frame.setLocationRelativeTo(null);
- frame.setVisible(true);
- }
-
- public static DataStore getStore() {
- return store;
- }
-
- public static JFrame getFrame() {
- return frame;
- }
-
- private void createMain() {
- JFileChooser chooser = new JFileChooser(new File("neon.ini.xml"));
- chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
- chooser.setDialogTitle("Choose module directory");
- if (chooser.showDialog(frame, "Choose") == JFileChooser.APPROVE_OPTION) {
- // create mod
- createMod(chooser.getSelectedFile());
- // enable editing
- enableEditing(true);
- frame.pack();
- }
- }
-
- private void createMod(File file) {
- // path here is the name of the dir, only files knows the full path
- String path = file.getName(); // this also becomes the mod id
- try {
- files.mount(file.getPath());
- // generate directories
- File objects = new File(file, "objects");
- objects.mkdir();
- File maps = new File(file, "maps");
- maps.mkdir();
- File quests = new File(file, "quests");
- quests.mkdir();
- File themes = new File(file, "themes");
- themes.mkdir();
-
- // load ensures that all resources etc. are initialized
- store.loadData(path, true, false);
- mapEditor.loadMaps(resources.getResources(RMap.class), path);
- } catch (IOException e) {
- JOptionPane.showMessageDialog(frame, "Invalid mod directory: " + file + ".");
- }
- }
-
- protected void enableEditing(boolean unpacked) {
- // enable menu items
- pack.setEnabled(unpacked);
- unpack.setEnabled(!unpacked);
- make.setEnabled(false);
- tools.setEnabled(true);
- edit.setEnabled(true);
- load.setEnabled(false);
- save.setEnabled(true);
-
- // prepare lists
- initObjects();
- initResources();
- initTerrain();
- }
-
- private void createExtension() {
- JFileChooser chooser = new JFileChooser(new File("neon.ini.xml"));
- chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
- chooser.setDialogTitle("Choose master module");
- if (chooser.showDialog(frame, "Master") == JFileChooser.APPROVE_OPTION) {
- File master = chooser.getSelectedFile();
- chooser.setDialogTitle("Choose extension directory");
- if (chooser.showDialog(frame, "Extension") == JFileChooser.APPROVE_OPTION) {
- // load master
- filer.load(master, false);
- // create extension
- createMod(chooser.getSelectedFile());
- enableEditing(true);
- frame.pack();
- }
- }
- }
-
- public static void addToolBar(JToolBar bar) {
- toolPanel.add(bar);
- }
-
- public static StatusBar getStatusBar() {
- return status;
- }
-
- private void initTerrain() {
- terrainListModel = new DefaultListModel();
- for (RTerrain rt : resources.getResources(RTerrain.class)) {
- terrainListModel.addElement(rt);
- }
- terrainList = new JList(terrainListModel);
- terrainList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- TerrainListener listener = new TerrainListener(mapEditor, terrainList);
- terrainList.addListSelectionListener(listener);
- terrainList.addMouseListener(listener);
- terrainPanel.add(new JScrollPane(terrainList));
- }
-
- private void initResources() {
- DefaultMutableTreeNode top = new DefaultMutableTreeNode("Resources");
-
- // crafts
- ResourceNode craftNode = new ResourceNode("Crafting", ResourceNode.ResourceType.CRAFT);
- for (RCraft craft : resources.getResources(RCraft.class)) {
- craftNode.add(new ResourceNode(craft, ResourceNode.ResourceType.CRAFT));
- }
- top.add(craftNode);
-
- // factions
- ResourceNode factionNode = new ResourceNode("Factions", ResourceNode.ResourceType.FACTION);
- for (RFaction faction : resources.getResources(RFaction.class)) {
- factionNode.add(new ResourceNode(faction, ResourceNode.ResourceType.FACTION));
- }
- top.add(factionNode);
-
- // region themes
- ResourceNode regionNode = new ResourceNode("Region themes", ResourceNode.ResourceType.REGION);
- for (RRegionTheme region : resources.getResources(RRegionTheme.class)) {
- regionNode.add(new ResourceNode(region, ResourceNode.ResourceType.REGION));
- }
- top.add(regionNode);
-
- // zone themes
- ResourceNode zoneNode = new ResourceNode("Zone themes", ResourceNode.ResourceType.ZONE);
- for (RZoneTheme zone : resources.getResources(RZoneTheme.class)) {
- zoneNode.add(new ResourceNode(zone, ResourceNode.ResourceType.ZONE));
- }
- top.add(zoneNode);
-
- // dungeon themes
- ResourceNode dungeonNode =
- new ResourceNode("Dungeon themes", ResourceNode.ResourceType.DUNGEON);
- for (RDungeonTheme dungeon : resources.getResources(RDungeonTheme.class)) {
- dungeonNode.add(new ResourceNode(dungeon, ResourceNode.ResourceType.DUNGEON));
- }
- top.add(dungeonNode);
-
- // quests
- ResourceNode questNode = new ResourceNode("Quests", ResourceNode.ResourceType.QUEST);
- for (RQuest quest : resources.getResources(RQuest.class)) {
- questNode.add(new ResourceNode(quest, ResourceNode.ResourceType.QUEST));
- }
- top.add(questNode);
-
- // alchemy
- ResourceNode alchemyNode = new ResourceNode("Alchemy", ResourceNode.ResourceType.RECIPE);
- for (RRecipe rr : resources.getResources(RRecipe.class)) {
- alchemyNode.add(new ResourceNode(rr, ResourceNode.ResourceType.RECIPE));
- }
- top.add(alchemyNode);
-
- // tattoos
- ResourceNode tattooNode = new ResourceNode("Tattoos", ResourceNode.ResourceType.TATTOO);
- for (RTattoo rt : resources.getResources(RTattoo.class)) {
- tattooNode.add(new ResourceNode(rt, ResourceNode.ResourceType.TATTOO));
- }
- top.add(tattooNode);
-
- // spells
- ResourceNode spellNode = new ResourceNode("Spells", ResourceNode.ResourceType.SPELL);
- ResourceNode powerNode = new ResourceNode("Powers", ResourceNode.ResourceType.POWER);
- ResourceNode curseNode = new ResourceNode("Curses", ResourceNode.ResourceType.CURSE);
- ResourceNode poisonNode = new ResourceNode("Poison", ResourceNode.ResourceType.POISON);
- ResourceNode enchantNode =
- new ResourceNode("Enchantments", ResourceNode.ResourceType.ENCHANTMENT);
- ResourceNode diseaseNode = new ResourceNode("Diseases", ResourceNode.ResourceType.DISEASE);
- ResourceNode levelNode =
- new ResourceNode("Leveled spells", ResourceNode.ResourceType.LEVEL_SPELL);
- for (RSpell rs : resources.getResources(RSpell.class)) {
- if (rs instanceof LSpell) {
- levelNode.add(new ResourceNode(rs, ResourceNode.ResourceType.LEVEL_SPELL));
- } else {
- switch (rs.type) {
- case CURSE:
- curseNode.add(new ResourceNode(rs, ResourceNode.ResourceType.CURSE));
- break;
- case DISEASE:
- diseaseNode.add(new ResourceNode(rs, ResourceNode.ResourceType.DISEASE));
- break;
- case ENCHANT:
- enchantNode.add(new ResourceNode(rs, ResourceNode.ResourceType.ENCHANTMENT));
- break;
- case POISON:
- poisonNode.add(new ResourceNode(rs, ResourceNode.ResourceType.POISON));
- break;
- case POWER:
- powerNode.add(new ResourceNode(rs, ResourceNode.ResourceType.POWER));
- break;
- case SPELL:
- spellNode.add(new ResourceNode(rs, ResourceNode.ResourceType.SPELL));
- break;
- }
- }
- }
- top.add(spellNode);
- top.add(powerNode);
- top.add(curseNode);
- top.add(poisonNode);
- top.add(enchantNode);
- top.add(diseaseNode);
- top.add(levelNode);
-
- // signs
- ResourceNode signNode = new ResourceNode("Birth signs", ResourceNode.ResourceType.SIGN);
- for (RSign rs : resources.getResources(RSign.class)) {
- signNode.add(new ResourceNode(rs, ResourceNode.ResourceType.SIGN));
- }
- top.add(signNode);
-
- resourceTree.setModel(new DefaultTreeModel(top));
- resourceTree.addMouseListener(new ResourceTreeListener(resourceTree, frame));
- resourcePanel.add(new JScrollPane(resourceTree));
- }
-
- private void initObjects() {
- DefaultMutableTreeNode top = new DefaultMutableTreeNode("Objects");
-
- // creatures;
- ObjectNode creatureNode = new ObjectNode("Creatures", ObjectNode.ObjectType.CREATURE);
- ObjectNode levelCreatureNode =
- new ObjectNode("Leveled creatures", ObjectNode.ObjectType.LEVEL_CREATURE);
- for (RCreature rc : resources.getResources(RCreature.class)) {
- if (rc instanceof LCreature) {
- levelCreatureNode.add(new ObjectNode(rc, ObjectNode.ObjectType.LEVEL_CREATURE));
- } else {
- creatureNode.add(new ObjectNode(rc, ObjectNode.ObjectType.CREATURE));
- }
- }
- top.add(creatureNode);
- top.add(levelCreatureNode);
-
- // NPCs
- ObjectNode npcNode = new ObjectNode("NPCs", ObjectNode.ObjectType.NPC);
- for (RPerson rp : resources.getResources(RPerson.class)) {
- npcNode.add(new ObjectNode(rp, ObjectNode.ObjectType.NPC));
- }
- top.add(npcNode);
-
- // items
- ObjectNode itemNode = new ObjectNode("Items", ObjectNode.ObjectType.ITEM);
- ObjectNode weaponNode = new ObjectNode("Weapons", ObjectNode.ObjectType.WEAPON);
- ObjectNode clothingNode = new ObjectNode("Clothing", ObjectNode.ObjectType.CLOTHING);
- ObjectNode armorNode = new ObjectNode("Armor", ObjectNode.ObjectType.ARMOR);
- ObjectNode lightNode = new ObjectNode("Light", ObjectNode.ObjectType.LIGHT);
- ObjectNode doorNode = new ObjectNode("Doors", ObjectNode.ObjectType.DOOR);
- ObjectNode containerNode = new ObjectNode("Containers", ObjectNode.ObjectType.CONTAINER);
- ObjectNode potionNode = new ObjectNode("Potions", ObjectNode.ObjectType.POTION);
- ObjectNode scrollNode = new ObjectNode("Scrolls", ObjectNode.ObjectType.SCROLL);
- ObjectNode bookNode = new ObjectNode("Books", ObjectNode.ObjectType.BOOK);
- ObjectNode coinNode = new ObjectNode("Money", ObjectNode.ObjectType.MONEY);
- ObjectNode foodNode = new ObjectNode("Food", ObjectNode.ObjectType.FOOD);
- ObjectNode levelItemNode = new ObjectNode("Leveled items", ObjectNode.ObjectType.LEVEL_ITEM);
- for (RItem ri : resources.getResources(RItem.class)) {
- if (ri instanceof LItem) {
- levelItemNode.add(new ObjectNode(ri, ObjectNode.ObjectType.LEVEL_ITEM));
- } else {
- switch (ri.type) {
- case armor:
- armorNode.add(new ObjectNode(ri, ObjectNode.ObjectType.ARMOR));
- break;
- case book:
- bookNode.add(new ObjectNode(ri, ObjectNode.ObjectType.BOOK));
- break;
- case clothing:
- clothingNode.add(new ObjectNode(ri, ObjectNode.ObjectType.CLOTHING));
- break;
- case coin:
- coinNode.add(new ObjectNode(ri, ObjectNode.ObjectType.MONEY));
- break;
- case container:
- containerNode.add(new ObjectNode(ri, ObjectNode.ObjectType.CONTAINER));
- break;
- case door:
- doorNode.add(new ObjectNode(ri, ObjectNode.ObjectType.DOOR));
- break;
- case food:
- foodNode.add(new ObjectNode(ri, ObjectNode.ObjectType.FOOD));
- break;
- case light:
- lightNode.add(new ObjectNode(ri, ObjectNode.ObjectType.LIGHT));
- break;
- case potion:
- potionNode.add(new ObjectNode(ri, ObjectNode.ObjectType.POTION));
- break;
- case scroll:
- scrollNode.add(new ObjectNode(ri, ObjectNode.ObjectType.SCROLL));
- break;
- case weapon:
- weaponNode.add(new ObjectNode(ri, ObjectNode.ObjectType.WEAPON));
- break;
- default:
- itemNode.add(new ObjectNode(ri, ObjectNode.ObjectType.ITEM));
- }
- }
- }
- top.add(itemNode);
- top.add(weaponNode);
- top.add(clothingNode);
- top.add(armorNode);
- top.add(lightNode);
- top.add(doorNode);
- top.add(containerNode);
- top.add(potionNode);
- top.add(scrollNode);
- top.add(bookNode);
- top.add(coinNode);
- top.add(foodNode);
- top.add(levelItemNode);
-
- objectTree.setModel(new DefaultTreeModel(top));
- objectTree.addMouseListener(new ObjectTreeListener(objectTree, frame));
- objectPanel.add(new JScrollPane(objectTree));
- objectTree.setDragEnabled(true);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("save")) {
- filer.save();
- } else if (e.getActionCommand().equals("load")) {
- filer.loadMod();
- } else if (e.getActionCommand().equals("quit")) {
- System.exit(0);
- } else if (e.getActionCommand().equals("newMain")) {
- createMain();
- } else if (e.getActionCommand().equals("newExt")) {
- createExtension();
- } else if (e.getActionCommand().equals("script")) {
- if (scriptEditor == null) {
- scriptEditor = new ScriptEditor(frame);
- }
- scriptEditor.show();
- } else if (e.getActionCommand().equals("cc")) {
- if (ccEditor == null) {
- ccEditor = new CCEditor(frame);
- }
- ccEditor.show();
- } else if (e.getActionCommand().equals("game")) {
- if (infoEditor == null) {
- infoEditor = new InfoEditor(frame);
- }
- infoEditor.show();
- } else if (e.getActionCommand().equals("events")) {
- if (eventEditor == null) {
- eventEditor = new EventEditor(frame);
- }
- eventEditor.show();
- } else if (e.getActionCommand().equals("pack")) {
- if (JOptionPane.showConfirmDialog(
- frame,
- "Do you wish to save the current data and pack it?",
- "Pack mod",
- JOptionPane.YES_NO_OPTION)
- == 0) {
- pack();
- }
- } else if (e.getActionCommand().equals("unpack")) {
- unpack();
- } else if (e.getActionCommand().equals("svg")) {
- if ((EditablePane) mapTabbedPane.getSelectedComponent() != null) {
- ZoneTreeNode node = ((EditablePane) mapTabbedPane.getSelectedComponent()).getNode();
- SVGExporter.exportToSVG(node, files, store);
- }
- } else if (e.getActionCommand().equals("calculate")) {
- new ChallengeCalculator().show();
- } else if (e.getActionCommand().equals("scripting")) {
- showHelp("scripting.html", "Scripting guide");
- } else if (e.getActionCommand().equals("intro")) {
- showHelp("intro.html", "Getting started");
- } else if (e.getActionCommand().equals("mapping")) {
- showHelp("maps.html", "Map editing");
- } else if (e.getActionCommand().equals("resources")) {
- showHelp("resources.html", "Resource editing");
- }
- }
-
- private void showHelp(String file, String title) {
- InputStream input = HelpLabels.class.getResourceAsStream(file);
- Scanner scanner = new Scanner(input, "UTF-8");
- String text = scanner.useDelimiter("\\A").next();
- scanner.close();
- new HelpWindow(frame).show(title, text);
- }
-
- private void pack() {
- filer.save();
- try {
- JarFile jar = FileUtils.pack(store.getActive().getPath()[0], store.getActive().get("id"));
- System.out.println("attributes: " + jar.getManifest().getMainAttributes());
- } catch (IOException e) {
- JOptionPane.showMessageDialog(frame, "Packing failed");
- }
- }
-
- private void unpack() {
- FileUtils.unpack(store.getActive().getPath()[0]);
- JOptionPane.showMessageDialog(
- frame,
- "Please restart the editor and load the unpacked mod.",
- "Unpack mod",
- JOptionPane.INFORMATION_MESSAGE);
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2013 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+import java.util.jar.JarFile;
+import javax.swing.*;
+import javax.swing.border.TitledBorder;
+import javax.swing.tree.*;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import neon.editor.help.HelpLabels;
+import neon.editor.maps.*;
+import neon.editor.resources.*;
+import neon.resources.*;
+import neon.resources.quest.RQuest;
+import neon.systems.files.*;
+import neon.ui.HelpWindow;
+
+// TODO: use mbassador for events
+@Slf4j
+public class Editor implements Runnable, ActionListener {
+ public static JCheckBoxMenuItem tShow, tEdit, oShow, oEdit;
+ @Getter private final FileSystem files;
+ @Getter private final ResourceManager resources;
+ @Getter private static JFrame frame;
+ @Getter private final DataStore store;
+ private static JPanel toolPanel;
+ private static StatusBar status;
+
+ protected MapEditor mapEditor;
+ private final JTabbedPane mapTabbedPane;
+ private final JMenuBar menuBar;
+ private final JMenuItem pack;
+ private final JMenuItem unpack;
+ private final JMenuItem newMain;
+ private final JMenuItem newExt;
+ private final JMenuItem load;
+ private final JMenuItem save;
+ private final JMenuItem export;
+ private final JMenuItem calculate;
+ private final JMenu make;
+ private final JMenu edit;
+ private final JMenu tools;
+ private final JPanel terrainPanel;
+ private final JPanel objectPanel;
+ private final JPanel resourcePanel;
+ private final JTree objectTree;
+ private final JTree resourceTree;
+ private final ModFiler filer;
+ private JList terrainList;
+ private DefaultListModel terrainListModel;
+ private InfoEditor infoEditor;
+ private CCEditor ccEditor;
+ private ScriptEditor scriptEditor;
+ private EventEditor eventEditor;
+
+ public static void main(String[] args) throws IOException {
+ try { // set directly here to avoid problems
+ javax.swing.UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (ClassNotFoundException
+ | InstantiationException
+ | IllegalAccessException
+ | UnsupportedLookAndFeelException e) {
+ log.error("No look and feel", e);
+ }
+ Editor editor = new Editor();
+ javax.swing.SwingUtilities.invokeLater(editor);
+ }
+
+ public Editor() throws IOException {
+ // main window
+ frame = new JFrame("Neon Editor");
+ frame.getContentPane().setLayout(new BorderLayout());
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setPreferredSize(new Dimension(1280, 800));
+
+ // stuff
+ files = new FileSystem();
+ resources = new ResourceManager();
+ store = new DataStore(resources, files);
+
+ // menu bar
+ menuBar = new JMenuBar();
+ JMenu file = new JMenu("File");
+ make = new JMenu("New");
+ newMain = new JMenuItem("Master module...");
+ newExt = new JMenuItem("Extension module...");
+ load = new JMenuItem("Load...");
+ save = new JMenuItem("Save");
+ JMenuItem quit = new JMenuItem("Exit");
+ newMain.setActionCommand("newMain");
+ newExt.setActionCommand("newExt");
+ save.setActionCommand("save");
+ load.setActionCommand("load");
+ quit.setActionCommand("quit");
+ newMain.addActionListener(this);
+ newExt.addActionListener(this);
+ quit.addActionListener(this);
+ filer = new ModFiler(frame, files, store, this);
+ save.addActionListener(this);
+ load.addActionListener(this);
+ save.setEnabled(false);
+ make.add(newMain);
+ make.add(newExt);
+ file.add(make);
+ file.add(load);
+ file.addSeparator();
+ file.add(save);
+ file.addSeparator();
+ file.add(quit);
+ edit = new JMenu("Edit");
+ edit.setEnabled(false);
+ JMenuItem script = new JMenuItem("Scripts...");
+ JMenuItem events = new JMenuItem("Events...");
+ JMenuItem cc = new JMenuItem("Character creation...");
+ JMenuItem game = new JMenuItem("Game info...");
+ script.setActionCommand("script");
+ cc.setActionCommand("cc");
+ game.setActionCommand("game");
+ events.setActionCommand("events");
+ script.addActionListener(this);
+ cc.addActionListener(this);
+ game.addActionListener(this);
+ events.addActionListener(this);
+ edit.add(script);
+ edit.add(events);
+ edit.add(cc);
+ edit.add(game);
+ JMenu view = new JMenu("View");
+ tShow = new JCheckBoxMenuItem("Show terrain");
+ oShow = new JCheckBoxMenuItem("Show objects");
+ tEdit = new JCheckBoxMenuItem("Edit terrain");
+ oEdit = new JCheckBoxMenuItem("Edit objects");
+ tShow.setSelected(true);
+ oShow.setSelected(true);
+ tEdit.setSelected(true);
+ oEdit.setSelected(true);
+ view.add(tShow);
+ view.add(oShow);
+ view.addSeparator();
+ view.add(tEdit);
+ view.add(oEdit);
+ tools = new JMenu("Tools");
+ tools.setEnabled(false);
+ unpack = new JMenuItem("Unpack mod...");
+ unpack.setActionCommand("unpack");
+ unpack.addActionListener(this);
+ pack = new JMenuItem("Pack mod...");
+ pack.setActionCommand("pack");
+ pack.addActionListener(this);
+ tools.add(unpack);
+ tools.add(pack);
+ tools.addSeparator();
+ export = new JMenuItem("Export SVG...");
+ export.setActionCommand("svg");
+ export.addActionListener(this);
+ calculate = new JMenuItem("Challenge calculator...");
+ calculate.setActionCommand("calculate");
+ calculate.addActionListener(this);
+ tools.add(export);
+ tools.add(calculate);
+ JMenu help = new JMenu("Help");
+ JMenuItem introGuide = new JMenuItem("Getting started...");
+ introGuide.setActionCommand("intro");
+ introGuide.addActionListener(this);
+ help.add(introGuide);
+ JMenuItem scriptGuide = new JMenuItem("Scripting guide...");
+ scriptGuide.setActionCommand("scripting");
+ scriptGuide.addActionListener(this);
+ help.add(scriptGuide);
+ JMenuItem mapGuide = new JMenuItem("Map editing...");
+ mapGuide.setActionCommand("mapping");
+ mapGuide.addActionListener(this);
+ help.add(mapGuide);
+ JMenuItem resGuide = new JMenuItem("Resource editing...");
+ resGuide.setActionCommand("resources");
+ resGuide.addActionListener(this);
+ help.add(resGuide);
+ menuBar.add(file);
+ menuBar.add(edit);
+ menuBar.add(view);
+ menuBar.add(tools);
+ menuBar.add(help);
+ frame.setJMenuBar(menuBar);
+
+ toolPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ frame.add(toolPanel, BorderLayout.PAGE_START);
+
+ // objects
+ objectTree = new JTree();
+ objectTree.setRootVisible(false);
+ objectTree.setShowsRootHandles(true);
+ resourceTree = new JTree();
+ resourceTree.setRootVisible(false);
+ resourceTree.setShowsRootHandles(true);
+
+ // panels with maps
+ mapTabbedPane = new JTabbedPane();
+ JPanel mapPanel = new JPanel(new BorderLayout());
+ mapEditor = new MapEditor(mapTabbedPane, mapPanel, store);
+
+ // panel with objects and terrain
+ JTabbedPane editPanel = new JTabbedPane();
+ objectPanel = new JPanel(new BorderLayout());
+ objectPanel.setBorder(new TitledBorder("Objects"));
+ editPanel.add(objectPanel, "Objects");
+ terrainPanel = new JPanel(new BorderLayout());
+ terrainPanel.setBorder(new TitledBorder("Terrain"));
+ editPanel.add(terrainPanel, "Terrain");
+ resourcePanel = new JPanel(new BorderLayout());
+ resourcePanel.setBorder(new TitledBorder("Resources"));
+ editPanel.add(resourcePanel, "Resources");
+
+ // fiddling with JSplitPanes to get three columns
+ JSplitPane bigSplitPane = new JSplitPane();
+ JSplitPane smallSplitPane = new JSplitPane();
+ smallSplitPane.setLeftComponent(mapPanel);
+ smallSplitPane.setRightComponent(mapTabbedPane);
+ bigSplitPane.setLeftComponent(smallSplitPane);
+ bigSplitPane.setRightComponent(editPanel);
+
+ // this could be better
+ smallSplitPane.setDividerLocation(200);
+ bigSplitPane.setDividerLocation(1000);
+ frame.add(bigSplitPane, BorderLayout.CENTER);
+
+ // status bar
+ status = new StatusBar();
+ frame.add(status, BorderLayout.SOUTH);
+ }
+
+ public void run() {
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ private void createMain() {
+ JFileChooser chooser = new JFileChooser(new File("neon.ini.xml"));
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ chooser.setDialogTitle("Choose module directory");
+ if (chooser.showDialog(frame, "Choose") == JFileChooser.APPROVE_OPTION) {
+ // create mod
+ createMod(chooser.getSelectedFile());
+ // enable editing
+ enableEditing(true);
+ frame.pack();
+ }
+ }
+
+ private void createMod(File file) {
+ // path here is the name of the dir, only files knows the full path
+ String path = file.getName(); // this also becomes the mod id
+ try {
+ files.mount(file.getPath());
+ // generate directories
+ File objects = new File(file, "objects");
+ objects.mkdir();
+ File maps = new File(file, "maps");
+ maps.mkdir();
+ File quests = new File(file, "quests");
+ quests.mkdir();
+ File themes = new File(file, "themes");
+ themes.mkdir();
+
+ // load ensures that all resources etc. are initialized
+ store.loadData(path, true, false);
+ mapEditor.loadMaps(resources.getResources(RMap.class), path);
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(frame, "Invalid mod directory: " + file + ".");
+ }
+ }
+
+ protected void enableEditing(boolean unpacked) {
+ // enable menu items
+ pack.setEnabled(unpacked);
+ unpack.setEnabled(!unpacked);
+ make.setEnabled(false);
+ tools.setEnabled(true);
+ edit.setEnabled(true);
+ load.setEnabled(false);
+ save.setEnabled(true);
+
+ // prepare lists
+ initObjects();
+ initResources();
+ initTerrain();
+ }
+
+ private void createExtension() {
+ JFileChooser chooser = new JFileChooser(new File("neon.ini.xml"));
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ chooser.setDialogTitle("Choose master module");
+ if (chooser.showDialog(frame, "Master") == JFileChooser.APPROVE_OPTION) {
+ File master = chooser.getSelectedFile();
+ chooser.setDialogTitle("Choose extension directory");
+ if (chooser.showDialog(frame, "Extension") == JFileChooser.APPROVE_OPTION) {
+ // load master
+ filer.load(master, false);
+ // create extension
+ createMod(chooser.getSelectedFile());
+ enableEditing(true);
+ frame.pack();
+ }
+ }
+ }
+
+ public static void addToolBar(JToolBar bar) {
+ toolPanel.add(bar);
+ }
+
+ public static StatusBar getStatusBar() {
+ return status;
+ }
+
+ private void initTerrain() {
+ terrainListModel = new DefaultListModel();
+ for (RTerrain rt : resources.getResources(RTerrain.class)) {
+ terrainListModel.addElement(rt);
+ }
+ terrainList = new JList(terrainListModel);
+ terrainList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ TerrainListener listener = new TerrainListener(mapEditor, terrainList, store);
+ terrainList.addListSelectionListener(listener);
+ terrainList.addMouseListener(listener);
+ terrainPanel.add(new JScrollPane(terrainList));
+ }
+
+ private void initResources() {
+ DefaultMutableTreeNode top = new DefaultMutableTreeNode("Resources");
+
+ // crafts
+ ResourceNode craftNode = new ResourceNode("Crafting", ResourceNode.ResourceType.CRAFT, store);
+ for (RCraft craft : resources.getResources(RCraft.class)) {
+ craftNode.add(new ResourceNode(craft, ResourceNode.ResourceType.CRAFT, store));
+ }
+ top.add(craftNode);
+
+ // factions
+ ResourceNode factionNode =
+ new ResourceNode("Factions", ResourceNode.ResourceType.FACTION, store);
+ for (RFaction faction : resources.getResources(RFaction.class)) {
+ factionNode.add(new ResourceNode(faction, ResourceNode.ResourceType.FACTION, store));
+ }
+ top.add(factionNode);
+
+ // region themes
+ ResourceNode regionNode =
+ new ResourceNode("Region themes", ResourceNode.ResourceType.REGION, store);
+ for (RRegionTheme region : resources.getResources(RRegionTheme.class)) {
+ regionNode.add(new ResourceNode(region, ResourceNode.ResourceType.REGION, store));
+ }
+ top.add(regionNode);
+
+ // zone themes
+ ResourceNode zoneNode = new ResourceNode("Zone themes", ResourceNode.ResourceType.ZONE, store);
+ for (RZoneTheme zone : resources.getResources(RZoneTheme.class)) {
+ zoneNode.add(new ResourceNode(zone, ResourceNode.ResourceType.ZONE, store));
+ }
+ top.add(zoneNode);
+
+ // dungeon themes
+ ResourceNode dungeonNode =
+ new ResourceNode("Dungeon themes", ResourceNode.ResourceType.DUNGEON, store);
+ for (RDungeonTheme dungeon : resources.getResources(RDungeonTheme.class)) {
+ dungeonNode.add(new ResourceNode(dungeon, ResourceNode.ResourceType.DUNGEON, store));
+ }
+ top.add(dungeonNode);
+
+ // quests
+ ResourceNode questNode = new ResourceNode("Quests", ResourceNode.ResourceType.QUEST, store);
+ for (RQuest quest : resources.getResources(RQuest.class)) {
+ questNode.add(new ResourceNode(quest, ResourceNode.ResourceType.QUEST, store));
+ }
+ top.add(questNode);
+
+ // alchemy
+ ResourceNode alchemyNode = new ResourceNode("Alchemy", ResourceNode.ResourceType.RECIPE, store);
+ for (RRecipe rr : resources.getResources(RRecipe.class)) {
+ alchemyNode.add(new ResourceNode(rr, ResourceNode.ResourceType.RECIPE, store));
+ }
+ top.add(alchemyNode);
+
+ // tattoos
+ ResourceNode tattooNode = new ResourceNode("Tattoos", ResourceNode.ResourceType.TATTOO, store);
+ for (RTattoo rt : resources.getResources(RTattoo.class)) {
+ tattooNode.add(new ResourceNode(rt, ResourceNode.ResourceType.TATTOO, store));
+ }
+ top.add(tattooNode);
+
+ // spells
+ ResourceNode spellNode = new ResourceNode("Spells", ResourceNode.ResourceType.SPELL, store);
+ ResourceNode powerNode = new ResourceNode("Powers", ResourceNode.ResourceType.POWER, store);
+ ResourceNode curseNode = new ResourceNode("Curses", ResourceNode.ResourceType.CURSE, store);
+ ResourceNode poisonNode = new ResourceNode("Poison", ResourceNode.ResourceType.POISON, store);
+ ResourceNode enchantNode =
+ new ResourceNode("Enchantments", ResourceNode.ResourceType.ENCHANTMENT, store);
+ ResourceNode diseaseNode =
+ new ResourceNode("Diseases", ResourceNode.ResourceType.DISEASE, store);
+ ResourceNode levelNode =
+ new ResourceNode("Leveled spells", ResourceNode.ResourceType.LEVEL_SPELL, store);
+ for (RSpell rs : resources.getResources(RSpell.class)) {
+ if (rs instanceof LSpell) {
+ levelNode.add(new ResourceNode(rs, ResourceNode.ResourceType.LEVEL_SPELL, store));
+ } else {
+ switch (rs.type) {
+ case CURSE -> curseNode.add(new ResourceNode(rs, ResourceNode.ResourceType.CURSE, store));
+ case DISEASE ->
+ diseaseNode.add(new ResourceNode(rs, ResourceNode.ResourceType.DISEASE, store));
+ case ENCHANT ->
+ enchantNode.add(new ResourceNode(rs, ResourceNode.ResourceType.ENCHANTMENT, store));
+ case POISON ->
+ poisonNode.add(new ResourceNode(rs, ResourceNode.ResourceType.POISON, store));
+ case POWER -> powerNode.add(new ResourceNode(rs, ResourceNode.ResourceType.POWER, store));
+ case SPELL -> spellNode.add(new ResourceNode(rs, ResourceNode.ResourceType.SPELL, store));
+ }
+ }
+ }
+ top.add(spellNode);
+ top.add(powerNode);
+ top.add(curseNode);
+ top.add(poisonNode);
+ top.add(enchantNode);
+ top.add(diseaseNode);
+ top.add(levelNode);
+
+ // signs
+ ResourceNode signNode = new ResourceNode("Birth signs", ResourceNode.ResourceType.SIGN, store);
+ for (RSign rs : resources.getResources(RSign.class)) {
+ signNode.add(new ResourceNode(rs, ResourceNode.ResourceType.SIGN, store));
+ }
+ top.add(signNode);
+
+ resourceTree.setModel(new DefaultTreeModel(top));
+ resourceTree.addMouseListener(new ResourceTreeListener(resourceTree, frame, store));
+ resourcePanel.add(new JScrollPane(resourceTree));
+ }
+
+ private void initObjects() {
+ DefaultMutableTreeNode top = new DefaultMutableTreeNode("Objects");
+
+ // creatures;
+ ObjectNode creatureNode = new ObjectNode("Creatures", ObjectNode.ObjectType.CREATURE, store);
+ ObjectNode levelCreatureNode =
+ new ObjectNode("Leveled creatures", ObjectNode.ObjectType.LEVEL_CREATURE, store);
+ for (RCreature rc : resources.getResources(RCreature.class)) {
+ if (rc instanceof LCreature) {
+ levelCreatureNode.add(new ObjectNode(rc, ObjectNode.ObjectType.LEVEL_CREATURE, store));
+ } else {
+ creatureNode.add(new ObjectNode(rc, ObjectNode.ObjectType.CREATURE, store));
+ }
+ }
+ top.add(creatureNode);
+ top.add(levelCreatureNode);
+
+ // NPCs
+ ObjectNode npcNode = new ObjectNode("NPCs", ObjectNode.ObjectType.NPC, store);
+ for (RPerson rp : resources.getResources(RPerson.class)) {
+ npcNode.add(new ObjectNode(rp, ObjectNode.ObjectType.NPC, store));
+ }
+ top.add(npcNode);
+
+ // items
+ ObjectNode itemNode = new ObjectNode("Items", ObjectNode.ObjectType.ITEM, store);
+ ObjectNode weaponNode = new ObjectNode("Weapons", ObjectNode.ObjectType.WEAPON, store);
+ ObjectNode clothingNode = new ObjectNode("Clothing", ObjectNode.ObjectType.CLOTHING, store);
+ ObjectNode armorNode = new ObjectNode("Armor", ObjectNode.ObjectType.ARMOR, store);
+ ObjectNode lightNode = new ObjectNode("Light", ObjectNode.ObjectType.LIGHT, store);
+ ObjectNode doorNode = new ObjectNode("Doors", ObjectNode.ObjectType.DOOR, store);
+ ObjectNode containerNode = new ObjectNode("Containers", ObjectNode.ObjectType.CONTAINER, store);
+ ObjectNode potionNode = new ObjectNode("Potions", ObjectNode.ObjectType.POTION, store);
+ ObjectNode scrollNode = new ObjectNode("Scrolls", ObjectNode.ObjectType.SCROLL, store);
+ ObjectNode bookNode = new ObjectNode("Books", ObjectNode.ObjectType.BOOK, store);
+ ObjectNode coinNode = new ObjectNode("Money", ObjectNode.ObjectType.MONEY, store);
+ ObjectNode foodNode = new ObjectNode("Food", ObjectNode.ObjectType.FOOD, store);
+ ObjectNode levelItemNode =
+ new ObjectNode("Leveled items", ObjectNode.ObjectType.LEVEL_ITEM, store);
+ for (RItem ri : resources.getResources(RItem.class)) {
+ if (ri instanceof LItem) {
+ levelItemNode.add(new ObjectNode(ri, ObjectNode.ObjectType.LEVEL_ITEM, store));
+ } else {
+ switch (ri.type) {
+ case armor -> armorNode.add(new ObjectNode(ri, ObjectNode.ObjectType.ARMOR, store));
+ case book -> bookNode.add(new ObjectNode(ri, ObjectNode.ObjectType.BOOK, store));
+ case clothing ->
+ clothingNode.add(new ObjectNode(ri, ObjectNode.ObjectType.CLOTHING, store));
+ case coin -> coinNode.add(new ObjectNode(ri, ObjectNode.ObjectType.MONEY, store));
+ case container ->
+ containerNode.add(new ObjectNode(ri, ObjectNode.ObjectType.CONTAINER, store));
+ case door -> doorNode.add(new ObjectNode(ri, ObjectNode.ObjectType.DOOR, store));
+ case food -> foodNode.add(new ObjectNode(ri, ObjectNode.ObjectType.FOOD, store));
+ case light -> lightNode.add(new ObjectNode(ri, ObjectNode.ObjectType.LIGHT, store));
+ case potion -> potionNode.add(new ObjectNode(ri, ObjectNode.ObjectType.POTION, store));
+ case scroll -> scrollNode.add(new ObjectNode(ri, ObjectNode.ObjectType.SCROLL, store));
+ case weapon -> weaponNode.add(new ObjectNode(ri, ObjectNode.ObjectType.WEAPON, store));
+ default -> itemNode.add(new ObjectNode(ri, ObjectNode.ObjectType.ITEM, store));
+ }
+ }
+ }
+ top.add(itemNode);
+ top.add(weaponNode);
+ top.add(clothingNode);
+ top.add(armorNode);
+ top.add(lightNode);
+ top.add(doorNode);
+ top.add(containerNode);
+ top.add(potionNode);
+ top.add(scrollNode);
+ top.add(bookNode);
+ top.add(coinNode);
+ top.add(foodNode);
+ top.add(levelItemNode);
+
+ objectTree.setModel(new DefaultTreeModel(top));
+ objectTree.addMouseListener(new ObjectTreeListener(objectTree, frame, store));
+ objectPanel.add(new JScrollPane(objectTree));
+ objectTree.setDragEnabled(true);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ switch (e.getActionCommand()) {
+ case "save" -> filer.save();
+ case "load" -> filer.loadMod();
+ case "quit" -> System.exit(0);
+ case "newMain" -> createMain();
+ case "newExt" -> createExtension();
+ case "script" -> {
+ if (scriptEditor == null) {
+ scriptEditor = new ScriptEditor(frame, store);
+ }
+ scriptEditor.show();
+ }
+ case "cc" -> {
+ if (ccEditor == null) {
+ ccEditor = new CCEditor(frame, store);
+ }
+ ccEditor.show();
+ }
+ case "game" -> {
+ if (infoEditor == null) {
+ infoEditor = new InfoEditor(frame, store);
+ }
+ infoEditor.show();
+ }
+ case "events" -> {
+ if (eventEditor == null) {
+ eventEditor = new EventEditor(frame, store);
+ }
+ eventEditor.show();
+ }
+ case "pack" -> {
+ if (JOptionPane.showConfirmDialog(
+ frame,
+ "Do you wish to save the current data and pack it?",
+ "Pack mod",
+ JOptionPane.YES_NO_OPTION)
+ == 0) {
+ pack();
+ }
+ }
+ case "unpack" -> unpack();
+ case "svg" -> {
+ if (mapTabbedPane.getSelectedComponent() != null) {
+ ZoneTreeNode node = ((EditablePane) mapTabbedPane.getSelectedComponent()).getNode();
+ SVGExporter.exportToSVG(node, files, store);
+ }
+ }
+ case "calculate" -> new ChallengeCalculator().show();
+ case "scripting" -> showHelp("scripting.html", "Scripting guide");
+ case "intro" -> showHelp("intro.html", "Getting started");
+ case "mapping" -> showHelp("maps.html", "Map editing");
+ case "resources" -> showHelp("resources.html", "Resource editing");
+ }
+ }
+
+ private void showHelp(String file, String title) {
+ InputStream input = HelpLabels.class.getResourceAsStream(file);
+ Scanner scanner = new Scanner(input, StandardCharsets.UTF_8);
+ String text = scanner.useDelimiter("\\A").next();
+ scanner.close();
+ new HelpWindow(frame).show(title, text);
+ }
+
+ private void pack() {
+ filer.save();
+ try {
+ JarFile jar = FileUtils.pack(store.getActive().getPath()[0], store.getActive().get("id"));
+ System.out.println("attributes: " + jar.getManifest().getMainAttributes());
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(frame, "Packing failed");
+ }
+ }
+
+ private void unpack() {
+ FileUtils.unpack(store.getActive().getPath()[0]);
+ JOptionPane.showMessageDialog(
+ frame,
+ "Please restart the editor and load the unpacked mod.",
+ "Unpack mod",
+ JOptionPane.INFORMATION_MESSAGE);
+ }
+}
diff --git a/src/main/java/neon/editor/EventEditor.java b/src/main/java/neon/editor/EventEditor.java
index 4842b49..f336058 100644
--- a/src/main/java/neon/editor/EventEditor.java
+++ b/src/main/java/neon/editor/EventEditor.java
@@ -1,196 +1,197 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.border.*;
-import javax.swing.event.*;
-
-public class EventEditor implements ListSelectionListener, ActionListener, MouseListener {
- private JDialog frame;
- private Multimap events;
- private JList times;
- private JList list;
- private DefaultListModel model;
- private DefaultListModel stampModel;
- private String[] scripts;
-
- public EventEditor(JFrame parent) {
- frame = new JDialog(parent, "Event Editor");
- frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
- frame.setPreferredSize(new Dimension(480, 300));
-
- model = new DefaultListModel();
- list = new JList(model);
- list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- list.addMouseListener(this);
- list.addListSelectionListener(this);
- JScrollPane listScroller = new JScrollPane(list);
- listScroller.setBorder(new TitledBorder("Events"));
-
- stampModel = new DefaultListModel();
- times = new JList(stampModel);
- times.addMouseListener(this);
- JScrollPane textScroller = new JScrollPane(times);
- textScroller.setBorder(new TitledBorder("Timestamps"));
-
- JPanel content = new JPanel(new BorderLayout());
- content.add(listScroller, BorderLayout.LINE_START);
- content.add(textScroller, BorderLayout.CENTER);
- frame.setContentPane(content);
-
- JPanel buttons = new JPanel();
- content.add(buttons, BorderLayout.PAGE_END);
- JButton ok = new JButton("Ok");
- ok.addActionListener(this);
- JButton cancel = new JButton("Cancel");
- cancel.addActionListener(this);
- JButton apply = new JButton("Apply");
- apply.addActionListener(this);
- buttons.add(ok);
- buttons.add(cancel);
- buttons.add(apply);
- }
-
- public void show() {
- model.clear();
- scripts = Editor.getStore().getScripts().keySet().toArray(new String[0]);
- events = ArrayListMultimap.create();
- for (String event : Editor.getStore().getEvents().keySet()) {
- events.putAll(event, Editor.getStore().getEvents().get(event));
- model.addElement(event);
- }
- frame.pack();
- frame.setLocationRelativeTo(null);
- frame.setVisible(true);
- }
-
- public void valueChanged(ListSelectionEvent e) {
- // blijkbaar worden er twee events gefired bij selectie
- if (e.getValueIsAdjusting()) {
- stampModel.clear();
- for (String s : events.get(list.getSelectedValue().toString())) {
- stampModel.addElement(s);
- }
- }
- }
-
- private void save() {
- Editor.getStore().getEvents().clear();
- for (String event : events.keySet()) {
- Editor.getStore().getEvents().putAll(event, events.get(event));
- }
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("Cancel")) {
- frame.dispose();
- } else if (e.getActionCommand().equals("Apply")) {
- save();
- } else if (e.getActionCommand().equals("Ok")) {
- save();
- frame.dispose();
- }
- }
-
- public void mousePressed(MouseEvent e) {}
-
- public void mouseReleased(MouseEvent e) {}
-
- public void mouseEntered(MouseEvent e) {}
-
- public void mouseExited(MouseEvent e) {}
-
- public void mouseClicked(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON3) {
- if (e.getSource().equals(list)) {
- JPopupMenu menu = new JPopupMenu();
- menu.add(new ClickAction("Add event"));
- menu.add(new ClickAction("Remove event"));
- menu.show(e.getComponent(), e.getX(), e.getY());
- list.setSelectedIndex(list.locationToIndex(new Point(e.getX(), e.getY())));
- } else if (e.getSource().equals(times)) {
- JPopupMenu menu = new JPopupMenu();
- menu.add(new ClickAction("Add timestamp"));
- menu.add(new ClickAction("Remove timestamp"));
- menu.show(e.getComponent(), e.getX(), e.getY());
- times.setSelectedIndex(times.locationToIndex(new Point(e.getX(), e.getY())));
- }
- }
- }
-
- @SuppressWarnings("serial")
- public class ClickAction extends AbstractAction {
- public ClickAction(String name) {
- super(name);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("Add event")) {
- String s =
- (String)
- JOptionPane.showInputDialog(
- frame,
- "Select event:",
- "New event",
- JOptionPane.PLAIN_MESSAGE,
- null,
- scripts,
- 0);
- if ((s != null) && (s.length() > 0)) {
- model.addElement(s);
- events.put(s, "");
- list.setSelectedValue(s, true);
- }
- } else if (e.getActionCommand().equals("Remove event")) {
- try {
- if (list.getSelectedIndex() >= 0) {
- int index = list.getSelectedIndex();
- events.removeAll(list.getSelectedValue().toString());
- model.remove(index);
- }
- } catch (ArrayIndexOutOfBoundsException a) {
- }
- } else if (e.getActionCommand().equals("Add timestamp")) {
- String s =
- (String)
- JOptionPane.showInputDialog(
- frame, "Timestamp:", "Add timestamp", JOptionPane.QUESTION_MESSAGE);
- if (s.matches("\\d*:?\\d*:?\\d*")) { // X:Y:Z
- stampModel.addElement(s);
- events.put(list.getSelectedValue().toString(), s);
- times.setSelectedValue(s, true);
- }
- } else if (e.getActionCommand().equals("Remove timestamp")) {
- try {
- if (times.getSelectedIndex() >= 0) {
- int index = times.getSelectedIndex();
- events.remove(list.getSelectedValue().toString(), times.getSelectedValue().toString());
- stampModel.remove(index);
- }
- } catch (ArrayIndexOutOfBoundsException a) {
- }
- }
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+
+public class EventEditor implements ListSelectionListener, ActionListener, MouseListener {
+ private final JDialog frame;
+ private final DataStore dataStore;
+ private Multimap events;
+ private final JList times;
+ private final JList list;
+ private final DefaultListModel model;
+ private final DefaultListModel stampModel;
+ private String[] scripts;
+
+ public EventEditor(JFrame parent, DataStore dataStore) {
+ frame = new JDialog(parent, "Event Editor");
+ this.dataStore = dataStore;
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.setPreferredSize(new Dimension(480, 300));
+
+ model = new DefaultListModel();
+ list = new JList(model);
+ list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ list.addMouseListener(this);
+ list.addListSelectionListener(this);
+ JScrollPane listScroller = new JScrollPane(list);
+ listScroller.setBorder(new TitledBorder("Events"));
+
+ stampModel = new DefaultListModel();
+ times = new JList(stampModel);
+ times.addMouseListener(this);
+ JScrollPane textScroller = new JScrollPane(times);
+ textScroller.setBorder(new TitledBorder("Timestamps"));
+
+ JPanel content = new JPanel(new BorderLayout());
+ content.add(listScroller, BorderLayout.LINE_START);
+ content.add(textScroller, BorderLayout.CENTER);
+ frame.setContentPane(content);
+
+ JPanel buttons = new JPanel();
+ content.add(buttons, BorderLayout.PAGE_END);
+ JButton ok = new JButton("Ok");
+ ok.addActionListener(this);
+ JButton cancel = new JButton("Cancel");
+ cancel.addActionListener(this);
+ JButton apply = new JButton("Apply");
+ apply.addActionListener(this);
+ buttons.add(ok);
+ buttons.add(cancel);
+ buttons.add(apply);
+ }
+
+ public void show() {
+ model.clear();
+ scripts = dataStore.getScripts().keySet().toArray(new String[0]);
+ events = ArrayListMultimap.create();
+ for (String event : dataStore.getEvents().keySet()) {
+ events.putAll(event, dataStore.getEvents().get(event));
+ model.addElement(event);
+ }
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public void valueChanged(ListSelectionEvent e) {
+ // blijkbaar worden er twee events gefired bij selectie
+ if (e.getValueIsAdjusting()) {
+ stampModel.clear();
+ for (String s : events.get(list.getSelectedValue())) {
+ stampModel.addElement(s);
+ }
+ }
+ }
+
+ private void save() {
+ dataStore.getEvents().clear();
+ for (String event : events.keySet()) {
+ dataStore.getEvents().putAll(event, events.get(event));
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("Cancel")) {
+ frame.dispose();
+ } else if (e.getActionCommand().equals("Apply")) {
+ save();
+ } else if (e.getActionCommand().equals("Ok")) {
+ save();
+ frame.dispose();
+ }
+ }
+
+ public void mousePressed(MouseEvent e) {}
+
+ public void mouseReleased(MouseEvent e) {}
+
+ public void mouseEntered(MouseEvent e) {}
+
+ public void mouseExited(MouseEvent e) {}
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ if (e.getSource().equals(list)) {
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new ClickAction("Add event"));
+ menu.add(new ClickAction("Remove event"));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ list.setSelectedIndex(list.locationToIndex(new Point(e.getX(), e.getY())));
+ } else if (e.getSource().equals(times)) {
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new ClickAction("Add timestamp"));
+ menu.add(new ClickAction("Remove timestamp"));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ times.setSelectedIndex(times.locationToIndex(new Point(e.getX(), e.getY())));
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public class ClickAction extends AbstractAction {
+ public ClickAction(String name) {
+ super(name);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("Add event")) {
+ String s =
+ (String)
+ JOptionPane.showInputDialog(
+ frame,
+ "Select event:",
+ "New event",
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ scripts,
+ 0);
+ if ((s != null) && (s.length() > 0)) {
+ model.addElement(s);
+ events.put(s, "");
+ list.setSelectedValue(s, true);
+ }
+ } else if (e.getActionCommand().equals("Remove event")) {
+ try {
+ if (list.getSelectedIndex() >= 0) {
+ int index = list.getSelectedIndex();
+ events.removeAll(list.getSelectedValue());
+ model.remove(index);
+ }
+ } catch (ArrayIndexOutOfBoundsException a) {
+ }
+ } else if (e.getActionCommand().equals("Add timestamp")) {
+ String s =
+ JOptionPane.showInputDialog(
+ frame, "Timestamp:", "Add timestamp", JOptionPane.QUESTION_MESSAGE);
+ if (s.matches("\\d*:?\\d*:?\\d*")) { // X:Y:Z
+ stampModel.addElement(s);
+ events.put(list.getSelectedValue(), s);
+ times.setSelectedValue(s, true);
+ }
+ } else if (e.getActionCommand().equals("Remove timestamp")) {
+ try {
+ if (times.getSelectedIndex() >= 0) {
+ int index = times.getSelectedIndex();
+ events.remove(list.getSelectedValue(), times.getSelectedValue());
+ stampModel.remove(index);
+ }
+ } catch (ArrayIndexOutOfBoundsException a) {
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/InfoEditor.java b/src/main/java/neon/editor/InfoEditor.java
index 6537e2c..efa8ee4 100644
--- a/src/main/java/neon/editor/InfoEditor.java
+++ b/src/main/java/neon/editor/InfoEditor.java
@@ -26,11 +26,15 @@
import neon.resources.RMod;
public class InfoEditor implements ActionListener {
- private JDialog frame;
- private JTextField titleField, bigField, smallField;
+ private final JDialog frame;
+ private final JTextField titleField;
+ private final JTextField bigField;
+ private final JTextField smallField;
+ private final DataStore dataStore;
- public InfoEditor(JFrame parent) {
+ public InfoEditor(JFrame parent, DataStore dataStore) {
frame = new JDialog(parent, "Game Info Editor");
+ this.dataStore = dataStore;
JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.PAGE_AXIS));
frame.setContentPane(content);
@@ -117,7 +121,7 @@ public InfoEditor(JFrame parent) {
public void show() {
// assume that data is always valid...
- RMod data = Editor.getStore().getActive();
+ RMod data = dataStore.getActive();
titleField.setText(data.get("title"));
if (data.get("big") != null) {
bigField.setText(data.get("big"));
@@ -131,7 +135,7 @@ public void show() {
}
private void save() {
- RMod data = Editor.getStore().getActive();
+ RMod data = dataStore.getActive();
// big may not be the same as small
if (!bigField.getText().equals(smallField.getText())) {
// big not null or "€"
@@ -144,7 +148,7 @@ private void save() {
}
}
// title not null or "", unless for extension
- if (!Editor.getStore().getActive().isExtension()
+ if (!dataStore.getActive().isExtension()
|| !(titleField.getText() == null && titleField.getText().isEmpty())) {
data.set("title", titleField.getText());
}
diff --git a/src/main/java/neon/editor/JacksonXmlBuilder.java b/src/main/java/neon/editor/JacksonXmlBuilder.java
new file mode 100644
index 0000000..e86cd5c
--- /dev/null
+++ b/src/main/java/neon/editor/JacksonXmlBuilder.java
@@ -0,0 +1,238 @@
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import neon.resources.RData;
+import neon.resources.RMod;
+import neon.systems.files.JacksonMapper;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.input.SAXBuilder;
+
+/**
+ * Jackson-based XML document builder for serializing game resources.
+ *
+ * This class replaces {@link XMLBuilder} as part of Phase 7A of the JDOM-to-Jackson migration.
+ * It uses Jackson for events serialization and leverages the existing {@code toElement()} bridge
+ * pattern for resources (as established in RSpell, RCreature, and RMod).
+ *
+ *
Phase 7A Scope: This class still produces JDOM Documents for compatibility with the
+ * existing {@code ModFiler.saveFile(Document)} API. In Phase 7B, when {@code toElement()} methods
+ * are removed from all resources, this class will be updated to serialize directly to
+ * ByteArrayOutputStream without the JDOM intermediate representation.
+ *
+ *
Migration Path:
+ *
+ *
+ * - Phase 7A (current): XMLBuilder → JacksonXmlBuilder (this class)
+ *
- Phase 7B: Remove toElement() from resources, update this class to skip JDOM
+ *
- Phase 7C: Direct Jackson serialization, eliminate this class
+ *
+ *
+ * @author mdriesen
+ */
+public class JacksonXmlBuilder {
+ private final JacksonMapper mapper = new JacksonMapper();
+ private final DataStore store;
+
+ /**
+ * Creates a new JacksonXmlBuilder.
+ *
+ * @param store the data store containing events and active mod information
+ */
+ public JacksonXmlBuilder(DataStore store) {
+ this.store = store;
+ }
+
+ /**
+ * Creates an events XML document using Jackson serialization.
+ *
+ * Serializes the scheduled game events from {@code DataStore.getEvents()} into an XML document
+ * with the structure:
+ *
+ *
{@code
+ *
+ *
+ *
+ *
+ * }
+ *
+ * @return JDOM Document containing events
+ */
+ public Document getEventsDoc() {
+ EventsModel model = new EventsModel();
+ model.events = new ArrayList<>();
+
+ // Convert Multimap to List
+ for (Map.Entry> entry : store.getEvents().asMap().entrySet()) {
+ String script = entry.getKey();
+ for (String tick : entry.getValue()) {
+ EventsModel.Event event = new EventsModel.Event();
+ event.script = script;
+ event.tick = tick;
+ model.events.add(event);
+ }
+ }
+
+ return toDocument(model);
+ }
+
+ /**
+ * Creates a resource document with unsorted resources.
+ *
+ * Resources maintain their natural iteration order. Filters resources to only include those
+ * belonging to the specified mod.
+ *
+ *
Use this for: factions, recipes, terrain, themes (resources where order doesn't matter).
+ *
+ * @param resources the collection of resources to serialize
+ * @param rootName the XML root element name (e.g., "factions", "terrain")
+ * @param mod the active mod (filters by mod ID)
+ * @return JDOM Document with unsorted resources
+ */
+ public Document getListDoc(Collection extends RData> resources, String rootName, RMod mod) {
+ return buildResourceDoc(resources, rootName, mod, false);
+ }
+
+ /**
+ * Creates a resource document with resources sorted alphabetically by ID.
+ *
+ *
Resources are sorted alphabetically by their {@code id} field. Filters resources to only
+ * include those belonging to the specified mod.
+ *
+ *
Use this for: items, creatures, spells (resources that benefit from alphabetical order).
+ *
+ * @param resources the collection of resources to serialize
+ * @param rootName the XML root element name (e.g., "items", "monsters")
+ * @param mod the active mod (filters by mod ID)
+ * @return JDOM Document with sorted resources
+ */
+ public Document getResourceDoc(Collection extends RData> resources, String rootName, RMod mod) {
+ return buildResourceDoc(resources, rootName, mod, true);
+ }
+
+ /**
+ * Builds a resource document, filtering by mod and optionally sorting by ID.
+ *
+ *
This method uses Jackson to serialize each resource directly, eliminating the need for
+ * {@code toElement()} methods in resource classes. Each resource is serialized individually and
+ * combined under a common root element.
+ *
+ * @param resources the collection of resources
+ * @param rootName the XML root element name
+ * @param mod the active mod
+ * @param sorted whether to sort resources alphabetically by ID
+ * @return JDOM Document
+ */
+ private Document buildResourceDoc(
+ Collection extends RData> resources, String rootName, RMod mod, boolean sorted) {
+
+ // Filter resources belonging to this mod
+ List filtered =
+ resources.stream()
+ .filter(r -> r.getPath()[0].equals(mod.get("id")))
+ .collect(Collectors.toList());
+
+ // Sort if requested
+ if (sorted) {
+ filtered.sort(Comparator.comparing(r -> r.id));
+ }
+
+ // Build JDOM element with children using Jackson serialization
+ Element root = new Element(rootName);
+ for (RData resource : filtered) {
+ try {
+ // Serialize resource to XML with Jackson
+ String xml = mapper.toXml(resource).toString();
+
+ // Parse XML to Element
+ Element element =
+ new SAXBuilder().build(new ByteArrayInputStream(xml.getBytes())).getRootElement();
+ element.detach();
+ root.addContent(element);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to serialize resource: " + resource.id, e);
+ }
+ }
+
+ return new Document(root);
+ }
+
+ /**
+ * Converts a Jackson model to a JDOM Document using the Jackson→JDOM bridge pattern.
+ *
+ * This follows the same pattern as {@code RSpell.toElement()} (line 384-398) and {@code
+ * RMod.getMainElement()} (line 179-203): serialize to XML via Jackson, then parse back to JDOM.
+ *
+ *
This bridge is temporary and will be removed in Phase 7B when {@code ModFiler} is updated to
+ * save ByteArrayOutputStream directly.
+ *
+ * @param model the Jackson-annotated model object to serialize
+ * @return JDOM Document
+ */
+ private Document toDocument(Object model) {
+ try {
+ // Serialize model to XML using Jackson
+ String xml = mapper.toXml(model).toString();
+
+ // Parse XML string back to JDOM Document
+ return new SAXBuilder().build(new ByteArrayInputStream(xml.getBytes()));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to convert Jackson model to JDOM Document", e);
+ }
+ }
+
+ /**
+ * Jackson model for events.xml structure.
+ *
+ *
Represents the XML structure:
+ *
+ *
{@code
+ *
+ *
+ * ...
+ *
+ * }
+ */
+ @JacksonXmlRootElement(localName = "events")
+ static class EventsModel {
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "event")
+ public List events;
+
+ /** Represents a single scheduled event. */
+ static class Event {
+ @JacksonXmlProperty(isAttribute = true)
+ public String script;
+
+ @JacksonXmlProperty(isAttribute = true)
+ public String tick;
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/ModCellRenderer.java b/src/main/java/neon/editor/ModCellRenderer.java
index 0532bf1..e4cf49e 100644
--- a/src/main/java/neon/editor/ModCellRenderer.java
+++ b/src/main/java/neon/editor/ModCellRenderer.java
@@ -27,6 +27,12 @@
@SuppressWarnings("serial")
public class ModCellRenderer extends JLabel implements ListCellRenderer {
+ private final DataStore dataStore;
+
+ public ModCellRenderer(DataStore dataStore) {
+ this.dataStore = dataStore;
+ }
+
/**
* Returns this renderer with the right properties (color, font, background color). If the cell
* does not contain a resource from the currently active mod, the text is rendered in italics.
@@ -46,7 +52,7 @@ public Component getListCellRendererComponent(
boolean cellHasFocus) {
setOpaque(true);
String text = value.toString();
- if (value.getPath()[0].equals(Editor.getStore().getActive().get("id"))) {
+ if (value.getPath()[0].equals(dataStore.getActive().get("id"))) {
setText(text);
} else {
setText("" + text + "");
diff --git a/src/main/java/neon/editor/ModFiler.java b/src/main/java/neon/editor/ModFiler.java
index ca676c1..7e4cb61 100644
--- a/src/main/java/neon/editor/ModFiler.java
+++ b/src/main/java/neon/editor/ModFiler.java
@@ -1,245 +1,347 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2013 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import javax.swing.JFileChooser;
-import javax.swing.JFrame;
-import javax.swing.JOptionPane;
-import neon.editor.resources.*;
-import neon.resources.RCraft;
-import neon.resources.RCreature;
-import neon.resources.RDungeonTheme;
-import neon.resources.RItem;
-import neon.resources.RMod;
-import neon.resources.RPerson;
-import neon.resources.RRecipe;
-import neon.resources.RRegionTheme;
-import neon.resources.RScript;
-import neon.resources.RSign;
-import neon.resources.RSpell;
-import neon.resources.RTattoo;
-import neon.resources.RTerrain;
-import neon.resources.RZoneTheme;
-import neon.resources.quest.RQuest;
-import neon.systems.files.FileSystem;
-import neon.systems.files.StringTranslator;
-import neon.systems.files.XMLTranslator;
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.jdom2.JDOMException;
-import org.jdom2.input.SAXBuilder;
-
-public class ModFiler {
- private FileSystem files;
- private DataStore store;
- private Editor editor;
- private JFrame frame;
-
- public ModFiler(JFrame frame, FileSystem files, DataStore store, Editor editor) {
- this.frame = frame;
- this.files = files;
- this.store = store;
- this.editor = editor;
- }
-
- void loadMod() {
- // hacky way to make the filechooser start in the game dir
- JFileChooser chooser = new JFileChooser(new File("neon.ini.xml"));
- chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
- chooser.setDialogTitle("Choose module");
- if (chooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
- load(chooser.getSelectedFile(), true);
- }
- }
-
- void load(File file, boolean active) {
- String path = file.getPath();
- try {
- path = files.mount(path);
- if (!isMod(path)) { // check if this is a mod
- JOptionPane.showMessageDialog(frame, "Selected file is not a valid mod.");
- files.removePath(path);
- } else {
- if (isExtension(path)) { // if extension: load all masters
- Document doc = files.getFile(new XMLTranslator(), path, "main.xml");
- for (Object master : doc.getRootElement().getChildren("master")) {
- String id = ((Element) master).getText();
- Document ini = new Document();
- try { // check in neon.ini.xml which mods exist
- FileInputStream in = new FileInputStream("neon.ini.xml");
- ini = new SAXBuilder().build(in);
- in.close();
- } catch (JDOMException e) {
- }
-
- // check if there is a mod with the correct id
- for (Element mod : ini.getRootElement().getChild("files").getChildren()) {
- if (!mod.getText().equals(path)) { // make sure current mod is not loaded again
- System.out.println(mod.getText() + ", " + path);
- files.mount(mod.getText());
- Document d = files.getFile(new XMLTranslator(), mod.getText(), "main.xml");
- if (d.getRootElement().getAttributeValue("id").equals(id)) {
- store.loadData(mod.getText(), false, false);
- } else {
- files.removePath(mod.getText());
- }
- }
- }
- }
- }
-
- frame.setTitle("Neon Editor: " + path);
- store.loadData(path, active, isExtension(path));
- editor.mapEditor.loadMaps(Editor.resources.getResources(RMap.class), path);
- editor.enableEditing(file.isDirectory());
- frame.pack();
- }
- } catch (IOException e1) {
- JOptionPane.showMessageDialog(frame, "Selected file is not a valid mod.");
- }
- }
-
- public void save() {
- XMLBuilder builder = new XMLBuilder(store);
- RMod active = store.getActive();
- saveFile(new Document(store.getActive().getMainElement()), "main.xml");
- saveFile(new Document(store.getActive().getCCElement()), "cc.xml");
- saveFile(
- builder.getResourceDoc(Editor.resources.getResources(RItem.class), "items", active),
- "objects",
- "items.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RFaction.class), "factions", active),
- "factions.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RRecipe.class), "recipes", active),
- "objects",
- "alchemy.xml");
- saveFile(builder.getEventsDoc(), "events.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RPerson.class), "people", active),
- "objects",
- "npc.xml");
- saveFile(
- builder.getResourceDoc(Editor.resources.getResources(RCreature.class), "monsters", active),
- "objects",
- "monsters.xml");
- saveFile(
- builder.getResourceDoc(Editor.resources.getResources(RSpell.class), "spells", active),
- "spells.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RTerrain.class), "terrain", active),
- "terrain.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RCraft.class), "items", active),
- "objects",
- "crafting.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RSign.class), "signs", active),
- "signs.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RTattoo.class), "tattoos", active),
- "tattoos.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RZoneTheme.class), "themes", active),
- "themes",
- "zones.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RDungeonTheme.class), "themes", active),
- "themes",
- "dungeons.xml");
- saveFile(
- builder.getListDoc(Editor.resources.getResources(RRegionTheme.class), "themes", active),
- "themes",
- "regions.xml");
- saveMaps();
- saveQuests();
- saveScripts();
- }
-
- private void saveMaps() {
- for (String name : files.listFiles(store.getActive().getPath()[0], "maps")) {
- String map =
- name.substring(name.lastIndexOf(File.separator) + 1, name.length() - 4); // -4 for ".xml"
- if (Editor.resources.getResource(map, "maps") == null) {
- files.delete(name);
- }
- }
- for (RMap map : editor.mapEditor.getActiveMaps()) {
- Document doc = new Document().setRootElement(map.toElement());
- saveFile(doc, "maps", map.id + ".xml");
- }
- }
-
- private void saveQuests() {
- for (String name : files.listFiles(store.getActive().getPath()[0], "quests")) {
- String quest =
- name.substring(name.lastIndexOf(File.separator) + 1, name.length() - 4); // -4 for ".xml"
- if (Editor.resources.getResource(quest, "quest") == null) {
- files.delete(name);
- }
- }
- for (RQuest quest : Editor.resources.getResources(RQuest.class)) {
- saveFile(new Document(quest.toElement()), "quests", quest.id + ".xml");
- }
- }
-
- private void saveScripts() {
- for (String name : files.listFiles(store.getActive().getPath()[0], "scripts")) {
- String script =
- name.substring(name.lastIndexOf(File.separator) + 1, name.length() - 3); // -3 for ".js"
- if (!store.getScripts().containsKey(script)) {
- files.delete(name);
- }
- }
- for (RScript script : store.getScripts().values()) {
- saveFile(script.script, "scripts", script.id + ".js");
- }
- }
-
- private void saveFile(String text, String... file) {
- String[] fullPath = new String[file.length + 1];
- System.arraycopy(file, 0, fullPath, 1, file.length);
- fullPath[0] = store.getActive().getPath()[0];
- files.saveFile(text, new StringTranslator(), fullPath);
- }
-
- private void saveFile(Document doc, String... file) {
- String[] fullPath = new String[file.length + 1];
- System.arraycopy(file, 0, fullPath, 1, file.length);
- fullPath[0] = store.getActive().getPath()[0];
- files.saveFile(doc, new XMLTranslator(), fullPath);
- }
-
- private boolean isExtension(String path) {
- Document doc = files.getFile(new XMLTranslator(), path, "main.xml");
- return doc.getRootElement().getName().equals("extension");
- }
-
- private boolean isMod(String path) {
- try { // main.xml must exist and be valid xml
- return files.getFile(new XMLTranslator(), path, "main.xml") != null;
- } catch (NullPointerException e) {
- return false;
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2013 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import lombok.extern.slf4j.Slf4j;
+import neon.editor.resources.*;
+import neon.maps.model.DungeonModel;
+import neon.maps.model.WorldModel;
+import neon.resources.RCraft;
+import neon.resources.RCreature;
+import neon.resources.RDungeonTheme;
+import neon.resources.RItem;
+import neon.resources.RMod;
+import neon.resources.RPerson;
+import neon.resources.RRecipe;
+import neon.resources.RRegionTheme;
+import neon.resources.RScript;
+import neon.resources.RSign;
+import neon.resources.RSpell;
+import neon.resources.RTattoo;
+import neon.resources.RTerrain;
+import neon.resources.RZoneTheme;
+import neon.resources.quest.RQuest;
+import neon.systems.files.FileSystem;
+import neon.systems.files.JacksonMapper;
+import neon.systems.files.StringTranslator;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.input.SAXBuilder;
+
+@Slf4j
+public class ModFiler {
+ private final FileSystem files;
+ private final DataStore dataStore;
+ private final Editor editor;
+ private final JFrame frame;
+
+ public ModFiler(JFrame frame, FileSystem files, DataStore dataStore, Editor editor) {
+ this.frame = frame;
+ this.files = files;
+ this.dataStore = dataStore;
+ this.editor = editor;
+ }
+
+ /**
+ * Bridge method for loading XML files as JDOM Documents without using XMLTranslator.
+ *
+ * @param path the path components to the XML file
+ * @return JDOM Document, or null if file doesn't exist
+ */
+ private Document loadAsDocument(String... path) {
+ try {
+ java.io.InputStream stream = files.getStream(path);
+ if (stream == null) {
+ return null;
+ }
+ org.jdom2.input.SAXBuilder builder = new org.jdom2.input.SAXBuilder();
+ Document doc = builder.build(stream);
+ stream.close();
+ return doc;
+ } catch (Exception e) {
+ log.error("Failed to load XML as Document: {}", java.util.Arrays.toString(path), e);
+ return null;
+ }
+ }
+
+ /**
+ * Saves a JDOM Document to the file system without using XMLTranslator.
+ *
+ * @param doc the JDOM Document to save
+ * @param path the path components
+ */
+ private void saveDocument(Document doc, String... path) {
+ try {
+ org.jdom2.output.XMLOutputter outputter = new org.jdom2.output.XMLOutputter();
+ outputter.setFormat(org.jdom2.output.Format.getPrettyFormat());
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ outputter.output(doc, out);
+
+ // Write to file using FileSystem
+ String fullPath = files.getFullPath(String.join(File.separator, path));
+ java.io.FileOutputStream fileOut = new java.io.FileOutputStream(fullPath);
+ out.writeTo(fileOut);
+ fileOut.close();
+ } catch (Exception e) {
+ log.error("Failed to save Document: {}", java.util.Arrays.toString(path), e);
+ }
+ }
+
+ void loadMod() {
+ // hacky way to make the filechooser start in the game dir
+ JFileChooser chooser = new JFileChooser(new File("neon.ini.xml"));
+ chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
+ chooser.setDialogTitle("Choose module");
+ if (chooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
+ load(chooser.getSelectedFile(), true);
+ }
+ }
+
+ void load(File file, boolean active) {
+ String path = file.getPath();
+ try {
+ path = files.mount(path);
+ if (!isMod(path)) { // check if this is a mod
+ JOptionPane.showMessageDialog(frame, "Selected file is not a valid mod.");
+ files.removePath(path);
+ } else {
+ if (isExtension(path)) { // if extension: load all masters
+ Document doc = loadAsDocument(path, "main.xml");
+ if (doc != null) {
+ for (Object master : doc.getRootElement().getChildren("master")) {
+ String id = ((Element) master).getText();
+ Document ini = new Document();
+ try { // check in neon.ini.xml which mods exist
+ FileInputStream in = new FileInputStream("neon.ini.xml");
+ ini = new SAXBuilder().build(in);
+ in.close();
+ } catch (JDOMException e) {
+ log.error("Error reading", e);
+ }
+
+ // check if there is a mod with the correct id
+ for (Element mod : ini.getRootElement().getChild("files").getChildren()) {
+ if (!mod.getText().equals(path)) { // make sure current mod is not loaded again
+ System.out.println(mod.getText() + ", " + path);
+ files.mount(mod.getText());
+ Document d = loadAsDocument(mod.getText(), "main.xml");
+ if (d != null && d.getRootElement().getAttributeValue("id").equals(id)) {
+ dataStore.loadData(mod.getText(), false, false);
+ } else {
+ files.removePath(mod.getText());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ frame.setTitle("Neon Editor: " + path);
+ dataStore.loadData(path, active, isExtension(path));
+ editor.mapEditor.loadMaps(dataStore.getResourceManager().getResources(RMap.class), path);
+ editor.enableEditing(file.isDirectory());
+ frame.pack();
+ }
+ } catch (IOException e1) {
+ log.error("File is not a mod", e1);
+ JOptionPane.showMessageDialog(frame, "Selected file is not a valid mod.");
+ }
+ }
+
+ public void save() {
+ JacksonXmlBuilder builder = new JacksonXmlBuilder(dataStore);
+ RMod active = dataStore.getActive();
+ saveFile(new Document(dataStore.getActive().getMainElement()), "main.xml");
+ saveFile(new Document(dataStore.getActive().getCCElement()), "cc.xml");
+ saveFile(
+ builder.getResourceDoc(
+ dataStore.getResourceManager().getResources(RItem.class), "items", active),
+ "objects",
+ "items.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RFaction.class), "factions", active),
+ "factions.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RRecipe.class), "recipes", active),
+ "objects",
+ "alchemy.xml");
+ saveFile(builder.getEventsDoc(), "events.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RPerson.class), "people", active),
+ "objects",
+ "npc.xml");
+ saveFile(
+ builder.getResourceDoc(
+ dataStore.getResourceManager().getResources(RCreature.class), "monsters", active),
+ "objects",
+ "monsters.xml");
+ saveFile(
+ builder.getResourceDoc(
+ dataStore.getResourceManager().getResources(RSpell.class), "spells", active),
+ "spells.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RTerrain.class), "terrain", active),
+ "terrain.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RCraft.class), "items", active),
+ "objects",
+ "crafting.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RSign.class), "signs", active),
+ "signs.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RTattoo.class), "tattoos", active),
+ "tattoos.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RZoneTheme.class), "themes", active),
+ "themes",
+ "zones.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RDungeonTheme.class), "themes", active),
+ "themes",
+ "dungeons.xml");
+ saveFile(
+ builder.getListDoc(
+ dataStore.getResourceManager().getResources(RRegionTheme.class), "themes", active),
+ "themes",
+ "regions.xml");
+ saveMaps();
+ saveQuests();
+ saveScripts();
+ }
+
+ /**
+ * Saves all maps using Jackson XML serialization.
+ *
+ * NOTE: Maps use Jackson via toWorldModel()/toDungeonModel(). Other resources still use
+ * toElement() bridge with JDOM. XMLTranslator has been eliminated. Full migration of resource
+ * saving to Jackson deferred to future phase.
+ */
+ private void saveMaps() {
+ // Delete maps that no longer exist
+ for (String name : files.listFiles(dataStore.getActive().getPath()[0], "maps")) {
+ String map =
+ name.substring(name.lastIndexOf(File.separator) + 1, name.length() - 4); // -4 for ".xml"
+ if (dataStore.getResourceManager().getResource(map, "maps") == null) {
+ files.delete(name);
+ }
+ }
+
+ // Save maps using Jackson serialization
+ JacksonMapper mapper = new JacksonMapper();
+ for (RMap map : editor.mapEditor.getActiveMaps()) {
+ try {
+ ByteArrayOutputStream out;
+ if (map.isDungeon()) {
+ DungeonModel model = map.toDungeonModel();
+ out = mapper.toXml(model);
+ } else {
+ WorldModel model = map.toWorldModel();
+ out = mapper.toXml(model);
+ }
+ // Convert ByteArrayOutputStream to String for saveFile
+ String xml = out.toString(StandardCharsets.UTF_8);
+ saveFile(xml, "maps", map.id + ".xml");
+ } catch (Exception e) {
+ System.err.println("Failed to save map: " + map.id);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void saveQuests() {
+ JacksonMapper mapper = new JacksonMapper();
+ for (String name : files.listFiles(dataStore.getActive().getPath()[0], "quests")) {
+ String quest =
+ name.substring(name.lastIndexOf(File.separator) + 1, name.length() - 4); // -4 for ".xml"
+ if (dataStore.getResourceManager().getResource(quest, "quest") == null) {
+ files.delete(name);
+ }
+ }
+ for (RQuest quest : dataStore.getResourceManager().getResources(RQuest.class)) {
+ try {
+ // Serialize quest with Jackson
+ ByteArrayOutputStream out = mapper.toXml(quest);
+ String xml = out.toString(StandardCharsets.UTF_8);
+ saveFile(xml, "quests", quest.id + ".xml");
+ } catch (Exception e) {
+ System.err.println("Failed to save quest: " + quest.id);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void saveScripts() {
+ for (String name : files.listFiles(dataStore.getActive().getPath()[0], "scripts")) {
+ String script =
+ name.substring(name.lastIndexOf(File.separator) + 1, name.length() - 3); // -3 for ".js"
+ if (!dataStore.getScripts().containsKey(script)) {
+ files.delete(name);
+ }
+ }
+ for (RScript script : dataStore.getScripts().values()) {
+ saveFile(script.script, "scripts", script.id + ".js");
+ }
+ }
+
+ private void saveFile(String text, String... file) {
+ String[] fullPath = new String[file.length + 1];
+ System.arraycopy(file, 0, fullPath, 1, file.length);
+ fullPath[0] = dataStore.getActive().getPath()[0];
+ files.saveFile(text, new StringTranslator(), fullPath);
+ }
+
+ private void saveFile(Document doc, String... file) {
+ String[] fullPath = new String[file.length + 1];
+ System.arraycopy(file, 0, fullPath, 1, file.length);
+ fullPath[0] = dataStore.getActive().getPath()[0];
+ saveDocument(doc, fullPath);
+ }
+
+ private boolean isExtension(String path) {
+ Document doc = loadAsDocument(path, "main.xml");
+ return doc != null && doc.getRootElement().getName().equals("extension");
+ }
+
+ private boolean isMod(String path) {
+ try { // main.xml must exist and be valid xml
+ return loadAsDocument(path, "main.xml") != null;
+ } catch (NullPointerException e) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/NeonFormat.java b/src/main/java/neon/editor/NeonFormat.java
index d184a00..10d39cd 100644
--- a/src/main/java/neon/editor/NeonFormat.java
+++ b/src/main/java/neon/editor/NeonFormat.java
@@ -22,8 +22,8 @@
import java.util.Locale;
public class NeonFormat {
- private static NumberFormat intFormat = NumberFormat.getIntegerInstance();
- private static NumberFormat floatFormat = NumberFormat.getInstance(Locale.ENGLISH);
+ private static final NumberFormat intFormat = NumberFormat.getIntegerInstance();
+ private static final NumberFormat floatFormat = NumberFormat.getInstance(Locale.ENGLISH);
public static NumberFormat getIntegerInstance() {
intFormat.setGroupingUsed(false);
diff --git a/src/main/java/neon/editor/NewObjectDialog.java b/src/main/java/neon/editor/NewObjectDialog.java
index 8325a4c..21e2736 100644
--- a/src/main/java/neon/editor/NewObjectDialog.java
+++ b/src/main/java/neon/editor/NewObjectDialog.java
@@ -28,6 +28,11 @@ public class NewObjectDialog implements ActionListener {
private JDialog dialog;
private boolean cancelled;
private String namespace = null;
+ private final DataStore dataStore;
+
+ public NewObjectDialog(DataStore dataStore) {
+ this.dataStore = dataStore;
+ }
public Properties showInputDialog(JFrame frame, String message, String namespace) {
this.namespace = namespace;
@@ -81,15 +86,15 @@ public void actionPerformed(ActionEvent e) {
public boolean exists(String id) {
if (namespace == null) {
- return Editor.resources.getResource(id) != null;
+ return dataStore.getResourceManager().getResource(id) != null;
} else {
- return Editor.resources.getResource(id, namespace) != null;
+ return dataStore.getResourceManager().getResource(id, namespace) != null;
}
}
public static class Properties {
- private String id;
- private boolean ok;
+ private final String id;
+ private final boolean ok;
public Properties(boolean cancelled, String id) {
this.ok = !cancelled;
diff --git a/src/main/java/neon/editor/ObjectNode.java b/src/main/java/neon/editor/ObjectNode.java
index 034233b..3f7c91a 100644
--- a/src/main/java/neon/editor/ObjectNode.java
+++ b/src/main/java/neon/editor/ObjectNode.java
@@ -24,31 +24,27 @@
import java.util.Vector;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
+import lombok.Getter;
import neon.resources.RData;
@SuppressWarnings("serial")
public class ObjectNode extends DefaultMutableTreeNode {
- private static NodeComparator nodeComparator = new NodeComparator();
- private RData resource;
- private ObjectType type;
+ private static final NodeComparator nodeComparator = new NodeComparator();
+ @Getter private RData resource;
+ @Getter private final ObjectType type;
private String name;
+ private final DataStore dataStore;
- public ObjectNode(RData r, ObjectType t) {
+ public ObjectNode(RData r, ObjectType t, DataStore dataStore) {
resource = r;
type = t;
+ this.dataStore = dataStore;
}
- public ObjectNode(String name, ObjectType t) {
+ public ObjectNode(String name, ObjectType t, DataStore dataStore) {
this.name = name;
type = t;
- }
-
- public RData getResource() {
- return resource;
- }
-
- public ObjectType getType() {
- return type;
+ this.dataStore = dataStore;
}
public String toString() {
@@ -57,7 +53,7 @@ public String toString() {
}
String id = resource.id; // leaf node
- if (!resource.getPath()[0].equals(Editor.getStore().getActive().get("id"))) {
+ if (!resource.getPath()[0].equals(dataStore.getActive().get("id"))) {
// niet-actieve data is cursief weergegeven
return "" + id + "";
} else {
@@ -98,6 +94,6 @@ public enum ObjectType {
SCROLL,
LEVEL_CREATURE,
LEVEL_ITEM,
- FOOD;
+ FOOD
}
}
diff --git a/src/main/java/neon/editor/ObjectTransferHandler.java b/src/main/java/neon/editor/ObjectTransferHandler.java
index 5f5177b..bcb7925 100644
--- a/src/main/java/neon/editor/ObjectTransferHandler.java
+++ b/src/main/java/neon/editor/ObjectTransferHandler.java
@@ -32,12 +32,14 @@
@SuppressWarnings("serial")
public class ObjectTransferHandler extends TransferHandler {
- private RZone zone;
- private EditablePane pane;
+ private final RZone zone;
+ private final EditablePane pane;
+ private final DataStore dataStore;
public ObjectTransferHandler(RZone zone, EditablePane pane) {
this.zone = zone;
this.pane = pane;
+ this.dataStore = zone.getDataStore();
}
public boolean canImport(TransferHandler.TransferSupport ts) {
@@ -49,9 +51,8 @@ public boolean importData(TransferHandler.TransferSupport ts) {
String id = ts.getTransferable().getTransferData(DataFlavor.stringFlavor).toString();
String type = "item";
- if (Editor.resources.getResource(id) instanceof RItem) {
+ if (dataStore.getResourceManager().getResource(id) instanceof RItem item) {
System.out.println(id);
- RItem item = (RItem) Editor.resources.getResource(id);
if (item instanceof RItem.Door) {
type = "door";
} else if (item.type == Type.container) {
@@ -73,7 +74,7 @@ public boolean importData(TransferHandler.TransferSupport ts) {
e.setAttribute("id", id);
e.setAttribute("uid", Integer.toString(zone.map.createUID(e)));
- Instance instance = RZone.getInstance(e, zone);
+ Instance instance = zone.getInstance(e, zone);
zone.getScene().addElement(instance, instance.getBounds(), instance.z);
UndoAction undo = new UndoAction.Drop(instance, zone.getScene());
diff --git a/src/main/java/neon/editor/ObjectTreeListener.java b/src/main/java/neon/editor/ObjectTreeListener.java
index c62f14c..a6bc806 100644
--- a/src/main/java/neon/editor/ObjectTreeListener.java
+++ b/src/main/java/neon/editor/ObjectTreeListener.java
@@ -1,240 +1,242 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2013 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.tree.*;
-import neon.editor.editors.ArmorEditor;
-import neon.editor.editors.BookEditor;
-import neon.editor.editors.ClothingEditor;
-import neon.editor.editors.ContainerEditor;
-import neon.editor.editors.CreatureEditor;
-import neon.editor.editors.DoorEditor;
-import neon.editor.editors.FoodEditor;
-import neon.editor.editors.ItemEditor;
-import neon.editor.editors.LevelCreatureEditor;
-import neon.editor.editors.LevelItemEditor;
-import neon.editor.editors.LightEditor;
-import neon.editor.editors.MoneyEditor;
-import neon.editor.editors.NPCEditor;
-import neon.editor.editors.ObjectEditor;
-import neon.editor.editors.PotionEditor;
-import neon.editor.editors.ScrollEditor;
-import neon.editor.editors.WeaponEditor;
-import neon.resources.*;
-import neon.resources.RItem.Type;
-
-public class ObjectTreeListener implements MouseListener {
- private JTree tree;
- private JFrame frame;
- private DefaultTreeModel model;
-
- public ObjectTreeListener(JTree objectTree, JFrame frame) {
- this.frame = frame;
- tree = objectTree;
- model = (DefaultTreeModel) tree.getModel();
- }
-
- public void mouseExited(MouseEvent e) {}
-
- public void mouseEntered(MouseEvent e) {}
-
- public void mouseReleased(MouseEvent e) {}
-
- public void mousePressed(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON1) {
- if (e.getClickCount() == 2) {
- DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
- if (tree.getSelectionPath() != null && node.getLevel() == 2) {
- launchEditor((ObjectNode) node);
- }
- }
- }
- }
-
- public void mouseClicked(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON3) {
- JPopupMenu menu = new JPopupMenu();
- tree.setSelectionRow(tree.getRowForLocation(e.getX(), e.getY()));
- ObjectNode node = (ObjectNode) tree.getLastSelectedPathComponent();
- if (tree.getSelectionPath() != null && node.getLevel() == 2) {
- menu.add(new ClickAction("Delete object", node.getType()));
- menu.add(new ClickAction("Edit object", node.getType()));
- menu.add(new ClickAction("New " + node.getType().toString().toLowerCase(), node.getType()));
- menu.show(e.getComponent(), e.getX(), e.getY());
- } else if (tree.getSelectionPath() != null && node.getLevel() == 1) {
- menu.add(new ClickAction("New " + node.getType().toString().toLowerCase(), node.getType()));
- menu.show(e.getComponent(), e.getX(), e.getY());
- }
- }
- }
-
- private void launchEditor(ObjectNode node) {
- ObjectEditor editor;
- switch (node.getType()) {
- case LEVEL_CREATURE:
- editor = new LevelCreatureEditor(frame, (LCreature) node.getResource());
- break;
- case BOOK:
- editor = new BookEditor(frame, (RItem.Text) node.getResource());
- break;
- case ARMOR:
- editor = new ArmorEditor(frame, (RClothing) node.getResource());
- break;
- case CLOTHING:
- editor = new ClothingEditor(frame, (RClothing) node.getResource());
- break;
- case CONTAINER:
- editor = new ContainerEditor(frame, (RItem.Container) node.getResource());
- break;
- case DOOR:
- editor = new DoorEditor(frame, (RItem.Door) node.getResource());
- break;
- case ITEM:
- editor = new ItemEditor(frame, (RItem) node.getResource());
- break;
- case LIGHT:
- editor = new LightEditor(frame, (RItem) node.getResource());
- break;
- case MONEY:
- editor = new MoneyEditor(frame, (RItem) node.getResource());
- break;
- case NPC:
- editor = new NPCEditor(frame, (RPerson) node.getResource());
- break;
- case POTION:
- editor = new PotionEditor(frame, (RItem) node.getResource());
- break;
- case SCROLL:
- editor = new ScrollEditor(frame, (RItem.Text) node.getResource());
- break;
- case LEVEL_ITEM:
- editor = new LevelItemEditor(frame, (LItem) node.getResource());
- break;
- case WEAPON:
- editor = new WeaponEditor(frame, (RWeapon) node.getResource());
- break;
- case FOOD:
- editor = new FoodEditor(frame, (RItem) node.getResource());
- break;
- default:
- editor = new CreatureEditor(frame, (RCreature) node.getResource());
- break;
- }
- editor.show();
- }
-
- @SuppressWarnings("serial")
- public class ClickAction extends AbstractAction {
- private ObjectNode.ObjectType type;
-
- public ClickAction(String name, ObjectNode.ObjectType type) {
- super(name);
- this.type = type;
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("New " + type.toString().toLowerCase())) {
- NewObjectDialog.Properties props =
- new NewObjectDialog()
- .showInputDialog(frame, "Create new " + type.toString().toLowerCase());
- if (!props.cancelled()) {
- RData object;
- // create item and add to dataStore
- switch (type) {
- case CREATURE:
- object = new RCreature(props.getID(), Editor.getStore().getActive().get("id"));
- break;
- case LEVEL_CREATURE:
- object = new LCreature(props.getID(), Editor.getStore().getActive().get("id"));
- break;
- case NPC:
- object = new RPerson(props.getID(), Editor.getStore().getActive().get("id"));
- break;
- case BOOK:
- case SCROLL:
- object =
- new RItem.Text(
- props.getID(),
- Type.valueOf(type.toString().toLowerCase()),
- Editor.getStore().getActive().get("id"));
- break;
- case DOOR:
- object =
- new RItem.Door(
- props.getID(),
- Type.valueOf(type.toString().toLowerCase()),
- Editor.getStore().getActive().get("id"));
- break;
- case WEAPON:
- object =
- new RWeapon(
- props.getID(),
- Type.valueOf(type.toString().toLowerCase()),
- Editor.getStore().getActive().get("id"));
- break;
- case CLOTHING:
- case ARMOR:
- object =
- new RClothing(
- props.getID(),
- Type.valueOf(type.toString().toLowerCase()),
- Editor.getStore().getActive().get("id"));
- break;
- case LEVEL_ITEM:
- object = new LItem(props.getID(), Editor.getStore().getActive().get("id"));
- break;
- default:
- object =
- new RItem(
- props.getID(),
- Type.valueOf(type.toString().toLowerCase()),
- Editor.getStore().getActive().get("id"));
- break;
- }
-
- // push node to the correct place in tree
- Editor.resources.addResource(object);
- ObjectNode node = new ObjectNode(object, type);
- DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
- for (int i = 0; i < root.getChildCount(); i++) {
- ObjectNode parent = (ObjectNode) root.getChildAt(i);
- if (parent.getType() == type) {
- parent.add(node);
- tree.updateUI(); // otherwise the tree doesn't update after adding
- Object[] nodes = {root, parent, node};
- TreePath path = new TreePath(nodes);
- tree.scrollPathToVisible(path);
- tree.setSelectionPath(path);
- }
- }
- launchEditor(node);
- }
- } else if (e.getActionCommand().equals("Edit object")) {
- launchEditor((ObjectNode) tree.getLastSelectedPathComponent());
- } else if (e.getActionCommand().equals("Delete object")) {
- // remove from tree and datastore
- ObjectNode node = (ObjectNode) tree.getLastSelectedPathComponent();
- model.removeNodeFromParent(node);
- Editor.resources.removeResource(node.getResource());
- }
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2013 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import neon.editor.editors.ArmorEditor;
+import neon.editor.editors.BookEditor;
+import neon.editor.editors.ClothingEditor;
+import neon.editor.editors.ContainerEditor;
+import neon.editor.editors.CreatureEditor;
+import neon.editor.editors.DoorEditor;
+import neon.editor.editors.FoodEditor;
+import neon.editor.editors.ItemEditor;
+import neon.editor.editors.LevelCreatureEditor;
+import neon.editor.editors.LevelItemEditor;
+import neon.editor.editors.LightEditor;
+import neon.editor.editors.MoneyEditor;
+import neon.editor.editors.NPCEditor;
+import neon.editor.editors.ObjectEditor;
+import neon.editor.editors.PotionEditor;
+import neon.editor.editors.ScrollEditor;
+import neon.editor.editors.WeaponEditor;
+import neon.resources.*;
+import neon.resources.RItem.Type;
+
+public class ObjectTreeListener implements MouseListener {
+ private final JTree tree;
+ private final JFrame frame;
+ private final DefaultTreeModel model;
+ private final DataStore dataStore;
+
+ public ObjectTreeListener(JTree objectTree, JFrame frame, DataStore dataStore) {
+ this.frame = frame;
+ tree = objectTree;
+ this.dataStore = dataStore;
+ model = (DefaultTreeModel) tree.getModel();
+ }
+
+ public void mouseExited(MouseEvent e) {}
+
+ public void mouseEntered(MouseEvent e) {}
+
+ public void mouseReleased(MouseEvent e) {}
+
+ public void mousePressed(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON1) {
+ if (e.getClickCount() == 2) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+ if (tree.getSelectionPath() != null && node.getLevel() == 2) {
+ launchEditor((ObjectNode) node);
+ }
+ }
+ }
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ JPopupMenu menu = new JPopupMenu();
+ tree.setSelectionRow(tree.getRowForLocation(e.getX(), e.getY()));
+ ObjectNode node = (ObjectNode) tree.getLastSelectedPathComponent();
+ if (tree.getSelectionPath() != null && node.getLevel() == 2) {
+ menu.add(new ClickAction("Delete object", node.getType()));
+ menu.add(new ClickAction("Edit object", node.getType()));
+ menu.add(new ClickAction("New " + node.getType().toString().toLowerCase(), node.getType()));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ } else if (tree.getSelectionPath() != null && node.getLevel() == 1) {
+ menu.add(new ClickAction("New " + node.getType().toString().toLowerCase(), node.getType()));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+ }
+
+ private void launchEditor(ObjectNode node) {
+ ObjectEditor editor;
+ switch (node.getType()) {
+ case LEVEL_CREATURE:
+ editor = new LevelCreatureEditor(frame, (LCreature) node.getResource(), dataStore);
+ break;
+ case BOOK:
+ editor = new BookEditor(frame, (RItem.Text) node.getResource(), dataStore);
+ break;
+ case ARMOR:
+ editor = new ArmorEditor(frame, (RClothing) node.getResource(), dataStore);
+ break;
+ case CLOTHING:
+ editor = new ClothingEditor(frame, (RClothing) node.getResource(), dataStore);
+ break;
+ case CONTAINER:
+ editor = new ContainerEditor(frame, (RItem.Container) node.getResource(), dataStore);
+ break;
+ case DOOR:
+ editor = new DoorEditor(frame, (RItem.Door) node.getResource(), dataStore);
+ break;
+ case ITEM:
+ editor = new ItemEditor(frame, (RItem) node.getResource(), dataStore);
+ break;
+ case LIGHT:
+ editor = new LightEditor(frame, (RItem) node.getResource(), dataStore);
+ break;
+ case MONEY:
+ editor = new MoneyEditor(frame, (RItem) node.getResource(), dataStore);
+ break;
+ case NPC:
+ editor = new NPCEditor(frame, (RPerson) node.getResource(), dataStore);
+ break;
+ case POTION:
+ editor = new PotionEditor(frame, (RItem) node.getResource(), dataStore);
+ break;
+ case SCROLL:
+ editor = new ScrollEditor(frame, (RItem.Text) node.getResource(), dataStore);
+ break;
+ case LEVEL_ITEM:
+ editor = new LevelItemEditor(frame, (LItem) node.getResource(), dataStore);
+ break;
+ case WEAPON:
+ editor = new WeaponEditor(frame, (RWeapon) node.getResource(), dataStore);
+ break;
+ case FOOD:
+ editor = new FoodEditor(frame, (RItem) node.getResource(), dataStore);
+ break;
+ default:
+ editor = new CreatureEditor(frame, (RCreature) node.getResource(), dataStore);
+ break;
+ }
+ editor.show();
+ }
+
+ @SuppressWarnings("serial")
+ public class ClickAction extends AbstractAction {
+ private final ObjectNode.ObjectType type;
+
+ public ClickAction(String name, ObjectNode.ObjectType type) {
+ super(name);
+ this.type = type;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("New " + type.toString().toLowerCase())) {
+ NewObjectDialog.Properties props =
+ new NewObjectDialog(dataStore)
+ .showInputDialog(frame, "Create new " + type.toString().toLowerCase());
+ if (!props.cancelled()) {
+ RData object;
+ // create item and add to dataStore
+ switch (type) {
+ case CREATURE:
+ object = new RCreature(props.getID(), dataStore.getActive().get("id"));
+ break;
+ case LEVEL_CREATURE:
+ object = new LCreature(props.getID(), dataStore.getActive().get("id"));
+ break;
+ case NPC:
+ object = new RPerson(props.getID(), dataStore.getActive().get("id"));
+ break;
+ case BOOK:
+ case SCROLL:
+ object =
+ new RItem.Text(
+ props.getID(),
+ Type.valueOf(type.toString().toLowerCase()),
+ dataStore.getActive().get("id"));
+ break;
+ case DOOR:
+ object =
+ new RItem.Door(
+ props.getID(),
+ Type.valueOf(type.toString().toLowerCase()),
+ dataStore.getActive().get("id"));
+ break;
+ case WEAPON:
+ object =
+ new RWeapon(
+ props.getID(),
+ Type.valueOf(type.toString().toLowerCase()),
+ dataStore.getActive().get("id"));
+ break;
+ case CLOTHING:
+ case ARMOR:
+ object =
+ new RClothing(
+ props.getID(),
+ Type.valueOf(type.toString().toLowerCase()),
+ dataStore.getActive().get("id"));
+ break;
+ case LEVEL_ITEM:
+ object = new LItem(props.getID(), dataStore.getActive().get("id"));
+ break;
+ default:
+ object =
+ new RItem(
+ props.getID(),
+ Type.valueOf(type.toString().toLowerCase()),
+ dataStore.getActive().get("id"));
+ break;
+ }
+
+ // push node to the correct place in tree
+ dataStore.getResourceManager().addResource(object);
+ ObjectNode node = new ObjectNode(object, type, dataStore);
+ DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
+ for (int i = 0; i < root.getChildCount(); i++) {
+ ObjectNode parent = (ObjectNode) root.getChildAt(i);
+ if (parent.getType() == type) {
+ parent.add(node);
+ tree.updateUI(); // otherwise the tree doesn't update after adding
+ Object[] nodes = {root, parent, node};
+ TreePath path = new TreePath(nodes);
+ tree.scrollPathToVisible(path);
+ tree.setSelectionPath(path);
+ }
+ }
+ launchEditor(node);
+ }
+ } else if (e.getActionCommand().equals("Edit object")) {
+ launchEditor((ObjectNode) tree.getLastSelectedPathComponent());
+ } else if (e.getActionCommand().equals("Delete object")) {
+ // remove from tree and datastore
+ ObjectNode node = (ObjectNode) tree.getLastSelectedPathComponent();
+ model.removeNodeFromParent(node);
+ dataStore.getResourceManager().removeResource(node.getResource());
+ }
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/ResourceAction.java b/src/main/java/neon/editor/ResourceAction.java
index dbd1685..66c929c 100644
--- a/src/main/java/neon/editor/ResourceAction.java
+++ b/src/main/java/neon/editor/ResourceAction.java
@@ -44,26 +44,28 @@
@SuppressWarnings("serial")
public class ResourceAction extends AbstractAction {
- private ResourceType type;
- private JTree tree;
- private DefaultTreeModel model;
- private ResourceTreeListener listener;
+ private final ResourceType type;
+ private final JTree tree;
+ private final DefaultTreeModel model;
+ private final ResourceTreeListener listener;
+ private final DataStore dataStore;
- public ResourceAction(String name, ResourceType type, ResourceTreeListener listener) {
+ public ResourceAction(
+ String name, ResourceType type, ResourceTreeListener listener, DataStore dataStore) {
super(name);
this.type = type;
this.listener = listener;
tree = listener.getTree();
- ;
+ this.dataStore = dataStore;
model = (DefaultTreeModel) tree.getModel();
}
public void actionPerformed(ActionEvent e) {
- String mod = Editor.getStore().getActive().get("id");
+ String mod = dataStore.getActive().get("id");
RData object = null;
if (e.getActionCommand().startsWith("New")) {
NewObjectDialog.Properties props =
- new NewObjectDialog()
+ new NewObjectDialog(dataStore)
.showInputDialog(
Editor.getFrame(),
"Create new " + type.toString().toLowerCase(),
@@ -71,7 +73,8 @@ public void actionPerformed(ActionEvent e) {
if (!props.cancelled()) {
switch (type) {
case RECIPE:
- Collection potions = Editor.resources.getResources(RItem.Potion.class);
+ Collection potions =
+ dataStore.getResourceManager().getResources(RItem.Potion.class);
if (!potions.isEmpty()) { // check if potions have already been created
RItem potion =
(RItem)
@@ -85,14 +88,14 @@ public void actionPerformed(ActionEvent e) {
0);
if (potion != null) {
object = new RRecipe(props.getID(), potion, mod);
- Editor.resources.addResource((RRecipe) object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
}
} else {
JOptionPane.showMessageDialog(Editor.getFrame(), "No potions defined!");
}
break;
case CRAFT:
- Collection items = Editor.resources.getResources(RItem.class);
+ Collection items = dataStore.getResourceManager().getResources(RItem.class);
if (!items.isEmpty()) { // check if items have already been created
RItem craft =
(RItem)
@@ -106,7 +109,7 @@ public void actionPerformed(ActionEvent e) {
0);
if (craft != null) {
object = new RCraft(props.getID(), craft, mod);
- Editor.resources.addResource((RCraft) object);
+ dataStore.getResourceManager().addResource(object);
}
} else {
JOptionPane.showMessageDialog(Editor.getFrame(), "No items defined!");
@@ -114,68 +117,68 @@ public void actionPerformed(ActionEvent e) {
break;
case FACTION:
object = new RFaction(props.getID(), mod);
- Editor.resources.addResource(object, "faction");
+ dataStore.getResourceManager().addResource(object, "faction");
break;
case REGION:
object = new RRegionTheme(props.getID(), mod);
- Editor.resources.addResource(object, "theme");
+ dataStore.getResourceManager().addResource(object, "theme");
break;
case ZONE:
object = new RZoneTheme(props.getID(), mod);
- Editor.resources.addResource(object, "theme");
+ dataStore.getResourceManager().addResource(object, "theme");
break;
case DUNGEON:
object = new RDungeonTheme(props.getID(), mod);
- Editor.resources.addResource(object, "theme");
+ dataStore.getResourceManager().addResource(object, "theme");
break;
case QUEST:
object = new RQuest(props.getID(), mod);
- Editor.resources.addResource(object, "quest");
+ dataStore.getResourceManager().addResource(object, "quest");
break;
case CURSE:
object = new RSpell(props.getID(), SpellType.CURSE, mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case DISEASE:
object = new RSpell(props.getID(), SpellType.DISEASE, mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case ENCHANTMENT:
object = new RSpell.Enchantment(props.getID(), mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case LEVEL_SPELL:
object = new LSpell(props.getID(), mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case POISON:
object = new RSpell(props.getID(), SpellType.POISON, mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case POWER:
object = new RSpell.Power(props.getID(), mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case SIGN:
object = new RSign(props.getID(), mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case SPELL:
object = new RSpell(props.getID(), SpellType.SPELL, mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
case TATTOO:
object = new RTattoo(props.getID(), mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
default:
object = new RSpell(props.getID(), SpellType.SPELL, mod);
- Editor.resources.addResource(object, "magic");
+ dataStore.getResourceManager().addResource(object, "magic");
break;
}
// push node to the correct place in tree
- ResourceNode node = new ResourceNode(object, type);
+ ResourceNode node = new ResourceNode(object, type, dataStore);
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
listener.launchEditor(node);
for (int i = 0; i < root.getChildCount(); i++) {
@@ -196,7 +199,7 @@ public void actionPerformed(ActionEvent e) {
// remove from tree and datastore
ResourceNode node = (ResourceNode) tree.getLastSelectedPathComponent();
model.removeNodeFromParent(node);
- Editor.resources.removeResource(node.getResource());
+ dataStore.getResourceManager().removeResource(node.getResource());
}
}
}
diff --git a/src/main/java/neon/editor/ResourceNode.java b/src/main/java/neon/editor/ResourceNode.java
index ff6c844..b0f156a 100644
--- a/src/main/java/neon/editor/ResourceNode.java
+++ b/src/main/java/neon/editor/ResourceNode.java
@@ -30,19 +30,22 @@
@SuppressWarnings("serial")
public class ResourceNode extends DefaultMutableTreeNode {
- private static NodeComparator nodeComparator = new NodeComparator();
+ private static final NodeComparator nodeComparator = new NodeComparator();
private RData resource;
- private ResourceType type;
+ private final ResourceType type;
private String name;
+ private final DataStore dataStore;
- public ResourceNode(RData r, ResourceType t) {
+ public ResourceNode(RData r, ResourceType t, DataStore dataStore) {
resource = r;
type = t;
+ this.dataStore = dataStore;
}
- public ResourceNode(String name, ResourceType t) {
+ public ResourceNode(String name, ResourceType t, DataStore dataStore) {
this.name = name;
type = t;
+ this.dataStore = dataStore;
}
public RData getResource() {
@@ -63,7 +66,7 @@ public String toString() {
id = resource.name;
}
- if (!resource.getPath()[0].equals(Editor.getStore().getActive().get("id"))) {
+ if (!resource.getPath()[0].equals(dataStore.getActive().get("id"))) {
// niet-actieve data is cursief weergegeven
return "" + id + "";
} else {
@@ -108,13 +111,13 @@ public enum ResourceType {
DISEASE("magic"),
RECIPE("magic");
- private String namespace;
+ private final String namespace;
- private ResourceType() {
+ ResourceType() {
this(null);
}
- private ResourceType(String namespace) {
+ ResourceType(String namespace) {
this.namespace = namespace;
}
diff --git a/src/main/java/neon/editor/ResourceTreeListener.java b/src/main/java/neon/editor/ResourceTreeListener.java
index 1967521..f61b3d8 100644
--- a/src/main/java/neon/editor/ResourceTreeListener.java
+++ b/src/main/java/neon/editor/ResourceTreeListener.java
@@ -1,146 +1,148 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.tree.*;
-import neon.editor.editors.AfflictionEditor;
-import neon.editor.editors.AlchemyEditor;
-import neon.editor.editors.CraftingEditor;
-import neon.editor.editors.DungeonThemeEditor;
-import neon.editor.editors.EnchantmentEditor;
-import neon.editor.editors.FactionEditor;
-import neon.editor.editors.LevelSpellEditor;
-import neon.editor.editors.PoisonEditor;
-import neon.editor.editors.PowerEditor;
-import neon.editor.editors.QuestEditor;
-import neon.editor.editors.RegionThemeEditor;
-import neon.editor.editors.SignEditor;
-import neon.editor.editors.SpellEditor;
-import neon.editor.editors.TattooEditor;
-import neon.editor.editors.ZoneThemeEditor;
-import neon.editor.resources.RFaction;
-import neon.resources.*;
-import neon.resources.quest.RQuest;
-
-public class ResourceTreeListener implements MouseListener {
- private JTree tree;
- private JFrame frame;
-
- public ResourceTreeListener(JTree resourceTree, JFrame parent) {
- tree = resourceTree;
- frame = parent;
- }
-
- protected JTree getTree() {
- return tree;
- }
-
- protected void launchEditor(ResourceNode node) {
- switch (node.getType()) {
- case CRAFT:
- new CraftingEditor(frame, (RCraft) node.getResource()).show();
- break;
- case FACTION:
- new FactionEditor(frame, (RFaction) node.getResource()).show();
- break;
- case DUNGEON:
- new DungeonThemeEditor(frame, (RDungeonTheme) node.getResource()).show();
- break;
- case ZONE:
- new ZoneThemeEditor(frame, (RZoneTheme) node.getResource()).show();
- break;
- case REGION:
- new RegionThemeEditor(frame, (RRegionTheme) node.getResource()).show();
- break;
- case QUEST:
- new QuestEditor(frame, (RQuest) node.getResource()).show();
- break;
- case CURSE:
- new AfflictionEditor(frame, (RSpell) node.getResource()).show();
- break;
- case DISEASE:
- new AfflictionEditor(frame, (RSpell) node.getResource()).show();
- break;
- case ENCHANTMENT:
- new EnchantmentEditor(frame, (RSpell.Enchantment) node.getResource()).show();
- break;
- case LEVEL_SPELL:
- new LevelSpellEditor(frame, (LSpell) node.getResource()).show();
- break;
- case POISON:
- new PoisonEditor(frame, (RSpell) node.getResource()).show();
- break;
- case POWER:
- new PowerEditor(frame, (RSpell.Power) node.getResource()).show();
- break;
- case SIGN:
- new SignEditor(frame, (RSign) node.getResource()).show();
- break;
- case RECIPE:
- new AlchemyEditor(frame, (RRecipe) node.getResource()).show();
- break;
- case TATTOO:
- new TattooEditor(frame, (RTattoo) node.getResource()).show();
- break;
- default:
- new SpellEditor(frame, (RSpell) node.getResource()).show();
- break;
- }
- }
-
- public void mouseExited(MouseEvent e) {}
-
- public void mouseEntered(MouseEvent e) {}
-
- public void mouseReleased(MouseEvent e) {}
-
- public void mousePressed(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON1) {
- if (e.getClickCount() == 2) {
- DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
- if (tree.getSelectionPath() != null && node.getLevel() == 2) {
- launchEditor((ResourceNode) node);
- }
- }
- }
- }
-
- public void mouseClicked(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON3) {
- JPopupMenu menu = new JPopupMenu();
- tree.setSelectionRow(tree.getRowForLocation(e.getX(), e.getY()));
- ResourceNode node = (ResourceNode) tree.getLastSelectedPathComponent();
- if (tree.getSelectionPath() != null && node.getLevel() == 2) {
- menu.add(new ResourceAction("Delete resource", node.getType(), this));
- menu.add(new ResourceAction("Edit resource", node.getType(), this));
- menu.add(
- new ResourceAction(
- "New " + node.getType().toString().toLowerCase(), node.getType(), this));
- menu.show(e.getComponent(), e.getX(), e.getY());
- } else if (tree.getSelectionPath() != null && node.getLevel() == 1) {
- menu.add(
- new ResourceAction(
- "New " + node.getType().toString().toLowerCase(), node.getType(), this));
- menu.show(e.getComponent(), e.getX(), e.getY());
- }
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import neon.editor.editors.AfflictionEditor;
+import neon.editor.editors.AlchemyEditor;
+import neon.editor.editors.CraftingEditor;
+import neon.editor.editors.DungeonThemeEditor;
+import neon.editor.editors.EnchantmentEditor;
+import neon.editor.editors.FactionEditor;
+import neon.editor.editors.LevelSpellEditor;
+import neon.editor.editors.PoisonEditor;
+import neon.editor.editors.PowerEditor;
+import neon.editor.editors.QuestEditor;
+import neon.editor.editors.RegionThemeEditor;
+import neon.editor.editors.SignEditor;
+import neon.editor.editors.SpellEditor;
+import neon.editor.editors.TattooEditor;
+import neon.editor.editors.ZoneThemeEditor;
+import neon.editor.resources.RFaction;
+import neon.resources.*;
+import neon.resources.quest.RQuest;
+
+public class ResourceTreeListener implements MouseListener {
+ private final JTree tree;
+ private final JFrame frame;
+ private final DataStore dataStore;
+
+ public ResourceTreeListener(JTree resourceTree, JFrame parent, DataStore dataStore) {
+ tree = resourceTree;
+ frame = parent;
+ this.dataStore = dataStore;
+ }
+
+ protected JTree getTree() {
+ return tree;
+ }
+
+ protected void launchEditor(ResourceNode node) {
+ switch (node.getType()) {
+ case CRAFT:
+ new CraftingEditor(frame, (RCraft) node.getResource(), dataStore).show();
+ break;
+ case FACTION:
+ new FactionEditor(frame, (RFaction) node.getResource(), dataStore).show();
+ break;
+ case DUNGEON:
+ new DungeonThemeEditor(frame, (RDungeonTheme) node.getResource(), dataStore).show();
+ break;
+ case ZONE:
+ new ZoneThemeEditor(frame, (RZoneTheme) node.getResource(), dataStore).show();
+ break;
+ case REGION:
+ new RegionThemeEditor(frame, (RRegionTheme) node.getResource(), dataStore).show();
+ break;
+ case QUEST:
+ new QuestEditor(frame, (RQuest) node.getResource(), dataStore).show();
+ break;
+ case CURSE:
+ new AfflictionEditor(frame, (RSpell) node.getResource(), dataStore).show();
+ break;
+ case DISEASE:
+ new AfflictionEditor(frame, (RSpell) node.getResource(), dataStore).show();
+ break;
+ case ENCHANTMENT:
+ new EnchantmentEditor(frame, (RSpell.Enchantment) node.getResource(), dataStore).show();
+ break;
+ case LEVEL_SPELL:
+ new LevelSpellEditor(frame, (LSpell) node.getResource(), dataStore).show();
+ break;
+ case POISON:
+ new PoisonEditor(frame, (RSpell) node.getResource(), dataStore).show();
+ break;
+ case POWER:
+ new PowerEditor(frame, (RSpell.Power) node.getResource(), dataStore).show();
+ break;
+ case SIGN:
+ new SignEditor(frame, (RSign) node.getResource(), dataStore).show();
+ break;
+ case RECIPE:
+ new AlchemyEditor(frame, (RRecipe) node.getResource(), dataStore).show();
+ break;
+ case TATTOO:
+ new TattooEditor(frame, (RTattoo) node.getResource(), dataStore).show();
+ break;
+ default:
+ new SpellEditor(frame, (RSpell) node.getResource(), dataStore).show();
+ break;
+ }
+ }
+
+ public void mouseExited(MouseEvent e) {}
+
+ public void mouseEntered(MouseEvent e) {}
+
+ public void mouseReleased(MouseEvent e) {}
+
+ public void mousePressed(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON1) {
+ if (e.getClickCount() == 2) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+ if (tree.getSelectionPath() != null && node.getLevel() == 2) {
+ launchEditor((ResourceNode) node);
+ }
+ }
+ }
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ JPopupMenu menu = new JPopupMenu();
+ tree.setSelectionRow(tree.getRowForLocation(e.getX(), e.getY()));
+ ResourceNode node = (ResourceNode) tree.getLastSelectedPathComponent();
+ if (tree.getSelectionPath() != null && node.getLevel() == 2) {
+ menu.add(new ResourceAction("Delete resource", node.getType(), this, dataStore));
+ menu.add(new ResourceAction("Edit resource", node.getType(), this, dataStore));
+ menu.add(
+ new ResourceAction(
+ "New " + node.getType().toString().toLowerCase(), node.getType(), this, dataStore));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ } else if (tree.getSelectionPath() != null && node.getLevel() == 1) {
+ menu.add(
+ new ResourceAction(
+ "New " + node.getType().toString().toLowerCase(), node.getType(), this, dataStore));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/SVGExporter.java b/src/main/java/neon/editor/SVGExporter.java
index dfa85be..d2fa8f2 100644
--- a/src/main/java/neon/editor/SVGExporter.java
+++ b/src/main/java/neon/editor/SVGExporter.java
@@ -18,12 +18,13 @@
package neon.editor;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import neon.editor.maps.*;
import neon.editor.resources.IRegion;
import neon.systems.files.FileSystem;
-import neon.systems.files.XMLTranslator;
import neon.ui.graphics.Renderable;
import neon.ui.graphics.ZComparator;
import neon.util.ColorFactory;
@@ -49,8 +50,7 @@ public static void exportToSVG(ZoneTreeNode node, FileSystem files, DataStore st
Collections.sort(regions, new ZComparator());
for (Renderable i : regions) {
- if (i instanceof IRegion) {
- IRegion ri = (IRegion) i;
+ if (i instanceof IRegion ri) {
Element region = new Element("rect", ns);
region.setAttribute("x", Integer.toString(ri.x));
region.setAttribute("y", Integer.toString(ri.y));
@@ -64,12 +64,27 @@ public static void exportToSVG(ZoneTreeNode node, FileSystem files, DataStore st
}
}
- files.saveFile(
- doc,
- new XMLTranslator(),
- store.getActive().getPath()[0],
- "shots",
- node.getZone().map.id + ".svg");
+ // Save SVG document directly without XMLTranslator
+ try {
+ org.jdom2.output.XMLOutputter outputter = new org.jdom2.output.XMLOutputter();
+ outputter.setFormat(org.jdom2.output.Format.getPrettyFormat());
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ outputter.output(doc, out);
+
+ String fullPath =
+ files.getFullPath(
+ store.getActive().getPath()[0]
+ + File.separator
+ + "shots"
+ + File.separator
+ + node.getZone().map.id
+ + ".svg");
+ java.io.FileOutputStream fileOut = new java.io.FileOutputStream(fullPath);
+ out.writeTo(fileOut);
+ fileOut.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
}
}
diff --git a/src/main/java/neon/editor/ScriptEditor.java b/src/main/java/neon/editor/ScriptEditor.java
index 2bf6479..30dc7e3 100644
--- a/src/main/java/neon/editor/ScriptEditor.java
+++ b/src/main/java/neon/editor/ScriptEditor.java
@@ -1,168 +1,169 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-import javax.swing.*;
-import javax.swing.border.*;
-import javax.swing.event.*;
-import neon.resources.RScript;
-
-public class ScriptEditor implements ListSelectionListener, ActionListener, MouseListener {
- private JDialog frame;
- private JTextArea text;
- private JList list;
- private DefaultListModel model;
-
- public ScriptEditor(JFrame parent) {
- frame = new JDialog(parent, "Script Editor");
- frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
- frame.setPreferredSize(new Dimension(480, 300));
-
- model = new DefaultListModel();
- list = new JList(model);
- list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- list.addListSelectionListener(this);
- list.setCellRenderer(new ModCellRenderer());
- JScrollPane listScroller = new JScrollPane(list);
- listScroller.setBorder(new TitledBorder("Scripts"));
-
- text = new JTextArea();
- JScrollPane textScroller = new JScrollPane(text);
- textScroller.setBorder(new TitledBorder("Edit script"));
-
- JPanel content = new JPanel(new BorderLayout());
- content.add(listScroller, BorderLayout.LINE_START);
- content.add(textScroller, BorderLayout.CENTER);
- frame.setContentPane(content);
-
- JPanel buttons = new JPanel();
- content.add(buttons, BorderLayout.PAGE_END);
- JButton ok = new JButton("Ok");
- ok.addActionListener(this);
- JButton cancel = new JButton("Cancel");
- cancel.addActionListener(this);
- JButton apply = new JButton("Apply");
- apply.addActionListener(this);
- buttons.add(ok);
- list.addMouseListener(this);
- buttons.add(cancel);
- buttons.add(apply);
- }
-
- public void show() {
- model.clear();
- for (RScript script : Editor.getStore().getScripts().values()) {
- model.addElement(new RScript(script));
- }
- frame.pack();
- frame.setLocationRelativeTo(null);
- frame.setVisible(true);
- }
-
- public void valueChanged(ListSelectionEvent e) {
- // blijkbaar worden er twee events gefired bij selectie
- if (e.getValueIsAdjusting()) {
- if (list.isSelectedIndex(e.getFirstIndex()) && !list.isSelectedIndex(e.getLastIndex())) {
- model.elementAt(e.getLastIndex()).script = text.getText();
- } else if (list.isSelectedIndex(e.getLastIndex())
- && !list.isSelectedIndex(e.getFirstIndex())) {
- model.elementAt(e.getFirstIndex()).script = text.getText();
- }
- text.setText(list.getSelectedValue().script);
- }
- }
-
- private void save() {
- HashMap temp = new HashMap(Editor.getStore().getScripts());
- Editor.getStore().getScripts().clear();
- for (Enumeration rs = model.elements(); rs.hasMoreElements(); ) {
- RScript script = rs.nextElement();
- if (!equals(script, temp.get(script.id))) { // script gewijzigd, dan naar active mod
- script.setPath(Editor.getStore().getActive().get("id"));
- }
- Editor.getStore().getScripts().put(script.id, script);
- }
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("Cancel")) {
- frame.dispose();
- } else if (e.getActionCommand().equals("Apply")) {
- save();
- } else if (e.getActionCommand().equals("Ok")) {
- save();
- frame.dispose();
- }
- }
-
- public boolean equals(RScript one, RScript two) {
- return one.id.equals(two.id) && one.script.equals(two.script);
- }
-
- public void mousePressed(MouseEvent e) {}
-
- public void mouseReleased(MouseEvent e) {}
-
- public void mouseEntered(MouseEvent e) {}
-
- public void mouseExited(MouseEvent e) {}
-
- public void mouseClicked(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON3) {
- JPopupMenu menu = new JPopupMenu();
- menu.add(new ClickAction("New script"));
- menu.add(new ClickAction("Delete script"));
- menu.show(e.getComponent(), e.getX(), e.getY());
- list.setSelectedIndex(list.locationToIndex(new Point(e.getX(), e.getY())));
- }
- }
-
- @SuppressWarnings("serial")
- public class ClickAction extends AbstractAction {
- public ClickAction(String name) {
- super(name);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("New script")) {
- String s =
- (String)
- JOptionPane.showInputDialog(
- frame, "Script name:", "New script", JOptionPane.QUESTION_MESSAGE);
- if ((s != null) && (s.length() > 0)) {
- RScript script = new RScript(s, Editor.getStore().getActive().get("id"));
- model.addElement(script);
- list.setSelectedValue(script, true);
- text.setText("");
- }
- } else if (e.getActionCommand().equals("Delete script")) {
- try {
- if (list.getSelectedIndex() >= 0) {
- int index = list.getSelectedIndex();
- model.remove(index);
- }
- } catch (ArrayIndexOutOfBoundsException a) {
- }
- }
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import neon.resources.RScript;
+
+public class ScriptEditor implements ListSelectionListener, ActionListener, MouseListener {
+ private final JDialog frame;
+ private final DataStore dataStore;
+ private final JTextArea text;
+ private final JList list;
+ private final DefaultListModel model;
+
+ public ScriptEditor(JFrame parent, DataStore dataStore) {
+ frame = new JDialog(parent, "Script Editor");
+ this.dataStore = dataStore;
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.setPreferredSize(new Dimension(480, 300));
+
+ model = new DefaultListModel();
+ list = new JList(model);
+ list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ list.addListSelectionListener(this);
+ list.setCellRenderer(new ModCellRenderer(dataStore));
+ JScrollPane listScroller = new JScrollPane(list);
+ listScroller.setBorder(new TitledBorder("Scripts"));
+
+ text = new JTextArea();
+ JScrollPane textScroller = new JScrollPane(text);
+ textScroller.setBorder(new TitledBorder("Edit script"));
+
+ JPanel content = new JPanel(new BorderLayout());
+ content.add(listScroller, BorderLayout.LINE_START);
+ content.add(textScroller, BorderLayout.CENTER);
+ frame.setContentPane(content);
+
+ JPanel buttons = new JPanel();
+ content.add(buttons, BorderLayout.PAGE_END);
+ JButton ok = new JButton("Ok");
+ ok.addActionListener(this);
+ JButton cancel = new JButton("Cancel");
+ cancel.addActionListener(this);
+ JButton apply = new JButton("Apply");
+ apply.addActionListener(this);
+ buttons.add(ok);
+ list.addMouseListener(this);
+ buttons.add(cancel);
+ buttons.add(apply);
+ }
+
+ public void show() {
+ model.clear();
+ for (RScript script : dataStore.getScripts().values()) {
+ model.addElement(new RScript(script));
+ }
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public void valueChanged(ListSelectionEvent e) {
+ // blijkbaar worden er twee events gefired bij selectie
+ if (e.getValueIsAdjusting()) {
+ if (list.isSelectedIndex(e.getFirstIndex()) && !list.isSelectedIndex(e.getLastIndex())) {
+ model.elementAt(e.getLastIndex()).script = text.getText();
+ } else if (list.isSelectedIndex(e.getLastIndex())
+ && !list.isSelectedIndex(e.getFirstIndex())) {
+ model.elementAt(e.getFirstIndex()).script = text.getText();
+ }
+ text.setText(list.getSelectedValue().script);
+ }
+ }
+
+ private void save() {
+ HashMap temp = new HashMap(dataStore.getScripts());
+ dataStore.getScripts().clear();
+ for (Enumeration rs = model.elements(); rs.hasMoreElements(); ) {
+ RScript script = rs.nextElement();
+ if (!equals(script, temp.get(script.id))) { // script gewijzigd, dan naar active mod
+ script.setPath(dataStore.getActive().get("id"));
+ }
+ dataStore.getScripts().put(script.id, script);
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("Cancel")) {
+ frame.dispose();
+ } else if (e.getActionCommand().equals("Apply")) {
+ save();
+ } else if (e.getActionCommand().equals("Ok")) {
+ save();
+ frame.dispose();
+ }
+ }
+
+ public boolean equals(RScript one, RScript two) {
+ return one.id.equals(two.id) && one.script.equals(two.script);
+ }
+
+ public void mousePressed(MouseEvent e) {}
+
+ public void mouseReleased(MouseEvent e) {}
+
+ public void mouseEntered(MouseEvent e) {}
+
+ public void mouseExited(MouseEvent e) {}
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new ClickAction("New script"));
+ menu.add(new ClickAction("Delete script"));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ list.setSelectedIndex(list.locationToIndex(new Point(e.getX(), e.getY())));
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public class ClickAction extends AbstractAction {
+ public ClickAction(String name) {
+ super(name);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("New script")) {
+ String s =
+ JOptionPane.showInputDialog(
+ frame, "Script name:", "New script", JOptionPane.QUESTION_MESSAGE);
+ if ((s != null) && (s.length() > 0)) {
+ RScript script = new RScript(s, dataStore.getActive().get("id"));
+ model.addElement(script);
+ list.setSelectedValue(script, true);
+ text.setText("");
+ }
+ } else if (e.getActionCommand().equals("Delete script")) {
+ try {
+ if (list.getSelectedIndex() >= 0) {
+ int index = list.getSelectedIndex();
+ model.remove(index);
+ }
+ } catch (ArrayIndexOutOfBoundsException a) {
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/StatusBar.java b/src/main/java/neon/editor/StatusBar.java
index 3d331d9..a44997b 100644
--- a/src/main/java/neon/editor/StatusBar.java
+++ b/src/main/java/neon/editor/StatusBar.java
@@ -24,7 +24,7 @@
@SuppressWarnings("serial")
public class StatusBar extends JComponent {
- private JLabel label;
+ private final JLabel label;
public StatusBar() {
setLayout(new BorderLayout());
diff --git a/src/main/java/neon/editor/XMLBuilder.java b/src/main/java/neon/editor/XMLBuilder.java
deleted file mode 100644
index b7ad47d..0000000
--- a/src/main/java/neon/editor/XMLBuilder.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor;
-
-import java.io.Serializable;
-import java.util.*;
-import neon.resources.RData;
-import neon.resources.RMod;
-import org.jdom2.Document;
-import org.jdom2.Element;
-
-public class XMLBuilder {
- private DataStore store;
-
- public XMLBuilder(DataStore store) {
- this.store = store;
- }
-
- public Document getEventsDoc() {
- Element root = new Element("events");
- for (String script : store.getEvents().keySet()) {
- for (String stamp : store.getEvents().get(script)) {
- Element event = new Element("event");
- event.setAttribute("script", script);
- event.setAttribute("tick", stamp);
- root.addContent(event);
- }
- }
- return new Document(root);
- }
-
- public Document getListDoc(Collection extends RData> elements, String name, RMod mod) {
- Element root = new Element(name);
- for (RData resource : elements) {
- if (resource.getPath()[0].equals(mod.get("id"))) {
- root.addContent(resource.toElement());
- }
- }
- return new Document(root);
- }
-
- public Document getResourceDoc(Collection extends RData> elements, String name, RMod mod) {
- ArrayList buffer = new ArrayList(elements);
- Collections.sort(buffer, new IDComparator());
- Element root = new Element(name);
- for (RData resource : buffer) {
- if (resource.getPath()[0].equals(mod.get("id"))) {
- root.addContent(resource.toElement());
- }
- }
- return new Document(root);
- }
-
- @SuppressWarnings("serial")
- private static class IDComparator implements Comparator, Serializable {
- public int compare(RData arg0, RData arg1) {
- return arg0.id.compareTo(arg1.id);
- }
- }
-}
diff --git a/src/main/java/neon/editor/editors/AfflictionEditor.java b/src/main/java/neon/editor/editors/AfflictionEditor.java
index c658c27..a1917c6 100644
--- a/src/main/java/neon/editor/editors/AfflictionEditor.java
+++ b/src/main/java/neon/editor/editors/AfflictionEditor.java
@@ -1,134 +1,136 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor.editors;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.border.*;
-import neon.editor.Editor;
-import neon.editor.NeonFormat;
-import neon.editor.help.HelpLabels;
-import neon.magic.Effect;
-import neon.resources.RSpell;
-
-public class AfflictionEditor extends ObjectEditor implements ActionListener {
- private JTextField nameField;
- private JFormattedTextField sizeField;
- private JComboBox effectBox;
- private JTextArea scriptArea;
- private RSpell data;
-
- public AfflictionEditor(JFrame parent, RSpell data) {
- super(parent, "Affliction: " + data.id);
- this.data = data;
-
- JPanel props = new JPanel();
- GroupLayout layout = new GroupLayout(props);
- props.setLayout(layout);
- layout.setAutoCreateGaps(true);
- props.setBorder(new TitledBorder("Properties"));
-
- JLabel nameLabel = new JLabel("Name: ");
- JLabel effectLabel = new JLabel("Effect: ");
- JLabel sizeLabel = new JLabel("Magnitude: ");
- nameField = new JTextField(15);
- effectBox = new JComboBox(Effect.values());
- effectBox.addActionListener(this);
- sizeField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
- JLabel effectHelpLabel = HelpLabels.getEffectHelpLabel();
- JLabel sizeHelpLabel = HelpLabels.getSpellSizeHelpLabel();
- layout.setVerticalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(nameLabel)
- .addComponent(nameField)
- .addComponent(nameHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(effectLabel)
- .addComponent(effectBox)
- .addComponent(effectHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(sizeLabel)
- .addComponent(sizeField)
- .addComponent(sizeHelpLabel))
- .addGap(10));
- layout.setHorizontalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING)
- .addComponent(nameLabel)
- .addComponent(effectLabel)
- .addComponent(sizeLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(
- nameField,
- GroupLayout.PREFERRED_SIZE,
- GroupLayout.DEFAULT_SIZE,
- GroupLayout.PREFERRED_SIZE)
- .addComponent(effectBox)
- .addComponent(sizeField))
- .addGap(10)
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(nameHelpLabel)
- .addComponent(effectHelpLabel)
- .addComponent(sizeHelpLabel)));
- frame.add(props, BorderLayout.PAGE_START);
-
- scriptArea = new JTextArea(6, 0);
- scriptArea.setDisabledTextColor(Color.red);
- JScrollPane scriptScroller = new JScrollPane(scriptArea);
- scriptScroller.setBorder(new TitledBorder("Script"));
- frame.add(scriptScroller, BorderLayout.CENTER);
- }
-
- protected void load() {
- nameField.setText(data.name);
- effectBox.setSelectedItem(data.effect);
- sizeField.setValue(data.size);
- scriptArea.setText(data.script);
- scriptArea.setEditable(data.effect == Effect.SCRIPTED);
- }
-
- protected void save() {
- data.name = nameField.getText();
- data.size = Integer.parseInt(sizeField.getText());
- data.effect = effectBox.getItemAt(effectBox.getSelectedIndex());
- data.script = scriptArea.getText();
- data.setPath(Editor.getStore().getActive().get("id"));
- }
-
- public void actionPerformed(ActionEvent e) {
- scriptArea.setEditable(effectBox.getSelectedItem() == Effect.SCRIPTED);
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor.editors;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import neon.editor.DataStore;
+import neon.editor.NeonFormat;
+import neon.editor.help.HelpLabels;
+import neon.magic.Effect;
+import neon.resources.RSpell;
+
+public class AfflictionEditor extends ObjectEditor implements ActionListener {
+ private final JTextField nameField;
+ private final JFormattedTextField sizeField;
+ private final JComboBox effectBox;
+ private final JTextArea scriptArea;
+ private final RSpell data;
+ private final DataStore dataStore;
+
+ public AfflictionEditor(JFrame parent, RSpell data, DataStore dataStore) {
+ super(parent, "Affliction: " + data.id);
+ this.data = data;
+ this.dataStore = dataStore;
+
+ JPanel props = new JPanel();
+ GroupLayout layout = new GroupLayout(props);
+ props.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+ props.setBorder(new TitledBorder("Properties"));
+
+ JLabel nameLabel = new JLabel("Name: ");
+ JLabel effectLabel = new JLabel("Effect: ");
+ JLabel sizeLabel = new JLabel("Magnitude: ");
+ nameField = new JTextField(15);
+ effectBox = new JComboBox(Effect.values());
+ effectBox.addActionListener(this);
+ sizeField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
+ JLabel effectHelpLabel = HelpLabels.getEffectHelpLabel();
+ JLabel sizeHelpLabel = HelpLabels.getSpellSizeHelpLabel();
+ layout.setVerticalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(nameLabel)
+ .addComponent(nameField)
+ .addComponent(nameHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(effectLabel)
+ .addComponent(effectBox)
+ .addComponent(effectHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(sizeLabel)
+ .addComponent(sizeField)
+ .addComponent(sizeHelpLabel))
+ .addGap(10));
+ layout.setHorizontalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(nameLabel)
+ .addComponent(effectLabel)
+ .addComponent(sizeLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(
+ nameField,
+ GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE,
+ GroupLayout.PREFERRED_SIZE)
+ .addComponent(effectBox)
+ .addComponent(sizeField))
+ .addGap(10)
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(nameHelpLabel)
+ .addComponent(effectHelpLabel)
+ .addComponent(sizeHelpLabel)));
+ frame.add(props, BorderLayout.PAGE_START);
+
+ scriptArea = new JTextArea(6, 0);
+ scriptArea.setDisabledTextColor(Color.red);
+ JScrollPane scriptScroller = new JScrollPane(scriptArea);
+ scriptScroller.setBorder(new TitledBorder("Script"));
+ frame.add(scriptScroller, BorderLayout.CENTER);
+ }
+
+ protected void load() {
+ nameField.setText(data.name);
+ effectBox.setSelectedItem(data.effect);
+ sizeField.setValue(data.size);
+ scriptArea.setText(data.script);
+ scriptArea.setEditable(data.effect == Effect.SCRIPTED);
+ }
+
+ protected void save() {
+ data.name = nameField.getText();
+ data.size = Integer.parseInt(sizeField.getText());
+ data.effect = effectBox.getItemAt(effectBox.getSelectedIndex());
+ data.script = scriptArea.getText();
+ data.setPath(dataStore.getActive().get("id"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ scriptArea.setEditable(effectBox.getSelectedItem() == Effect.SCRIPTED);
+ }
+}
diff --git a/src/main/java/neon/editor/editors/AlchemyEditor.java b/src/main/java/neon/editor/editors/AlchemyEditor.java
index 805371d..f186e44 100644
--- a/src/main/java/neon/editor/editors/AlchemyEditor.java
+++ b/src/main/java/neon/editor/editors/AlchemyEditor.java
@@ -1,129 +1,131 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2013 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor.editors;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-import javax.swing.*;
-import javax.swing.border.TitledBorder;
-import neon.editor.Editor;
-import neon.editor.NeonFormat;
-import neon.editor.help.HelpLabels;
-import neon.resources.RItem;
-import neon.resources.RRecipe;
-
-@SuppressWarnings("serial")
-public class AlchemyEditor extends ObjectEditor implements MouseListener {
- private JList contentList;
- private DefaultListModel contentModel;
- private RRecipe recipe;
- private JFormattedTextField costField;
-
- public AlchemyEditor(JFrame parent, RRecipe recipe) {
- super(parent, "Recipe: " + recipe.id);
- this.recipe = recipe;
-
- contentModel = new DefaultListModel();
- contentList = new JList(contentModel);
- contentList.addMouseListener(this);
- JScrollPane textScroller = new JScrollPane(contentList);
- textScroller.setBorder(new TitledBorder("Ingredients"));
- frame.add(textScroller, BorderLayout.CENTER);
-
- JLabel costLabel = new JLabel("Cost: ");
- costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- costField.setColumns(10);
- JLabel costHelpLabel = HelpLabels.getAlchemyCostHelpLabel();
-
- JPanel costPanel = new JPanel();
- costPanel.setBorder(new TitledBorder("Properties"));
- costPanel.add(costLabel);
- costPanel.add(costField);
- costPanel.add(costHelpLabel);
- frame.add(costPanel, BorderLayout.PAGE_START);
- }
-
- protected void load() {
- contentModel.clear();
- costField.setValue(recipe.cost);
- for (String id : recipe.ingredients) {
- RItem ri = (RItem) Editor.resources.getResource(id);
- contentModel.addElement(ri);
- }
- }
-
- protected void save() {
- recipe.ingredients.clear();
- recipe.cost = (int) costField.getValue();
- for (Enumeration items = contentModel.elements(); items.hasMoreElements(); ) {
- recipe.ingredients.add(items.nextElement().id);
- }
- }
-
- public void mousePressed(MouseEvent e) {}
-
- public void mouseReleased(MouseEvent e) {}
-
- public void mouseEntered(MouseEvent e) {}
-
- public void mouseExited(MouseEvent e) {}
-
- public void mouseClicked(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON3) {
- JPopupMenu menu = new JPopupMenu();
- menu.add(new ClickAction("Add ingredient"));
- menu.add(new ClickAction("Remove ingredient"));
- menu.show(e.getComponent(), e.getX(), e.getY());
- contentList.setSelectedIndex(contentList.locationToIndex(new Point(e.getX(), e.getY())));
- }
- }
-
- public class ClickAction extends AbstractAction {
- public ClickAction(String name) {
- super(name);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("Add ingredient")) {
- Object[] items = Editor.resources.getResources(RItem.class).toArray();
- RItem ingredient =
- (RItem)
- JOptionPane.showInputDialog(
- frame,
- "Add ingredient:",
- "Add ingredient",
- JOptionPane.PLAIN_MESSAGE,
- null,
- items,
- 0);
- if (ingredient != null) {
- contentModel.addElement(ingredient);
- }
- } else if (e.getActionCommand().equals("Remove ingredient")) {
- try {
- if (contentList.getSelectedIndex() >= 0) {
- contentModel.remove(contentList.getSelectedIndex());
- }
- } catch (ArrayIndexOutOfBoundsException a) {
- }
- }
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2013 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor.editors;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.TitledBorder;
+import neon.editor.DataStore;
+import neon.editor.NeonFormat;
+import neon.editor.help.HelpLabels;
+import neon.resources.RItem;
+import neon.resources.RRecipe;
+
+@SuppressWarnings("serial")
+public class AlchemyEditor extends ObjectEditor implements MouseListener {
+ private final JList contentList;
+ private final DefaultListModel contentModel;
+ private final RRecipe recipe;
+ private final JFormattedTextField costField;
+ private final DataStore dataStore;
+
+ public AlchemyEditor(JFrame parent, RRecipe recipe, DataStore dataStore) {
+ super(parent, "Recipe: " + recipe.id);
+ this.recipe = recipe;
+ this.dataStore = dataStore;
+
+ contentModel = new DefaultListModel();
+ contentList = new JList(contentModel);
+ contentList.addMouseListener(this);
+ JScrollPane textScroller = new JScrollPane(contentList);
+ textScroller.setBorder(new TitledBorder("Ingredients"));
+ frame.add(textScroller, BorderLayout.CENTER);
+
+ JLabel costLabel = new JLabel("Cost: ");
+ costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ costField.setColumns(10);
+ JLabel costHelpLabel = HelpLabels.getAlchemyCostHelpLabel();
+
+ JPanel costPanel = new JPanel();
+ costPanel.setBorder(new TitledBorder("Properties"));
+ costPanel.add(costLabel);
+ costPanel.add(costField);
+ costPanel.add(costHelpLabel);
+ frame.add(costPanel, BorderLayout.PAGE_START);
+ }
+
+ protected void load() {
+ contentModel.clear();
+ costField.setValue(recipe.cost);
+ for (String id : recipe.ingredients) {
+ RItem ri = (RItem) dataStore.getResourceManager().getResource(id);
+ contentModel.addElement(ri);
+ }
+ }
+
+ protected void save() {
+ recipe.ingredients.clear();
+ recipe.cost = (int) costField.getValue();
+ for (Enumeration items = contentModel.elements(); items.hasMoreElements(); ) {
+ recipe.ingredients.add(items.nextElement().id);
+ }
+ }
+
+ public void mousePressed(MouseEvent e) {}
+
+ public void mouseReleased(MouseEvent e) {}
+
+ public void mouseEntered(MouseEvent e) {}
+
+ public void mouseExited(MouseEvent e) {}
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new ClickAction("Add ingredient"));
+ menu.add(new ClickAction("Remove ingredient"));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ contentList.setSelectedIndex(contentList.locationToIndex(new Point(e.getX(), e.getY())));
+ }
+ }
+
+ public class ClickAction extends AbstractAction {
+ public ClickAction(String name) {
+ super(name);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("Add ingredient")) {
+ Object[] items = dataStore.getResourceManager().getResources(RItem.class).toArray();
+ RItem ingredient =
+ (RItem)
+ JOptionPane.showInputDialog(
+ frame,
+ "Add ingredient:",
+ "Add ingredient",
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ items,
+ 0);
+ if (ingredient != null) {
+ contentModel.addElement(ingredient);
+ }
+ } else if (e.getActionCommand().equals("Remove ingredient")) {
+ try {
+ if (contentList.getSelectedIndex() >= 0) {
+ contentModel.remove(contentList.getSelectedIndex());
+ }
+ } catch (ArrayIndexOutOfBoundsException a) {
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/editors/ArmorEditor.java b/src/main/java/neon/editor/editors/ArmorEditor.java
index b6e21c3..c94b1d2 100644
--- a/src/main/java/neon/editor/editors/ArmorEditor.java
+++ b/src/main/java/neon/editor/editors/ArmorEditor.java
@@ -22,7 +22,9 @@
import java.util.Vector;
import javax.swing.*;
import javax.swing.border.TitledBorder;
-import neon.editor.*;
+import neon.editor.ColorCellRenderer;
+import neon.editor.DataStore;
+import neon.editor.NeonFormat;
import neon.editor.help.HelpLabels;
import neon.entities.property.Slot;
import neon.resources.RClothing;
@@ -30,17 +32,24 @@
import neon.util.ColorFactory;
public class ArmorEditor extends ObjectEditor {
- private JTextField nameField;
- private JFormattedTextField costField, weightField, charField, ratingField;
- private JComboBox colorBox, spellBox;
- private JComboBox slotBox;
- private JComboBox classBox;
- private RClothing data;
+ private final JTextField nameField;
+ private final JFormattedTextField costField;
+ private final JFormattedTextField weightField;
+ private final JFormattedTextField charField;
+ private final JFormattedTextField ratingField;
+ private final JComboBox colorBox;
+ private final JComboBox spellBox;
+ private final JComboBox slotBox;
+ private final JComboBox classBox;
+ private final RClothing data;
+ private final DataStore dataStore;
+ private final HelpLabels helpLabels;
- public ArmorEditor(JFrame parent, RClothing data) {
+ public ArmorEditor(JFrame parent, RClothing data, DataStore dataStore) {
super(parent, "Armor Editor: " + data.id);
this.data = data;
-
+ this.dataStore = dataStore;
+ this.helpLabels = new HelpLabels(dataStore);
JPanel itemProps = new JPanel();
GroupLayout layout = new GroupLayout(itemProps);
itemProps.setLayout(layout);
@@ -68,7 +77,7 @@ public ArmorEditor(JFrame parent, RClothing data) {
ratingField = new JFormattedTextField(NeonFormat.getIntegerInstance());
spellBox = new JComboBox(loadSpells());
JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
- JLabel costHelpLabel = HelpLabels.getCostHelpLabel();
+ JLabel costHelpLabel = helpLabels.getCostHelpLabel();
JLabel colorHelpLabel = HelpLabels.getColorHelpLabel();
JLabel charHelpLabel = HelpLabels.getCharHelpLabel();
JLabel weightHelpLabel = HelpLabels.getWeightHelpLabel();
@@ -208,13 +217,14 @@ protected void save() {
data.spell = spellBox.getSelectedItem().toString();
}
- data.setPath(Editor.getStore().getActive().get("id"));
+ data.setPath(dataStore.getActive().get("id"));
}
private Vector loadSpells() {
Vector spells = new Vector();
spells.add(null);
- for (RSpell.Enchantment spell : Editor.resources.getResources(RSpell.Enchantment.class)) {
+ for (RSpell.Enchantment spell :
+ dataStore.getResourceManager().getResources(RSpell.Enchantment.class)) {
if (spell.item.equals("clothing")) {
spells.add(spell.id);
}
diff --git a/src/main/java/neon/editor/editors/BookEditor.java b/src/main/java/neon/editor/editors/BookEditor.java
index 77c4049..c2983f6 100644
--- a/src/main/java/neon/editor/editors/BookEditor.java
+++ b/src/main/java/neon/editor/editors/BookEditor.java
@@ -1,166 +1,172 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor.editors;
-
-import java.awt.*;
-import javax.swing.*;
-import javax.swing.border.*;
-import neon.editor.ColorCellRenderer;
-import neon.editor.Editor;
-import neon.editor.NeonFormat;
-import neon.editor.help.HelpLabels;
-import neon.resources.RItem;
-import neon.util.ColorFactory;
-
-public class BookEditor extends ObjectEditor {
- private JTextField nameField, textField;
- private JFormattedTextField costField, weightField, charField;
- private JComboBox colorBox;
- private RItem.Text data;
-
- public BookEditor(JFrame parent, RItem.Text data) {
- super(parent, "Book Editor: " + data.id);
- this.data = data;
-
- JPanel itemProps = new JPanel();
- GroupLayout layout = new GroupLayout(itemProps);
- itemProps.setLayout(layout);
- layout.setAutoCreateGaps(true);
-
- JLabel nameLabel = new JLabel("Name: ");
- JLabel costLabel = new JLabel("Cost: ");
- JLabel colorLabel = new JLabel("Color: ");
- JLabel charLabel = new JLabel("Character: ");
- JLabel weightLabel = new JLabel("Weight: ");
- JLabel textLabel = new JLabel("Text: ");
- nameField = new JTextField(15);
- costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- colorBox = new JComboBox(ColorFactory.getColorNames());
- colorBox.setBackground(Color.black);
- colorBox.setRenderer(new ColorCellRenderer());
- colorBox.addActionListener(new ColorListener(colorBox));
- charField = new JFormattedTextField(getMaskFormatter("*", 'X'));
- weightField = new JFormattedTextField(NeonFormat.getFloatInstance());
- textField = new JTextField(15);
- JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
- JLabel costHelpLabel = HelpLabels.getCostHelpLabel();
- JLabel colorHelpLabel = HelpLabels.getColorHelpLabel();
- JLabel charHelpLabel = HelpLabels.getCharHelpLabel();
- JLabel weightHelpLabel = HelpLabels.getWeightHelpLabel();
- JLabel textHelpLabel = HelpLabels.getBookTextHelpLabel();
- layout.setVerticalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(nameLabel)
- .addComponent(nameField)
- .addComponent(nameHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(costLabel)
- .addComponent(costField)
- .addComponent(costHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(colorLabel)
- .addComponent(colorBox)
- .addComponent(colorHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(charLabel)
- .addComponent(charField)
- .addComponent(charHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(weightLabel)
- .addComponent(weightField)
- .addComponent(weightHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(textLabel)
- .addComponent(textField)
- .addComponent(textHelpLabel)));
- layout.setHorizontalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING)
- .addComponent(nameLabel)
- .addComponent(costLabel)
- .addComponent(colorLabel)
- .addComponent(charLabel)
- .addComponent(weightLabel)
- .addComponent(textLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(
- nameField,
- GroupLayout.PREFERRED_SIZE,
- GroupLayout.DEFAULT_SIZE,
- GroupLayout.PREFERRED_SIZE)
- .addComponent(costField)
- .addComponent(colorBox)
- .addComponent(charField)
- .addComponent(weightField)
- .addComponent(textField))
- .addGap(10)
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(nameHelpLabel)
- .addComponent(costHelpLabel)
- .addComponent(colorHelpLabel)
- .addComponent(charHelpLabel)
- .addComponent(weightHelpLabel)
- .addComponent(textHelpLabel)));
-
- JScrollPane propScroller = new JScrollPane(itemProps);
- propScroller.setBorder(new TitledBorder("Properties"));
- frame.add(propScroller, BorderLayout.CENTER);
- }
-
- protected void load() {
- nameField.setText(data.name);
- costField.setValue(data.cost);
- colorBox.setSelectedItem(data.color);
- charField.setValue(data.text);
- weightField.setValue(data.weight);
- textField.setText(data.content);
- }
-
- protected void save() {
- data.name = nameField.getText();
- data.cost = Integer.parseInt(costField.getText());
- data.color = colorBox.getSelectedItem().toString();
- data.text = charField.getText();
- data.weight = Float.parseFloat(weightField.getText());
- data.content = textField.getText();
-
- data.setPath(Editor.getStore().getActive().get("id"));
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor.editors;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import neon.editor.ColorCellRenderer;
+import neon.editor.DataStore;
+import neon.editor.NeonFormat;
+import neon.editor.help.HelpLabels;
+import neon.resources.RItem;
+import neon.util.ColorFactory;
+
+public class BookEditor extends ObjectEditor {
+ private final JTextField nameField;
+ private final JTextField textField;
+ private final JFormattedTextField costField;
+ private final JFormattedTextField weightField;
+ private final JFormattedTextField charField;
+ private final JComboBox colorBox;
+ private final RItem.Text data;
+ private final DataStore dataStore;
+ private final HelpLabels helpLabels;
+
+ public BookEditor(JFrame parent, RItem.Text data, DataStore dataStore) {
+ super(parent, "Book Editor: " + data.id);
+ this.data = data;
+ this.dataStore = dataStore;
+ helpLabels = new HelpLabels(dataStore);
+ JPanel itemProps = new JPanel();
+ GroupLayout layout = new GroupLayout(itemProps);
+ itemProps.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+
+ JLabel nameLabel = new JLabel("Name: ");
+ JLabel costLabel = new JLabel("Cost: ");
+ JLabel colorLabel = new JLabel("Color: ");
+ JLabel charLabel = new JLabel("Character: ");
+ JLabel weightLabel = new JLabel("Weight: ");
+ JLabel textLabel = new JLabel("Text: ");
+ nameField = new JTextField(15);
+ costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ colorBox = new JComboBox(ColorFactory.getColorNames());
+ colorBox.setBackground(Color.black);
+ colorBox.setRenderer(new ColorCellRenderer());
+ colorBox.addActionListener(new ColorListener(colorBox));
+ charField = new JFormattedTextField(getMaskFormatter("*", 'X'));
+ weightField = new JFormattedTextField(NeonFormat.getFloatInstance());
+ textField = new JTextField(15);
+ JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
+ JLabel costHelpLabel = helpLabels.getCostHelpLabel();
+ JLabel colorHelpLabel = HelpLabels.getColorHelpLabel();
+ JLabel charHelpLabel = HelpLabels.getCharHelpLabel();
+ JLabel weightHelpLabel = HelpLabels.getWeightHelpLabel();
+ JLabel textHelpLabel = HelpLabels.getBookTextHelpLabel();
+ layout.setVerticalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(nameLabel)
+ .addComponent(nameField)
+ .addComponent(nameHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(costLabel)
+ .addComponent(costField)
+ .addComponent(costHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(colorLabel)
+ .addComponent(colorBox)
+ .addComponent(colorHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(charLabel)
+ .addComponent(charField)
+ .addComponent(charHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(weightLabel)
+ .addComponent(weightField)
+ .addComponent(weightHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(textLabel)
+ .addComponent(textField)
+ .addComponent(textHelpLabel)));
+ layout.setHorizontalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(nameLabel)
+ .addComponent(costLabel)
+ .addComponent(colorLabel)
+ .addComponent(charLabel)
+ .addComponent(weightLabel)
+ .addComponent(textLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(
+ nameField,
+ GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE,
+ GroupLayout.PREFERRED_SIZE)
+ .addComponent(costField)
+ .addComponent(colorBox)
+ .addComponent(charField)
+ .addComponent(weightField)
+ .addComponent(textField))
+ .addGap(10)
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(nameHelpLabel)
+ .addComponent(costHelpLabel)
+ .addComponent(colorHelpLabel)
+ .addComponent(charHelpLabel)
+ .addComponent(weightHelpLabel)
+ .addComponent(textHelpLabel)));
+
+ JScrollPane propScroller = new JScrollPane(itemProps);
+ propScroller.setBorder(new TitledBorder("Properties"));
+ frame.add(propScroller, BorderLayout.CENTER);
+ }
+
+ protected void load() {
+ nameField.setText(data.name);
+ costField.setValue(data.cost);
+ colorBox.setSelectedItem(data.color);
+ charField.setValue(data.text);
+ weightField.setValue(data.weight);
+ textField.setText(data.content);
+ }
+
+ protected void save() {
+ data.name = nameField.getText();
+ data.cost = Integer.parseInt(costField.getText());
+ data.color = colorBox.getSelectedItem().toString();
+ data.text = charField.getText();
+ data.weight = Float.parseFloat(weightField.getText());
+ data.content = textField.getText();
+
+ data.setPath(dataStore.getActive().get("id"));
+ }
+}
diff --git a/src/main/java/neon/editor/editors/ClothingEditor.java b/src/main/java/neon/editor/editors/ClothingEditor.java
index 8be9aa6..1b2efe1 100644
--- a/src/main/java/neon/editor/editors/ClothingEditor.java
+++ b/src/main/java/neon/editor/editors/ClothingEditor.java
@@ -1,207 +1,216 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor.editors;
-
-import java.awt.*;
-import java.util.Vector;
-import javax.swing.*;
-import javax.swing.border.*;
-import neon.editor.*;
-import neon.editor.help.HelpLabels;
-import neon.entities.property.Slot;
-import neon.resources.RClothing;
-import neon.resources.RSpell;
-import neon.util.ColorFactory;
-
-public class ClothingEditor extends ObjectEditor {
- private JTextField nameField;
- private JFormattedTextField costField, weightField, charField;
- private JComboBox colorBox, spellBox;
- private JComboBox slotBox;
- private RClothing data;
-
- public ClothingEditor(JFrame parent, RClothing data) {
- super(parent, "Clothing Editor: " + data.id);
- this.data = data;
-
- JPanel itemProps = new JPanel();
- GroupLayout layout = new GroupLayout(itemProps);
- itemProps.setLayout(layout);
- layout.setAutoCreateGaps(true);
-
- JLabel nameLabel = new JLabel("Name: ");
- JLabel costLabel = new JLabel("Cost: ");
- JLabel colorLabel = new JLabel("Color: ");
- JLabel charLabel = new JLabel("Character: ");
- JLabel weightLabel = new JLabel("Weight: ");
- JLabel slotLabel = new JLabel("Slot: ");
- JLabel spellLabel = new JLabel("Enchantment: ");
- nameField = new JTextField(15);
- costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- colorBox = new JComboBox(ColorFactory.getColorNames());
- colorBox.setBackground(Color.black);
- colorBox.setRenderer(new ColorCellRenderer());
- colorBox.addActionListener(new ColorListener(colorBox));
- charField = new JFormattedTextField(getMaskFormatter("*", 'X'));
- weightField = new JFormattedTextField(NeonFormat.getFloatInstance());
- slotBox = new JComboBox(loadSlots());
- spellBox = new JComboBox(loadSpells());
- JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
- JLabel costHelpLabel = HelpLabels.getCostHelpLabel();
- JLabel colorHelpLabel = HelpLabels.getColorHelpLabel();
- JLabel charHelpLabel = HelpLabels.getCharHelpLabel();
- JLabel weightHelpLabel = HelpLabels.getWeightHelpLabel();
- JLabel slotHelpLabel = HelpLabels.getClothingSlotHelpLabel();
- JLabel spellHelpLabel = HelpLabels.getClothingEnchantmentHelpLabel();
- layout.setVerticalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(nameLabel)
- .addComponent(nameField)
- .addComponent(nameHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(costLabel)
- .addComponent(costField)
- .addComponent(costHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(colorLabel)
- .addComponent(colorBox)
- .addComponent(colorHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(charLabel)
- .addComponent(charField)
- .addComponent(charHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(weightLabel)
- .addComponent(weightField)
- .addComponent(weightHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(slotLabel)
- .addComponent(slotBox)
- .addComponent(slotHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(spellLabel)
- .addComponent(spellBox)
- .addComponent(spellHelpLabel)));
- layout.setHorizontalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING)
- .addComponent(nameLabel)
- .addComponent(costLabel)
- .addComponent(colorLabel)
- .addComponent(charLabel)
- .addComponent(weightLabel)
- .addComponent(slotLabel)
- .addComponent(spellLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(
- nameField,
- GroupLayout.PREFERRED_SIZE,
- GroupLayout.DEFAULT_SIZE,
- GroupLayout.PREFERRED_SIZE)
- .addComponent(costField)
- .addComponent(colorBox)
- .addComponent(charField)
- .addComponent(weightField)
- .addComponent(slotBox)
- .addComponent(spellBox))
- .addGap(10)
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(nameHelpLabel)
- .addComponent(costHelpLabel)
- .addComponent(colorHelpLabel)
- .addComponent(charHelpLabel)
- .addComponent(weightHelpLabel)
- .addComponent(slotHelpLabel)
- .addComponent(spellHelpLabel)));
- JScrollPane propScroller = new JScrollPane(itemProps);
- propScroller.setBorder(new TitledBorder("Properties"));
- frame.add(propScroller, BorderLayout.CENTER);
- }
-
- protected void load() {
- nameField.setText(data.name);
- costField.setValue(data.cost);
- colorBox.setSelectedItem(data.color);
- charField.setValue(data.text);
- weightField.setValue(data.weight);
- slotBox.setSelectedItem(data.slot);
- spellBox.setSelectedItem(data.spell);
- }
-
- protected void save() {
- data.name = nameField.getText();
- data.cost = Integer.parseInt(costField.getText());
- data.color = colorBox.getSelectedItem().toString();
- data.text = charField.getText();
- data.weight = Float.parseFloat(weightField.getText());
- data.slot = slotBox.getItemAt(slotBox.getSelectedIndex());
- if (spellBox.getSelectedItem() != null) {
- data.spell = spellBox.getSelectedItem().toString();
- }
-
- data.setPath(Editor.getStore().getActive().get("id"));
- }
-
- private Vector loadSpells() {
- Vector spells = new Vector();
- spells.add(null);
- for (RSpell.Enchantment spell : Editor.resources.getResources(RSpell.Enchantment.class)) {
- if (spell.item.equals("clothing")) {
- spells.add(spell.id);
- }
- }
- return spells;
- }
-
- private Vector loadSlots() {
- Vector slots = new Vector();
- slots.add(Slot.AMULET);
- slots.add(Slot.RING);
- slots.add(Slot.BELT);
- slots.add(Slot.SHIRT);
- slots.add(Slot.PANTS);
- slots.add(Slot.CLOAK);
- slots.add(Slot.SHOES);
- slots.add(Slot.GLOVES);
- return slots;
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor.editors;
+
+import java.awt.*;
+import java.util.Vector;
+import javax.swing.*;
+import javax.swing.border.*;
+import neon.editor.ColorCellRenderer;
+import neon.editor.DataStore;
+import neon.editor.NeonFormat;
+import neon.editor.help.HelpLabels;
+import neon.entities.property.Slot;
+import neon.resources.RClothing;
+import neon.resources.RSpell;
+import neon.util.ColorFactory;
+
+public class ClothingEditor extends ObjectEditor {
+ private final JTextField nameField;
+ private final JFormattedTextField costField;
+ private final JFormattedTextField weightField;
+ private final JFormattedTextField charField;
+ private final JComboBox colorBox;
+ private final JComboBox spellBox;
+ private final JComboBox slotBox;
+ private final RClothing data;
+ private final DataStore dataStore;
+ private final HelpLabels helpLabels;
+
+ public ClothingEditor(JFrame parent, RClothing data, DataStore dataStore) {
+ super(parent, "Clothing Editor: " + data.id);
+ this.data = data;
+ this.dataStore = dataStore;
+ helpLabels = new HelpLabels(dataStore);
+ JPanel itemProps = new JPanel();
+ GroupLayout layout = new GroupLayout(itemProps);
+ itemProps.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+
+ JLabel nameLabel = new JLabel("Name: ");
+ JLabel costLabel = new JLabel("Cost: ");
+ JLabel colorLabel = new JLabel("Color: ");
+ JLabel charLabel = new JLabel("Character: ");
+ JLabel weightLabel = new JLabel("Weight: ");
+ JLabel slotLabel = new JLabel("Slot: ");
+ JLabel spellLabel = new JLabel("Enchantment: ");
+ nameField = new JTextField(15);
+ costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ colorBox = new JComboBox(ColorFactory.getColorNames());
+ colorBox.setBackground(Color.black);
+ colorBox.setRenderer(new ColorCellRenderer());
+ colorBox.addActionListener(new ColorListener(colorBox));
+ charField = new JFormattedTextField(getMaskFormatter("*", 'X'));
+ weightField = new JFormattedTextField(NeonFormat.getFloatInstance());
+ slotBox = new JComboBox(loadSlots());
+ spellBox = new JComboBox(loadSpells());
+ JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
+ JLabel costHelpLabel = helpLabels.getCostHelpLabel();
+ JLabel colorHelpLabel = HelpLabels.getColorHelpLabel();
+ JLabel charHelpLabel = HelpLabels.getCharHelpLabel();
+ JLabel weightHelpLabel = HelpLabels.getWeightHelpLabel();
+ JLabel slotHelpLabel = HelpLabels.getClothingSlotHelpLabel();
+ JLabel spellHelpLabel = HelpLabels.getClothingEnchantmentHelpLabel();
+ layout.setVerticalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(nameLabel)
+ .addComponent(nameField)
+ .addComponent(nameHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(costLabel)
+ .addComponent(costField)
+ .addComponent(costHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(colorLabel)
+ .addComponent(colorBox)
+ .addComponent(colorHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(charLabel)
+ .addComponent(charField)
+ .addComponent(charHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(weightLabel)
+ .addComponent(weightField)
+ .addComponent(weightHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(slotLabel)
+ .addComponent(slotBox)
+ .addComponent(slotHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(spellLabel)
+ .addComponent(spellBox)
+ .addComponent(spellHelpLabel)));
+ layout.setHorizontalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(nameLabel)
+ .addComponent(costLabel)
+ .addComponent(colorLabel)
+ .addComponent(charLabel)
+ .addComponent(weightLabel)
+ .addComponent(slotLabel)
+ .addComponent(spellLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(
+ nameField,
+ GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE,
+ GroupLayout.PREFERRED_SIZE)
+ .addComponent(costField)
+ .addComponent(colorBox)
+ .addComponent(charField)
+ .addComponent(weightField)
+ .addComponent(slotBox)
+ .addComponent(spellBox))
+ .addGap(10)
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(nameHelpLabel)
+ .addComponent(costHelpLabel)
+ .addComponent(colorHelpLabel)
+ .addComponent(charHelpLabel)
+ .addComponent(weightHelpLabel)
+ .addComponent(slotHelpLabel)
+ .addComponent(spellHelpLabel)));
+ JScrollPane propScroller = new JScrollPane(itemProps);
+ propScroller.setBorder(new TitledBorder("Properties"));
+ frame.add(propScroller, BorderLayout.CENTER);
+ }
+
+ protected void load() {
+ nameField.setText(data.name);
+ costField.setValue(data.cost);
+ colorBox.setSelectedItem(data.color);
+ charField.setValue(data.text);
+ weightField.setValue(data.weight);
+ slotBox.setSelectedItem(data.slot);
+ spellBox.setSelectedItem(data.spell);
+ }
+
+ protected void save() {
+ data.name = nameField.getText();
+ data.cost = Integer.parseInt(costField.getText());
+ data.color = colorBox.getSelectedItem().toString();
+ data.text = charField.getText();
+ data.weight = Float.parseFloat(weightField.getText());
+ data.slot = slotBox.getItemAt(slotBox.getSelectedIndex());
+ if (spellBox.getSelectedItem() != null) {
+ data.spell = spellBox.getSelectedItem().toString();
+ }
+
+ data.setPath(dataStore.getActive().get("id"));
+ }
+
+ private Vector loadSpells() {
+ Vector spells = new Vector();
+ spells.add(null);
+ for (RSpell.Enchantment spell :
+ dataStore.getResourceManager().getResources(RSpell.Enchantment.class)) {
+ if (spell.item.equals("clothing")) {
+ spells.add(spell.id);
+ }
+ }
+ return spells;
+ }
+
+ private Vector loadSlots() {
+ Vector slots = new Vector();
+ slots.add(Slot.AMULET);
+ slots.add(Slot.RING);
+ slots.add(Slot.BELT);
+ slots.add(Slot.SHIRT);
+ slots.add(Slot.PANTS);
+ slots.add(Slot.CLOAK);
+ slots.add(Slot.SHOES);
+ slots.add(Slot.GLOVES);
+ return slots;
+ }
+}
diff --git a/src/main/java/neon/editor/editors/ContainerEditor.java b/src/main/java/neon/editor/editors/ContainerEditor.java
index d84027b..c2f08f5 100644
--- a/src/main/java/neon/editor/editors/ContainerEditor.java
+++ b/src/main/java/neon/editor/editors/ContainerEditor.java
@@ -1,182 +1,183 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor.editors;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.Enumeration;
-import javax.swing.*;
-import javax.swing.border.*;
-import neon.editor.ColorCellRenderer;
-import neon.editor.Editor;
-import neon.editor.help.HelpLabels;
-import neon.resources.RItem;
-import neon.util.ColorFactory;
-
-public class ContainerEditor extends ObjectEditor implements MouseListener {
- private JTextField nameField;
- private JComboBox colorBox;
- private JFormattedTextField charField;
- private RItem.Container data;
- private JList items;
- private DefaultListModel model;
-
- public ContainerEditor(JFrame parent, RItem.Container data) {
- super(parent, "Container Editor: " + data.id);
- frame.setPreferredSize(new Dimension(400, 200));
- this.data = data;
-
- JPanel itemProps = new JPanel();
- GroupLayout layout = new GroupLayout(itemProps);
- itemProps.setLayout(layout);
- layout.setAutoCreateGaps(true);
-
- JLabel nameLabel = new JLabel("Name: ");
- JLabel colorLabel = new JLabel("Color: ");
- JLabel charLabel = new JLabel("Character: ");
- nameField = new JTextField(15);
- colorBox = new JComboBox(ColorFactory.getColorNames());
- colorBox.setBackground(Color.black);
- colorBox.setRenderer(new ColorCellRenderer());
- colorBox.addActionListener(new ColorListener(colorBox));
- charField = new JFormattedTextField(getMaskFormatter("*", 'X'));
- JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
- JLabel colorHelpLabel = HelpLabels.getColorHelpLabel();
- JLabel charHelpLabel = HelpLabels.getCharHelpLabel();
- layout.setVerticalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(nameLabel)
- .addComponent(nameField)
- .addComponent(nameHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(colorLabel)
- .addComponent(colorBox)
- .addComponent(colorHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(charLabel)
- .addComponent(charField)
- .addComponent(charHelpLabel)));
- layout.setHorizontalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING)
- .addComponent(nameLabel)
- .addComponent(colorLabel)
- .addComponent(charLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(
- nameField,
- GroupLayout.PREFERRED_SIZE,
- GroupLayout.DEFAULT_SIZE,
- GroupLayout.PREFERRED_SIZE)
- .addComponent(colorBox)
- .addComponent(charField))
- .addGap(10)
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(nameHelpLabel)
- .addComponent(colorHelpLabel)
- .addComponent(charHelpLabel)));
-
- JScrollPane propScroller = new JScrollPane(itemProps);
- propScroller.setBorder(new TitledBorder("Properties"));
- frame.add(propScroller, BorderLayout.LINE_START);
-
- model = new DefaultListModel();
- items = new JList(model);
- items.addMouseListener(this);
- JScrollPane itemScroller = new JScrollPane(items);
- itemScroller.setBorder(new TitledBorder("Contents"));
- frame.add(itemScroller, BorderLayout.CENTER);
- }
-
- protected void save() {
- data.name = nameField.getText();
- data.color = colorBox.getSelectedItem().toString();
- data.text = charField.getText();
-
- data.contents.clear();
- for (Enumeration e = model.elements(); e.hasMoreElements(); ) {
- RItem ri = e.nextElement();
- data.contents.add(ri.id);
- }
-
- data.setPath(Editor.getStore().getActive().get("id"));
- }
-
- protected void load() {
- nameField.setText(data.name);
- colorBox.setSelectedItem(data.color);
- charField.setValue(data.text);
- for (String id : data.contents) {
- model.addElement((RItem) Editor.resources.getResource(id));
- }
- }
-
- public void mouseExited(MouseEvent e) {}
-
- public void mouseEntered(MouseEvent e) {}
-
- public void mouseReleased(MouseEvent e) {}
-
- public void mousePressed(MouseEvent e) {}
-
- public void mouseClicked(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON3) {
- JPopupMenu menu = new JPopupMenu();
- menu.add(new ItemListAction("Add item"));
- menu.add(new ItemListAction("Delete item"));
- menu.show(e.getComponent(), e.getX(), e.getY());
- items.setSelectedIndex(items.locationToIndex(e.getPoint()));
- }
- }
-
- @SuppressWarnings("serial")
- private class ItemListAction extends AbstractAction {
- public ItemListAction(String name) {
- super(name);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().equals("Add item")) {
- Object[] items = Editor.resources.getResources(RItem.class).toArray();
- RItem ri =
- (RItem)
- JOptionPane.showInputDialog(
- frame, "Add item:", "Add item", JOptionPane.PLAIN_MESSAGE, null, items, 0);
- model.addElement(ri);
- } else if (e.getActionCommand().equals("Delete item")) {
- model.remove(items.getSelectedIndex());
- }
- }
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor.editors;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.Enumeration;
+import javax.swing.*;
+import javax.swing.border.*;
+import neon.editor.ColorCellRenderer;
+import neon.editor.DataStore;
+import neon.editor.help.HelpLabels;
+import neon.resources.RItem;
+import neon.util.ColorFactory;
+
+public class ContainerEditor extends ObjectEditor implements MouseListener {
+ private final JTextField nameField;
+ private final JComboBox colorBox;
+ private final JFormattedTextField charField;
+ private final RItem.Container data;
+ private final DataStore dataStore;
+ private final JList items;
+ private final DefaultListModel model;
+
+ public ContainerEditor(JFrame parent, RItem.Container data, DataStore dataStore) {
+ super(parent, "Container Editor: " + data.id);
+ frame.setPreferredSize(new Dimension(400, 200));
+ this.data = data;
+ this.dataStore = dataStore;
+ JPanel itemProps = new JPanel();
+ GroupLayout layout = new GroupLayout(itemProps);
+ itemProps.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+
+ JLabel nameLabel = new JLabel("Name: ");
+ JLabel colorLabel = new JLabel("Color: ");
+ JLabel charLabel = new JLabel("Character: ");
+ nameField = new JTextField(15);
+ colorBox = new JComboBox(ColorFactory.getColorNames());
+ colorBox.setBackground(Color.black);
+ colorBox.setRenderer(new ColorCellRenderer());
+ colorBox.addActionListener(new ColorListener(colorBox));
+ charField = new JFormattedTextField(getMaskFormatter("*", 'X'));
+ JLabel nameHelpLabel = HelpLabels.getNameHelpLabel();
+ JLabel colorHelpLabel = HelpLabels.getColorHelpLabel();
+ JLabel charHelpLabel = HelpLabels.getCharHelpLabel();
+ layout.setVerticalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(nameLabel)
+ .addComponent(nameField)
+ .addComponent(nameHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(colorLabel)
+ .addComponent(colorBox)
+ .addComponent(colorHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(charLabel)
+ .addComponent(charField)
+ .addComponent(charHelpLabel)));
+ layout.setHorizontalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(nameLabel)
+ .addComponent(colorLabel)
+ .addComponent(charLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(
+ nameField,
+ GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE,
+ GroupLayout.PREFERRED_SIZE)
+ .addComponent(colorBox)
+ .addComponent(charField))
+ .addGap(10)
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(nameHelpLabel)
+ .addComponent(colorHelpLabel)
+ .addComponent(charHelpLabel)));
+
+ JScrollPane propScroller = new JScrollPane(itemProps);
+ propScroller.setBorder(new TitledBorder("Properties"));
+ frame.add(propScroller, BorderLayout.LINE_START);
+
+ model = new DefaultListModel();
+ items = new JList(model);
+ items.addMouseListener(this);
+ JScrollPane itemScroller = new JScrollPane(items);
+ itemScroller.setBorder(new TitledBorder("Contents"));
+ frame.add(itemScroller, BorderLayout.CENTER);
+ }
+
+ protected void save() {
+ data.name = nameField.getText();
+ data.color = colorBox.getSelectedItem().toString();
+ data.text = charField.getText();
+
+ data.contents.clear();
+ for (Enumeration e = model.elements(); e.hasMoreElements(); ) {
+ RItem ri = e.nextElement();
+ data.contents.add(ri.id);
+ }
+
+ data.setPath(dataStore.getActive().get("id"));
+ }
+
+ protected void load() {
+ nameField.setText(data.name);
+ colorBox.setSelectedItem(data.color);
+ charField.setValue(data.text);
+ for (String id : data.contents) {
+ model.addElement((RItem) dataStore.getResourceManager().getResource(id));
+ }
+ }
+
+ public void mouseExited(MouseEvent e) {}
+
+ public void mouseEntered(MouseEvent e) {}
+
+ public void mouseReleased(MouseEvent e) {}
+
+ public void mousePressed(MouseEvent e) {}
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new ItemListAction("Add item"));
+ menu.add(new ItemListAction("Delete item"));
+ menu.show(e.getComponent(), e.getX(), e.getY());
+ items.setSelectedIndex(items.locationToIndex(e.getPoint()));
+ }
+ }
+
+ @SuppressWarnings("serial")
+ private class ItemListAction extends AbstractAction {
+ public ItemListAction(String name) {
+ super(name);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("Add item")) {
+ Object[] items = dataStore.getResourceManager().getResources(RItem.class).toArray();
+ RItem ri =
+ (RItem)
+ JOptionPane.showInputDialog(
+ frame, "Add item:", "Add item", JOptionPane.PLAIN_MESSAGE, null, items, 0);
+ model.addElement(ri);
+ } else if (e.getActionCommand().equals("Delete item")) {
+ model.remove(items.getSelectedIndex());
+ }
+ }
+ }
+}
diff --git a/src/main/java/neon/editor/editors/CraftingEditor.java b/src/main/java/neon/editor/editors/CraftingEditor.java
index 900eb07..0b6bec2 100644
--- a/src/main/java/neon/editor/editors/CraftingEditor.java
+++ b/src/main/java/neon/editor/editors/CraftingEditor.java
@@ -1,121 +1,125 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.editor.editors;
-
-import java.awt.BorderLayout;
-import javax.swing.*;
-import javax.swing.border.TitledBorder;
-import neon.editor.Editor;
-import neon.editor.NeonFormat;
-import neon.editor.help.HelpLabels;
-import neon.resources.RCraft;
-import neon.resources.RItem;
-
-public class CraftingEditor extends ObjectEditor {
- private RCraft craft;
- private JComboBox rawBox;
- private JFormattedTextField costField, amountField;
-
- public CraftingEditor(JFrame parent, RCraft data) {
- super(parent, "Crafting Editor: " + data.id);
- craft = data;
-
- JPanel props = new JPanel();
- GroupLayout layout = new GroupLayout(props);
- props.setLayout(layout);
- layout.setAutoCreateGaps(true);
- props.setBorder(new TitledBorder("Properties"));
-
- JLabel rawLabel = new JLabel("Raw material: ");
- JLabel amountLabel = new JLabel("Amount: ");
- JLabel costLabel = new JLabel("Cost: ");
- rawBox = new JComboBox(Editor.resources.getResources(RItem.class));
- amountField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
- JLabel rawHelpLabel = HelpLabels.getRawHelpLabel();
- JLabel amountHelpLabel = HelpLabels.getAmountHelpLabel();
- JLabel costHelpLabel = HelpLabels.getCraftingCostHelpLabel();
- layout.setVerticalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(rawLabel)
- .addComponent(rawBox)
- .addComponent(rawHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(amountLabel)
- .addComponent(amountField)
- .addComponent(amountHelpLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.BASELINE)
- .addComponent(costLabel)
- .addComponent(costField)
- .addComponent(costHelpLabel)));
- layout.setHorizontalGroup(
- layout
- .createSequentialGroup()
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING)
- .addComponent(rawLabel)
- .addComponent(amountLabel)
- .addComponent(costLabel))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(
- rawBox,
- GroupLayout.PREFERRED_SIZE,
- GroupLayout.DEFAULT_SIZE,
- GroupLayout.PREFERRED_SIZE)
- .addComponent(amountField)
- .addComponent(costField))
- .addGroup(
- layout
- .createParallelGroup(GroupLayout.Alignment.LEADING, false)
- .addComponent(
- rawHelpLabel,
- GroupLayout.PREFERRED_SIZE,
- GroupLayout.DEFAULT_SIZE,
- GroupLayout.PREFERRED_SIZE)
- .addComponent(amountHelpLabel)
- .addComponent(costHelpLabel)));
-
- frame.add(props, BorderLayout.CENTER);
- }
-
- protected void load() {
- RItem raw = (RItem) Editor.resources.getResource(craft.raw);
- rawBox.setSelectedItem(raw);
- amountField.setValue(craft.amount);
- costField.setValue(craft.cost);
- }
-
- protected void save() {
- craft.raw = ((RItem) rawBox.getSelectedItem()).id;
- craft.cost = Integer.parseInt(costField.getText());
- craft.amount = Integer.parseInt(amountField.getText());
- craft.setPath(Editor.getStore().getActive().get("id"));
- }
-}
+/*
+ * Neon, a roguelike engine.
+ * Copyright (C) 2012 - Maarten Driesen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package neon.editor.editors;
+
+import java.awt.BorderLayout;
+import javax.swing.*;
+import javax.swing.border.TitledBorder;
+import neon.editor.DataStore;
+import neon.editor.NeonFormat;
+import neon.editor.help.HelpLabels;
+import neon.resources.RCraft;
+import neon.resources.RItem;
+
+public class CraftingEditor extends ObjectEditor {
+ private final RCraft craft;
+ private final JComboBox rawBox;
+ private final JFormattedTextField costField;
+ private final JFormattedTextField amountField;
+ private final DataStore dataStore;
+ private final HelpLabels helpLabels;
+
+ public CraftingEditor(JFrame parent, RCraft data, DataStore dataStore) {
+ super(parent, "Crafting Editor: " + data.id);
+ craft = data;
+ this.dataStore = dataStore;
+ helpLabels = new HelpLabels(dataStore);
+ JPanel props = new JPanel();
+ GroupLayout layout = new GroupLayout(props);
+ props.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+ props.setBorder(new TitledBorder("Properties"));
+
+ JLabel rawLabel = new JLabel("Raw material: ");
+ JLabel amountLabel = new JLabel("Amount: ");
+ JLabel costLabel = new JLabel("Cost: ");
+ rawBox = new JComboBox(dataStore.getResourceManager().getResources(RItem.class));
+ amountField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ costField = new JFormattedTextField(NeonFormat.getIntegerInstance());
+ JLabel rawHelpLabel = HelpLabels.getRawHelpLabel();
+ JLabel amountHelpLabel = HelpLabels.getAmountHelpLabel();
+ JLabel costHelpLabel = helpLabels.getCraftingCostHelpLabel();
+ layout.setVerticalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(rawLabel)
+ .addComponent(rawBox)
+ .addComponent(rawHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(amountLabel)
+ .addComponent(amountField)
+ .addComponent(amountHelpLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(costLabel)
+ .addComponent(costField)
+ .addComponent(costHelpLabel)));
+ layout.setHorizontalGroup(
+ layout
+ .createSequentialGroup()
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(rawLabel)
+ .addComponent(amountLabel)
+ .addComponent(costLabel))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(
+ rawBox,
+ GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE,
+ GroupLayout.PREFERRED_SIZE)
+ .addComponent(amountField)
+ .addComponent(costField))
+ .addGroup(
+ layout
+ .createParallelGroup(GroupLayout.Alignment.LEADING, false)
+ .addComponent(
+ rawHelpLabel,
+ GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE,
+ GroupLayout.PREFERRED_SIZE)
+ .addComponent(amountHelpLabel)
+ .addComponent(costHelpLabel)));
+
+ frame.add(props, BorderLayout.CENTER);
+ }
+
+ protected void load() {
+ RItem raw = (RItem) dataStore.getResourceManager().getResource(craft.raw);
+ rawBox.setSelectedItem(raw);
+ amountField.setValue(craft.amount);
+ costField.setValue(craft.cost);
+ }
+
+ protected void save() {
+ craft.raw = ((RItem) rawBox.getSelectedItem()).id;
+ craft.cost = Integer.parseInt(costField.getText());
+ craft.amount = Integer.parseInt(amountField.getText());
+ craft.setPath(dataStore.getActive().get("id"));
+ }
+}
diff --git a/src/main/java/neon/editor/editors/CreatureEditor.java b/src/main/java/neon/editor/editors/CreatureEditor.java
index 227886b..e3a0d53 100644
--- a/src/main/java/neon/editor/editors/CreatureEditor.java
+++ b/src/main/java/neon/editor/editors/CreatureEditor.java
@@ -23,7 +23,7 @@
import javax.swing.*;
import javax.swing.border.*;
import neon.editor.ColorCellRenderer;
-import neon.editor.Editor;
+import neon.editor.DataStore;
import neon.editor.NeonFormat;
import neon.editor.help.HelpLabels;
import neon.entities.property.Habitat;
@@ -32,23 +32,34 @@
import neon.resources.RCreature.Type;
public class CreatureEditor extends ObjectEditor {
- private RCreature data;
- private JTextField nameField;
- private JComboBox colorBox;
- private JFormattedTextField charField;
- private JFormattedTextField speedField, defenseField, rangeField, manaField;
- private JTextField hitField;
- private JComboBox typeBox;
- private JComboBox sizeBox;
- private JComboBox habitatBox;
- private JTextField attackField;
- private JFormattedTextField strField, conField, dexField, intField, wisField, chaField;
- private JComboBox aiTypeBox;
- private JSpinner aggressionSpinner, confidenceSpinner;
+ private final RCreature data;
+ private final JTextField nameField;
+ private final JComboBox colorBox;
+ private final JFormattedTextField charField;
+ private final JFormattedTextField speedField;
+ private final JFormattedTextField defenseField;
+ private final JFormattedTextField rangeField;
+ private final JFormattedTextField manaField;
+ private final JTextField hitField;
+ private final JComboBox typeBox;
+ private final JComboBox sizeBox;
+ private final JComboBox