- blight-lang: TextResolver + EN/DE Sprachpakete (TextReference i18n) - AnimSet: Clips + ActionMap in .animset.json zusammengeführt - EZ-Tree: Branch-Parameter-Fixes (length/radius/children/force nicht senden, twist Grad→Radiant, leaves.size ×5); Ordner-ComboBox mit Auto-Refresh - Logging beim Baum-Export in allen drei Generatoren (EZ-Tree, Blight, Palme) - Atmosphäre-Tools: Emitter, Licht, Wasser, Sound-/Musikbereiche, Spiel-Starten - AnimPreviewState, RetargetingSystem, AnimationLibrary (Animations-Editor) - Terrain-Transparenz-Fix, Schatten-Fix, ThirdPersonCamera-Fix - DayNightState, WeatherState, CloudsNode, JmeConsole - MapIO v6, neue blight-common Modell-Klassen (GameCharacter, NPC, Quests…) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
92 lines
3.9 KiB
Java
92 lines
3.9 KiB
Java
package de.blight.common.model;
|
|
|
|
import com.google.gson.*;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.*;
|
|
import java.util.stream.Stream;
|
|
|
|
/**
|
|
* Lädt und speichert {@link GameCharacter}-Instanzen als JSON.
|
|
* Dateiformat: <id>.character im Verzeichnis character/
|
|
*
|
|
* JSON-Struktur:
|
|
* {
|
|
* "type": "MAIN_CHARACTER" | "NPC",
|
|
* "characterId": "hero",
|
|
* "name": "Der Held",
|
|
* "modelPath": "Models/hero.j3o",
|
|
* "animSetPath": "animations/sets/hero.j3o",
|
|
* ... (subclass fields via Gson)
|
|
* }
|
|
* Die Aktions-Zuweisung (IDLE → Clip-Name usw.) ist im AnimSet gespeichert
|
|
* (animSetPath.replaceAll(".j3o", "") + ".animset.json").
|
|
*/
|
|
public final class CharacterIO {
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(CharacterIO.class);
|
|
private static final String EXTENSION = ".character";
|
|
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
|
|
|
private CharacterIO() {}
|
|
|
|
// ── Save ──────────────────────────────────────────────────────────────────
|
|
|
|
public static void save(GameCharacter character, Path directory) throws IOException {
|
|
String id = character.getCharacterId();
|
|
if (id == null || id.isBlank()) throw new IllegalArgumentException("characterId must be set");
|
|
Files.createDirectories(directory);
|
|
|
|
JsonObject obj = (JsonObject) GSON.toJsonTree(character);
|
|
obj.addProperty("type", character instanceof MainCharacter ? "MAIN_CHARACTER" : "NPC");
|
|
Files.writeString(directory.resolve(id + EXTENSION),
|
|
GSON.toJson(obj), StandardCharsets.UTF_8);
|
|
log.debug("[CharacterIO] Gespeichert: {}", id);
|
|
}
|
|
|
|
// ── Load ──────────────────────────────────────────────────────────────────
|
|
|
|
public static GameCharacter load(Path file) throws IOException {
|
|
String json = Files.readString(file, StandardCharsets.UTF_8);
|
|
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
|
|
String type = obj.has("type") ? obj.get("type").getAsString() : "NPC";
|
|
GameCharacter c = "MAIN_CHARACTER".equals(type)
|
|
? GSON.fromJson(obj, MainCharacter.class)
|
|
: GSON.fromJson(obj, NPC.class);
|
|
log.debug("[CharacterIO] Geladen: {}", file.getFileName());
|
|
return c;
|
|
}
|
|
|
|
/** Lädt alle .character-Dateien aus dem angegebenen Verzeichnis. */
|
|
public static List<GameCharacter> loadAll(Path directory) {
|
|
if (!Files.isDirectory(directory)) return List.of();
|
|
List<GameCharacter> result = new ArrayList<>();
|
|
try (Stream<Path> walk = Files.list(directory)) {
|
|
walk.filter(p -> p.toString().endsWith(EXTENSION))
|
|
.sorted()
|
|
.forEach(p -> {
|
|
try { result.add(load(p)); } catch (IOException e) {
|
|
log.warn("[CharacterIO] Fehler beim Laden von {}: {}", p, e.getMessage());
|
|
}
|
|
});
|
|
} catch (IOException e) {
|
|
log.warn("[CharacterIO] Fehler beim Scannen von {}: {}", directory, e.getMessage());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/** Gibt true zurück wenn bereits eine MainCharacter-Datei im Verzeichnis existiert (außer der mit excludeId). */
|
|
public static boolean mainCharacterExists(Path directory, String excludeId) {
|
|
for (GameCharacter c : loadAll(directory)) {
|
|
if (c instanceof MainCharacter
|
|
&& !Objects.equals(c.getCharacterId(), excludeId)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|