Einige Bug Fixes
This commit is contained in:
375
blight-assets/src/main/resources/character/bjoernson.character
Normal file
375
blight-assets/src/main/resources/character/bjoernson.character
Normal file
@@ -0,0 +1,375 @@
|
||||
{
|
||||
"status": "NEUTRAL",
|
||||
"trader": false,
|
||||
"dialogOptions": {
|
||||
"begruessung": {
|
||||
"id": "begruessung",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.begruessung.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.begruessung.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"wer_bist_du": {
|
||||
"id": "wer_bist_du",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.wer_bist_du.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.wer_bist_du.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"aus_dem_norden": {
|
||||
"id": "aus_dem_norden",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.aus_dem_norden.textmainchar"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.aus_dem_norden.textnpc" },
|
||||
"audio": { "key": "dialog.bjoernson.aus_dem_norden.textnpc" }
|
||||
},
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.aus_dem_norden.textnpc.1" },
|
||||
"audio": { "key": "dialog.bjoernson.aus_dem_norden.textnpc.1" }
|
||||
}
|
||||
],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"bier_angebot": {
|
||||
"id": "bier_angebot",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.bier_angebot.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.bier_angebot.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"abgemacht_bier": {
|
||||
"id": "abgemacht_bier",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.abgemacht_bier.textmainchar"
|
||||
},
|
||||
"recievesQuest": {
|
||||
"questId": "kuehles_bier"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"bier_geliefert": {
|
||||
"id": "bier_geliefert",
|
||||
"requiresChapter": 0,
|
||||
"requiresQuestOpen": {
|
||||
"questId": "kuehles_bier"
|
||||
},
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.bier_geliefert.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.bier_geliefert.textnpc"
|
||||
},
|
||||
"fulfillsQuest": {
|
||||
"questId": "kuehles_bier"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"schwert_annehmen": {
|
||||
"id": "schwert_annehmen",
|
||||
"requiresChapter": 1,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.schwert_annehmen.textmainchar"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.schwert_annehmen.textnpc" },
|
||||
"audio": { "key": "dialog.bjoernson.schwert_annehmen.textnpc" }
|
||||
},
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.schwert_annehmen.textnpc.1" },
|
||||
"audio": { "key": "dialog.bjoernson.schwert_annehmen.textnpc.1" }
|
||||
},
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.schwert_annehmen.textnpc.2" },
|
||||
"audio": { "key": "dialog.bjoernson.schwert_annehmen.textnpc.2" }
|
||||
},
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.schwert_annehmen.textnpc.3" },
|
||||
"audio": { "key": "dialog.bjoernson.schwert_annehmen.textnpc.3" }
|
||||
}
|
||||
],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"ich_kuemmere_mich": {
|
||||
"id": "ich_kuemmere_mich",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.ich_kuemmere_mich.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.ich_kuemmere_mich.textnpc"
|
||||
},
|
||||
"recievesQuest": {
|
||||
"questId": "blight_wuehler"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"erz_quest": {
|
||||
"id": "erz_quest",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.erz_quest.textmainchar"
|
||||
},
|
||||
"recievesQuest": {
|
||||
"questId": "magnetiterz"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"wie_schuerfern": {
|
||||
"id": "wie_schuerfern",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.wie_schuerfern.textmainchar"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.wie_schuerfern.textnpc" },
|
||||
"audio": { "key": "dialog.bjoernson.wie_schuerfern.textnpc" }
|
||||
},
|
||||
{
|
||||
"text": { "id": "dialog.bjoernson.wie_schuerfern.textnpc.1" },
|
||||
"audio": { "key": "dialog.bjoernson.wie_schuerfern.textnpc.1" }
|
||||
}
|
||||
],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"danke_bjoernson": {
|
||||
"id": "danke_bjoernson",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.danke_bjoernson.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.danke_bjoernson.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"quests_erledigt": {
|
||||
"id": "quests_erledigt",
|
||||
"requiresChapter": 0,
|
||||
"requiresQuestComplete": {
|
||||
"questId": "blight_wuehler"
|
||||
},
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.quests_erledigt.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.quests_erledigt.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"ich_bin_zaeh": {
|
||||
"id": "ich_bin_zaeh",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.ich_bin_zaeh.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.ich_bin_zaeh.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"wie_stehts_abmachung": {
|
||||
"id": "wie_stehts_abmachung",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.wie_stehts_abmachung.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.wie_stehts_abmachung.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"nur_die_waffe": {
|
||||
"id": "nur_die_waffe",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.nur_die_waffe.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.nur_die_waffe.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": false,
|
||||
"onChosenTriggers": []
|
||||
},
|
||||
"selber_lernen": {
|
||||
"id": "selber_lernen",
|
||||
"requiresChapter": 0,
|
||||
"textHero": {
|
||||
"id": "dialog.bjoernson.selber_lernen.textmainchar"
|
||||
},
|
||||
"textNpc": {
|
||||
"id": "dialog.bjoernson.selber_lernen.textnpc"
|
||||
},
|
||||
"heroSteps": [],
|
||||
"npcSteps": [],
|
||||
"abortsQuests": [],
|
||||
"enablesTrade": true,
|
||||
"onChosenTriggers": []
|
||||
}
|
||||
},
|
||||
"dialogRoots": [
|
||||
"begruessung",
|
||||
"bier_geliefert",
|
||||
"schwert_annehmen",
|
||||
"quests_erledigt"
|
||||
],
|
||||
"dialogNext": {
|
||||
"begruessung": [
|
||||
"wer_bist_du",
|
||||
"bier_angebot"
|
||||
],
|
||||
"wer_bist_du": [
|
||||
"aus_dem_norden",
|
||||
"bier_angebot"
|
||||
],
|
||||
"aus_dem_norden": [
|
||||
"bier_angebot"
|
||||
],
|
||||
"bier_angebot": [
|
||||
"abgemacht_bier"
|
||||
],
|
||||
"schwert_annehmen": [
|
||||
"ich_kuemmere_mich"
|
||||
],
|
||||
"ich_kuemmere_mich": [
|
||||
"erz_quest",
|
||||
"wie_schuerfern",
|
||||
"danke_bjoernson"
|
||||
],
|
||||
"erz_quest": [
|
||||
"wie_schuerfern",
|
||||
"danke_bjoernson"
|
||||
],
|
||||
"quests_erledigt": [
|
||||
"ich_bin_zaeh"
|
||||
],
|
||||
"ich_bin_zaeh": [
|
||||
"wie_stehts_abmachung"
|
||||
],
|
||||
"wie_stehts_abmachung": [
|
||||
"nur_die_waffe",
|
||||
"selber_lernen"
|
||||
]
|
||||
},
|
||||
"dialogDisables": {
|
||||
"abgemacht_bier": [
|
||||
"begruessung",
|
||||
"wer_bist_du",
|
||||
"aus_dem_norden",
|
||||
"bier_angebot"
|
||||
],
|
||||
"nur_die_waffe": [
|
||||
"selber_lernen",
|
||||
"quests_erledigt",
|
||||
"ich_bin_zaeh",
|
||||
"wie_stehts_abmachung"
|
||||
],
|
||||
"selber_lernen": [
|
||||
"nur_die_waffe",
|
||||
"quests_erledigt",
|
||||
"ich_bin_zaeh",
|
||||
"wie_stehts_abmachung"
|
||||
]
|
||||
},
|
||||
"defaultMessages": {},
|
||||
"routines": [
|
||||
{
|
||||
"name": "chapter0",
|
||||
"blocks": [
|
||||
{
|
||||
"startHour": 0,
|
||||
"endHour": 24,
|
||||
"activity": {
|
||||
"type": "STAND",
|
||||
"position": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"characterId": "bjoernson",
|
||||
"name": {
|
||||
"id": "bjoernson.name"
|
||||
},
|
||||
"modelPath": "Models/Chars/bjoernson.j3o",
|
||||
"animSetPath": "human",
|
||||
"type": "NPC"
|
||||
}
|
||||
14
blight-assets/src/main/resources/quests/blight_wuehler.quest
Normal file
14
blight-assets/src/main/resources/quests/blight_wuehler.quest
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"xp": 150,
|
||||
"questId": "blight_wuehler",
|
||||
"text": {
|
||||
"id": "quest.blight_wuehler.name"
|
||||
},
|
||||
"description": {
|
||||
"id": "quest.blight_wuehler.description"
|
||||
},
|
||||
"successText": {
|
||||
"id": "quest.blight_wuehler.success"
|
||||
},
|
||||
"type": "INTERACT"
|
||||
}
|
||||
15
blight-assets/src/main/resources/quests/kuehles_bier.quest
Normal file
15
blight-assets/src/main/resources/quests/kuehles_bier.quest
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"count": 1,
|
||||
"xp": 75,
|
||||
"questId": "kuehles_bier",
|
||||
"text": {
|
||||
"id": "quest.kuehles_bier.name"
|
||||
},
|
||||
"description": {
|
||||
"id": "quest.kuehles_bier.description"
|
||||
},
|
||||
"successText": {
|
||||
"id": "quest.kuehles_bier.success"
|
||||
},
|
||||
"type": "ITEM"
|
||||
}
|
||||
15
blight-assets/src/main/resources/quests/magnetiterz.quest
Normal file
15
blight-assets/src/main/resources/quests/magnetiterz.quest
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"count": 5,
|
||||
"xp": 100,
|
||||
"questId": "magnetiterz",
|
||||
"text": {
|
||||
"id": "quest.magnetiterz.name"
|
||||
},
|
||||
"description": {
|
||||
"id": "quest.magnetiterz.description"
|
||||
},
|
||||
"successText": {
|
||||
"id": "quest.magnetiterz.success"
|
||||
},
|
||||
"type": "ITEM"
|
||||
}
|
||||
@@ -282,6 +282,9 @@ public class EditorApp extends Application {
|
||||
private VBox zonenDynamicContent;
|
||||
private Runnable zonenKindStyleUpdater;
|
||||
|
||||
// Auswahl-Werkzeug – dynamischer Bereich für selektierte Items
|
||||
private VBox auswahlDynamicContent;
|
||||
|
||||
// Spiel-Starten-Werkzeug-Zustand
|
||||
private TextField spawnXField;
|
||||
private TextField spawnZField;
|
||||
@@ -461,7 +464,7 @@ public class EditorApp extends Application {
|
||||
centerStack = new StackPane(worldViewport);
|
||||
setupAssetOverlay();
|
||||
root.setCenter(centerStack);
|
||||
root.setRight(toolPanel);
|
||||
root.setRight(buildAuswahlPanel());
|
||||
worldBottomBar = buildBottomBox();
|
||||
root.setBottom(worldBottomBar);
|
||||
|
||||
@@ -752,6 +755,18 @@ public class EditorApp extends Application {
|
||||
updateLocationZonePanel(input.selectedLocationZoneInfo);
|
||||
}
|
||||
|
||||
if (input.auswahlZoneSelected) {
|
||||
input.auswahlZoneSelected = false;
|
||||
input.activeLayer = SharedInput.LAYER_ZONEN;
|
||||
if (zonenBtn != null) zonenBtn.setSelected(true);
|
||||
root.setRight(buildZonenPanel());
|
||||
}
|
||||
|
||||
if (input.itemSelectionChanged) {
|
||||
input.itemSelectionChanged = false;
|
||||
updateAuswahlItemPanel(input.selectedItemInfo);
|
||||
}
|
||||
|
||||
if (input.zonenSelectionChanged) {
|
||||
input.zonenSelectionChanged = false;
|
||||
updateZonenPanel(input.zonenSelectedKind);
|
||||
@@ -969,7 +984,11 @@ public class EditorApp extends Application {
|
||||
} else {
|
||||
setCenterView(worldViewport);
|
||||
}
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
root.setRight(buildAuswahlPanel());
|
||||
} else {
|
||||
root.setRight(toolPanel);
|
||||
}
|
||||
root.setBottom(worldBottomBar);
|
||||
if (wasObjEditor && (input.activeLayer == SharedInput.LAYER_OBJECTS
|
||||
|| input.activeLayer == SharedInput.LAYER_OBJECTS_EDIT)) {
|
||||
@@ -1590,9 +1609,11 @@ public class EditorApp extends Application {
|
||||
zonenBtn.setToggleGroup(layerGroup);
|
||||
playToolBtn.setToggleGroup(layerGroup);
|
||||
auswahlBtn.setSelected(true);
|
||||
input.activeLayer = SharedInput.LAYER_AUSWAHL;
|
||||
|
||||
auswahlBtn.setOnAction(e -> {
|
||||
input.activeLayer = SharedInput.LAYER_AUSWAHL;
|
||||
input.deselectAllOnAuswahl = true;
|
||||
root.setRight(buildAuswahlPanel());
|
||||
});
|
||||
baseBtn.setOnAction(e -> {
|
||||
@@ -1871,7 +1892,8 @@ public class EditorApp extends Application {
|
||||
|
||||
private boolean isObjectMode() {
|
||||
return input.activeLayer == SharedInput.LAYER_OBJECTS
|
||||
|| input.activeLayer == SharedInput.LAYER_OBJECTS_EDIT;
|
||||
|| input.activeLayer == SharedInput.LAYER_OBJECTS_EDIT
|
||||
|| input.activeLayer == SharedInput.LAYER_AUSWAHL;
|
||||
}
|
||||
|
||||
private static Label styledHint(String text) {
|
||||
@@ -3584,7 +3606,7 @@ public class EditorApp extends Application {
|
||||
title.setStyle("-fx-font-weight: bold; -fx-font-size: 13; -fx-text-fill: #111;");
|
||||
panel.getChildren().addAll(title, new Separator());
|
||||
panel.getChildren().addAll(
|
||||
styledHint("L-Klick: Objekt auswählen"),
|
||||
styledHint("L-Klick: Element auswählen"),
|
||||
styledHint(" → wechselt automatisch zum"),
|
||||
styledHint(" passenden Werkzeug"),
|
||||
new Separator(),
|
||||
@@ -3593,10 +3615,37 @@ public class EditorApp extends Application {
|
||||
styledHint(" * Lichter / Emitter"),
|
||||
styledHint(" ~ Wasser / Wasserfall"),
|
||||
styledHint(" -- Wege / Knoten"),
|
||||
styledHint(" [ ] Zonen"));
|
||||
styledHint(" [ ] Zonen"),
|
||||
styledHint(" o Spawnpunkte (Items)"),
|
||||
new Separator());
|
||||
auswahlDynamicContent = new VBox(6);
|
||||
Label noSel = new Label("Kein Element ausgewählt");
|
||||
noSel.setStyle("-fx-text-fill: #888;");
|
||||
auswahlDynamicContent.getChildren().add(noSel);
|
||||
panel.getChildren().add(auswahlDynamicContent);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void updateAuswahlItemPanel(String info) {
|
||||
if (auswahlDynamicContent == null) return;
|
||||
auswahlDynamicContent.getChildren().clear();
|
||||
if (info == null) {
|
||||
Label noSel = new Label("Kein Element ausgewählt");
|
||||
noSel.setStyle("-fx-text-fill: #888;");
|
||||
auswahlDynamicContent.getChildren().add(noSel);
|
||||
return;
|
||||
}
|
||||
String[] parts = info.split("\\|");
|
||||
String itemId = parts.length > 1 ? parts[1] : "?";
|
||||
Label idLabel = new Label("Item: " + itemId);
|
||||
idLabel.setStyle("-fx-font-weight: bold;");
|
||||
Button deleteBtn = new Button("Löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #c0392b; -fx-text-fill: white;");
|
||||
deleteBtn.setOnAction(e -> input.deleteSelectedItemRequested = true);
|
||||
auswahlDynamicContent.getChildren().addAll(idLabel, deleteBtn);
|
||||
}
|
||||
|
||||
private VBox buildAtmospherePanel() {
|
||||
VBox panel = new VBox(10);
|
||||
panel.setPadding(new Insets(10));
|
||||
@@ -12022,6 +12071,7 @@ public class EditorApp extends Application {
|
||||
private void switchToContentEditor() {
|
||||
currentTool = "content";
|
||||
if (jmeApp != null) jmeApp.setRenderThrottled(true);
|
||||
root.setTop(topBar);
|
||||
topBar.getChildren().set(1, buildSingleWindowContentToolBar());
|
||||
root.setLeft(buildContentTreePanel());
|
||||
root.setBottom(null);
|
||||
|
||||
@@ -1114,6 +1114,18 @@ public class SharedInput {
|
||||
/** activeLayer==33 → Auswahl-Modus: kein Terrain-Editing, Klick selektiert Objekte */
|
||||
public static final int LAYER_AUSWAHL = 33;
|
||||
|
||||
/** JavaFX → JME3: Alle Selektionen aufheben wenn in Auswahl-Modus gewechselt wird. */
|
||||
public volatile boolean deselectAllOnAuswahl = false;
|
||||
/** JME3 → JavaFX: Zone wurde im Auswahl-Modus selektiert → Zonen-Werkzeug aktivieren. */
|
||||
public volatile boolean auswahlZoneSelected = false;
|
||||
/** JME3 → JavaFX: Item-Spawn im Auswahl-Modus selektiert. Format "idx|itemId|x|y|z" oder null. */
|
||||
public volatile String selectedItemInfo = null;
|
||||
public volatile boolean itemSelectionChanged = false;
|
||||
/** JavaFX → JME3: Selektierten Item-Spawn löschen. */
|
||||
public volatile boolean deleteSelectedItemRequested = false;
|
||||
/** JME3-intern: Auswahl-Klick für ItemPlacementState (kein Objekt/keine Zone getroffen). */
|
||||
public final ConcurrentLinkedQueue<ObjectClick> auswahlClickQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
// ── Voxel-Textur-Malen ────────────────────────────────────────────────────
|
||||
/** Parallel-Queue zu textureEditQueue – wird von submitEdit(layer=4) mitbefüllt,
|
||||
* damit VoxelEditorState unabhängig vom Terrain seine Splatmap beschreiben kann. */
|
||||
|
||||
@@ -80,7 +80,8 @@ public class AreaState extends BaseAppState {
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
if (input.activeLayer != SharedInput.LAYER_AREAS && input.activeLayer != SharedInput.LAYER_ZONEN) {
|
||||
if (input.activeLayer != SharedInput.LAYER_AREAS && input.activeLayer != SharedInput.LAYER_ZONEN
|
||||
&& input.activeLayer != SharedInput.LAYER_AUSWAHL) {
|
||||
if (placing) cancelPoly();
|
||||
return;
|
||||
}
|
||||
@@ -172,6 +173,7 @@ public class AreaState extends BaseAppState {
|
||||
if (lzs != null && lzs.findZoneAt(hitX, hitZ) >= 0) return;
|
||||
}
|
||||
deselect();
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) return;
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN && !"area".equals(input.zonenNewKind)) return;
|
||||
placing = true;
|
||||
currX.clear();
|
||||
@@ -313,10 +315,13 @@ public class AreaState extends BaseAppState {
|
||||
String triggersJson = TriggerIO.serializeList(a.triggers());
|
||||
input.selectedAreaInfo = idx + "|" + a.areaId() + "|" + triggersJson;
|
||||
input.areaSelectionChanged = true;
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN) {
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN || input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
input.zonenSelectedKind = "area";
|
||||
input.zonenSelectionChanged = true;
|
||||
}
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
input.auswahlZoneSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Add / Remove / Apply ──────────────────────────────────────────────────
|
||||
|
||||
@@ -46,6 +46,8 @@ public class ItemPlacementState extends BaseAppState {
|
||||
private final Map<String, Item> itemDefs = new HashMap<>();
|
||||
private Node itemRoot;
|
||||
private Node previewNode;
|
||||
private int selectedItemIdx = -1;
|
||||
private Node selectedItemNode = null;
|
||||
|
||||
public ItemPlacementState(SharedInput input) {
|
||||
this.input = input;
|
||||
@@ -119,8 +121,15 @@ public class ItemPlacementState extends BaseAppState {
|
||||
public void update(float tpf) {
|
||||
if (input.reloadPlacedItems) {
|
||||
input.reloadPlacedItems = false;
|
||||
deselectItem();
|
||||
reloadFromDisk();
|
||||
}
|
||||
|
||||
// Highlight entfernen sobald wir nicht mehr im Auswahl-Modus sind
|
||||
if (selectedItemIdx >= 0 && input.activeLayer != SharedInput.LAYER_AUSWAHL) {
|
||||
deselectItem();
|
||||
}
|
||||
|
||||
updatePreview();
|
||||
|
||||
SharedInput.ObjectClick click;
|
||||
@@ -132,6 +141,19 @@ public class ItemPlacementState extends BaseAppState {
|
||||
handlePlace(click);
|
||||
}
|
||||
}
|
||||
|
||||
SharedInput.ObjectClick auswahlClick;
|
||||
while ((auswahlClick = input.auswahlClickQueue.poll()) != null) {
|
||||
handleAuswahlClick(auswahlClick);
|
||||
}
|
||||
|
||||
if (input.deleteSelectedItemRequested) {
|
||||
input.deleteSelectedItemRequested = false;
|
||||
if (selectedItemIdx >= 0) {
|
||||
removeItem(selectedItemIdx);
|
||||
deselectItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePreview() {
|
||||
@@ -183,6 +205,68 @@ public class ItemPlacementState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAuswahlClick(SharedInput.ObjectClick click) {
|
||||
float jmeX = click.screenX() * (float) input.viewportScaleX;
|
||||
float jmeY = cam.getHeight() - click.screenY() * (float) input.viewportScaleY;
|
||||
Ray ray = screenToRay(jmeX, jmeY);
|
||||
CollisionResults hits = new CollisionResults();
|
||||
itemRoot.collideWith(ray, hits);
|
||||
if (hits.size() > 0) {
|
||||
Spatial geo = hits.getClosestCollision().getGeometry();
|
||||
Node parent = geo.getParent();
|
||||
while (parent != null && !nodes.contains(parent)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
if (parent != null) {
|
||||
selectItem(nodes.indexOf(parent));
|
||||
return;
|
||||
}
|
||||
}
|
||||
deselectItem();
|
||||
}
|
||||
|
||||
private void selectItem(int idx) {
|
||||
deselectItem();
|
||||
selectedItemIdx = idx;
|
||||
selectedItemNode = nodes.get(idx);
|
||||
Geometry highlight = new Geometry("item_sel",
|
||||
new com.jme3.scene.shape.Box(0.28f, 0.28f, 0.28f));
|
||||
Material mat = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
mat.setColor("Color", new com.jme3.math.ColorRGBA(1f, 0.6f, 0.1f, 1f));
|
||||
mat.getAdditionalRenderState().setWireframe(true);
|
||||
mat.getAdditionalRenderState().setDepthTest(false);
|
||||
highlight.setMaterial(mat);
|
||||
highlight.setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket.Transparent);
|
||||
selectedItemNode.attachChild(highlight);
|
||||
PlacedItem pi = items.get(idx);
|
||||
input.selectedItemInfo = idx + "|" + pi.itemId() + "|" + pi.x() + "|" + pi.y() + "|" + pi.z();
|
||||
input.itemSelectionChanged = true;
|
||||
}
|
||||
|
||||
private void deselectItem() {
|
||||
if (selectedItemNode != null) {
|
||||
Spatial h = selectedItemNode.getChild("item_sel");
|
||||
if (h != null) h.removeFromParent();
|
||||
selectedItemNode = null;
|
||||
}
|
||||
if (selectedItemIdx >= 0) {
|
||||
selectedItemIdx = -1;
|
||||
input.selectedItemInfo = null;
|
||||
input.itemSelectionChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeItem(int idx) {
|
||||
itemRoot.detachChild(nodes.get(idx));
|
||||
items.remove(idx);
|
||||
nodes.remove(idx);
|
||||
try {
|
||||
PlacedItemIO.save(items);
|
||||
} catch (IOException e) {
|
||||
log.warn("[ItemPlacement] Speichern fehlgeschlagen: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ── Hilfsmethoden ────────────────────────────────────────────────────────
|
||||
|
||||
private Node buildItemNode(String itemId) {
|
||||
|
||||
@@ -78,7 +78,8 @@ public class LocationZoneState extends BaseAppState {
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
if (input.activeLayer != SharedInput.LAYER_LOCATION_ZONES && input.activeLayer != SharedInput.LAYER_ZONEN) {
|
||||
if (input.activeLayer != SharedInput.LAYER_LOCATION_ZONES && input.activeLayer != SharedInput.LAYER_ZONEN
|
||||
&& input.activeLayer != SharedInput.LAYER_AUSWAHL) {
|
||||
if (placing) cancelPoly();
|
||||
return;
|
||||
}
|
||||
@@ -170,6 +171,7 @@ public class LocationZoneState extends BaseAppState {
|
||||
if (sas != null && sas.findZoneAt(hitX, hitZ) >= 0) return;
|
||||
}
|
||||
deselect();
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) return;
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN && !"location".equals(input.zonenNewKind)) return;
|
||||
placing = true;
|
||||
currX.clear();
|
||||
@@ -303,10 +305,13 @@ public class LocationZoneState extends BaseAppState {
|
||||
String triggersJson = de.blight.common.model.trigger.TriggerIO.serializeList(z.triggers());
|
||||
input.selectedLocationZoneInfo = idx + "|" + z.nameId() + "|" + triggersJson;
|
||||
input.locationZoneSelectionChanged = true;
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN) {
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN || input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
input.zonenSelectedKind = "location";
|
||||
input.zonenSelectionChanged = true;
|
||||
}
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
input.auswahlZoneSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Add / Remove / Apply ──────────────────────────────────────────────────
|
||||
|
||||
@@ -399,6 +399,17 @@ public class SceneObjectState extends BaseAppState {
|
||||
|| input.activeLayer == SharedInput.LAYER_AUSWAHL;
|
||||
if (!isObjectLayer) return;
|
||||
|
||||
if (input.deselectAllOnAuswahl) {
|
||||
input.deselectAllOnAuswahl = false;
|
||||
deselectAll();
|
||||
AreaState as = getStateManager().getState(AreaState.class);
|
||||
if (as != null) as.deselectSilent();
|
||||
SoundAreaState sas = getStateManager().getState(SoundAreaState.class);
|
||||
if (sas != null) sas.deselectSilent();
|
||||
LocationZoneState lzs = getStateManager().getState(LocationZoneState.class);
|
||||
if (lzs != null) lzs.deselectSilent();
|
||||
}
|
||||
|
||||
// Animation-Clip-Zuweisung von JavaFX
|
||||
String animClipPending = input.pendingAnimClip;
|
||||
if (animClipPending != null) {
|
||||
@@ -696,8 +707,16 @@ public class SceneObjectState extends BaseAppState {
|
||||
deselectAll();
|
||||
return;
|
||||
}
|
||||
// Auswahl-Modus: kein Objekt angeklickt → nur deselektieren, Layer bleibt
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) { deselectAll(); return; }
|
||||
// Auswahl-Modus: kein Objekt angeklickt → deselektieren + Zonen/Items prüfen lassen
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
deselectAll();
|
||||
float sx = click.screenX(), sy = click.screenY();
|
||||
input.areaClickQueue.offer(new SharedInput.AreaClick(sx, sy, false));
|
||||
input.soundAreaClickQueue.offer(new SharedInput.SoundAreaClick(sx, sy, false));
|
||||
input.locationZoneClickQueue.offer(new SharedInput.LocationZoneClick(sx, sy, false));
|
||||
input.auswahlClickQueue.offer(click);
|
||||
return;
|
||||
}
|
||||
if (input.activeLayer != SharedInput.LAYER_OBJECTS) { deselectAll(); return; }
|
||||
|
||||
String modelPath = input.pendingModelPath;
|
||||
|
||||
@@ -82,7 +82,8 @@ public class SoundAreaState extends BaseAppState {
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
if (input.activeLayer != SharedInput.LAYER_SOUND_AREAS && input.activeLayer != SharedInput.LAYER_ZONEN) {
|
||||
if (input.activeLayer != SharedInput.LAYER_SOUND_AREAS && input.activeLayer != SharedInput.LAYER_ZONEN
|
||||
&& input.activeLayer != SharedInput.LAYER_AUSWAHL) {
|
||||
if (placing) cancelPoly();
|
||||
return;
|
||||
}
|
||||
@@ -174,6 +175,7 @@ public class SoundAreaState extends BaseAppState {
|
||||
if (lzs != null && lzs.findZoneAt(hitX, hitZ) >= 0) return;
|
||||
}
|
||||
deselect();
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) return;
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN && !"sound".equals(input.zonenNewKind)) return;
|
||||
placing = true;
|
||||
currX.clear();
|
||||
@@ -311,10 +313,13 @@ public class SoundAreaState extends BaseAppState {
|
||||
PlacedSoundArea a = areas.get(idx);
|
||||
input.selectedSoundAreaInfo = idx + "|" + a.soundPath() + "|" + a.volume() + "|" + a.crossfade();
|
||||
input.soundAreaSelectionChanged = true;
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN) {
|
||||
if (input.activeLayer == SharedInput.LAYER_ZONEN || input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
input.zonenSelectedKind = "sound";
|
||||
input.zonenSelectionChanged = true;
|
||||
}
|
||||
if (input.activeLayer == SharedInput.LAYER_AUSWAHL) {
|
||||
input.auswahlZoneSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Add / Remove / Apply ──────────────────────────────────────────────────
|
||||
|
||||
@@ -179,7 +179,7 @@ public class DialogEditorView extends BorderPane {
|
||||
skriptBtn = new Button("Skript");
|
||||
skriptBtn.setStyle("-fx-background-color: #3a4a6a; -fx-text-fill: white;");
|
||||
skriptBtn.setDisable(true);
|
||||
skriptBtn.setOnAction(e -> { exportNpcScript(); exportHeroScript(); });
|
||||
skriptBtn.setOnAction(e -> { exportDialogOdt(); exportHeroOdt(); });
|
||||
|
||||
ttsBtn = new Button("TTS");
|
||||
ttsBtn.setStyle("-fx-background-color: #5a3a2a; -fx-text-fill: white;");
|
||||
@@ -1278,7 +1278,7 @@ public class DialogEditorView extends BorderPane {
|
||||
|
||||
// ── Skript-Export ──────────────────────────────────────────────────────────
|
||||
|
||||
private void exportNpcScript() {
|
||||
private void exportDialogOdt() {
|
||||
saveCurrentForm();
|
||||
if (currentNpcId.isBlank() || allOptions.isEmpty()) {
|
||||
Dialogs.alert(Alert.AlertType.WARNING,
|
||||
@@ -1286,39 +1286,91 @@ public class DialogEditorView extends BorderPane {
|
||||
return;
|
||||
}
|
||||
Map<String, String> texts = loadTexts();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("=== NPC-Skript: ").append(currentNpcId).append(" ===\n");
|
||||
sb.append("Exportiert: ").append(LocalDate.now()).append("\n\n");
|
||||
OdtScriptExporter odt = new OdtScriptExporter();
|
||||
odt.h1("Dialog-Skript: " + currentNpcId);
|
||||
odt.normal("Exportiert: " + LocalDate.now());
|
||||
odt.empty();
|
||||
|
||||
String npcLabel = currentNpcId.replace('_', ' ').toUpperCase();
|
||||
|
||||
List<String> ordered = new ArrayList<>(rootIds);
|
||||
for (String id : allOptions.keySet()) {
|
||||
if (!rootIds.contains(id)) ordered.add(id);
|
||||
}
|
||||
|
||||
for (String optId : ordered) {
|
||||
DialogOption opt = allOptions.get(optId);
|
||||
if (opt == null) continue;
|
||||
|
||||
for (Map.Entry<String, DialogOption> entry : allOptions.entrySet()) {
|
||||
DialogOption opt = entry.getValue();
|
||||
String optId = entry.getKey();
|
||||
String base = "dialog." + currentNpcId + "." + optId;
|
||||
List<String> heroKeys = resolveStepKeys(opt.getHeroSteps(), opt.getTextHero(), base, ".textmainchar");
|
||||
List<String> npcKeys = resolveStepKeys(opt.getNpcSteps(), opt.getTextNpc(), base, ".textnpc");
|
||||
if (npcKeys.isEmpty()) continue;
|
||||
if (heroKeys.isEmpty() && npcKeys.isEmpty()) continue;
|
||||
|
||||
sb.append("--- ").append(optId);
|
||||
if (rootIds.contains(optId)) sb.append(" [ROOT]");
|
||||
sb.append(" ---\n");
|
||||
appendStepLines(sb, npcKeys, "NPC", texts);
|
||||
sb.append("\n");
|
||||
odt.h2(optId + (rootIds.contains(optId) ? " [ROOT]" : ""));
|
||||
|
||||
List<String> conds = conditions(opt);
|
||||
if (!conds.isEmpty()) odt.meta("Bedingungen: " + String.join(" / ", conds));
|
||||
|
||||
if (!heroKeys.isEmpty()) {
|
||||
odt.speakerHeld(heroKeys.size() > 1 ? "HELD (" + heroKeys.size() + " Varianten):" : "HELD:");
|
||||
for (int i = 0; i < heroKeys.size(); i++) {
|
||||
String key = heroKeys.get(i);
|
||||
String textVal = texts.getOrDefault(key, "(nicht übersetzt)");
|
||||
if (heroKeys.size() > 1) odt.meta("Variante " + (i + 1) + ":");
|
||||
odt.speechHeld("„" + textVal + "“");
|
||||
odt.meta("Key: " + key);
|
||||
odt.meta("Audio: " + audioPathForKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
writeScript(ProjectRoot.resolve("dialog_script_npc_" + currentNpcId + ".txt"), sb.toString());
|
||||
if (!npcKeys.isEmpty()) {
|
||||
odt.speakerNpc(npcLabel + ":");
|
||||
for (int i = 0; i < npcKeys.size(); i++) {
|
||||
String key = npcKeys.get(i);
|
||||
String textVal = texts.getOrDefault(key, "(nicht übersetzt)");
|
||||
if (npcKeys.size() > 1) odt.meta("Schritt " + (i + 1) + ":");
|
||||
odt.speechNpc("„" + textVal + "“");
|
||||
odt.meta("Key: " + key);
|
||||
odt.meta("Audio: " + audioPathForKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
private void exportHeroScript() {
|
||||
if (opt.getNextOptions() != null && !opt.getNextOptions().isEmpty()) {
|
||||
List<String> ids = opt.getNextOptions().stream()
|
||||
.filter(o -> o != null && o.getId() != null && !o.getId().isBlank())
|
||||
.map(DialogOption::getId).toList();
|
||||
if (!ids.isEmpty()) odt.meta("→ Danach: " + String.join(", ", ids));
|
||||
}
|
||||
if (opt.getDisablesOptions() != null && !opt.getDisablesOptions().isEmpty()) {
|
||||
List<String> ids = opt.getDisablesOptions().stream()
|
||||
.filter(o -> o != null && o.getId() != null && !o.getId().isBlank())
|
||||
.map(DialogOption::getId).toList();
|
||||
if (!ids.isEmpty()) odt.meta("✗ Deaktiviert: " + String.join(", ", ids));
|
||||
}
|
||||
|
||||
odt.empty();
|
||||
}
|
||||
|
||||
writeOdt(ProjectRoot.resolve("dialog_script_" + currentNpcId + ".odt"), odt);
|
||||
}
|
||||
|
||||
private void exportHeroOdt() {
|
||||
Map<String, String> texts = loadTexts();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("=== Held-Skript ===\n");
|
||||
sb.append("Exportiert: ").append(LocalDate.now()).append("\n\n");
|
||||
OdtScriptExporter odt = new OdtScriptExporter();
|
||||
odt.h1("Held-Skript");
|
||||
odt.normal("Exportiert: " + LocalDate.now());
|
||||
odt.empty();
|
||||
|
||||
Path charDir = ProjectRoot.resolve("blight-assets", "src", "main", "resources", "character");
|
||||
for (GameCharacter gc : CharacterIO.loadAll(charDir)) {
|
||||
if (!(gc instanceof NPC npc)) continue;
|
||||
if (npc.getDialogOptions() == null || npc.getDialogOptions().isEmpty()) continue;
|
||||
|
||||
String npcLabel = (npc.getCharacterId() != null ? npc.getCharacterId() : "NPC")
|
||||
.replace('_', ' ').toUpperCase();
|
||||
boolean npcHeaderWritten = false;
|
||||
|
||||
for (Map.Entry<String, DialogOption> entry : npc.getDialogOptions().entrySet()) {
|
||||
String optId = entry.getKey();
|
||||
DialogOption opt = entry.getValue();
|
||||
@@ -1327,31 +1379,73 @@ public class DialogEditorView extends BorderPane {
|
||||
if (heroKeys.isEmpty()) continue;
|
||||
|
||||
if (!npcHeaderWritten) {
|
||||
sb.append("=== NPC: ").append(npc.getCharacterId()).append(" ===\n\n");
|
||||
odt.h1("NPC: " + npc.getCharacterId());
|
||||
odt.empty();
|
||||
npcHeaderWritten = true;
|
||||
}
|
||||
sb.append("--- ").append(optId).append(" ---\n");
|
||||
appendStepLines(sb, heroKeys, "HELD", texts);
|
||||
sb.append("\n");
|
||||
odt.h2(optId);
|
||||
|
||||
List<String> npcKeys = resolveStepKeys(opt.getNpcSteps(), opt.getTextNpc(), base, ".textnpc");
|
||||
if (!npcKeys.isEmpty()) {
|
||||
odt.speakerNpc(npcLabel + " (Kontext):");
|
||||
for (String key : npcKeys) {
|
||||
odt.contextNpc("„" + texts.getOrDefault(key, "(nicht übersetzt)") + "“");
|
||||
}
|
||||
}
|
||||
|
||||
odt.speakerHeld("HELD:");
|
||||
for (int i = 0; i < heroKeys.size(); i++) {
|
||||
String key = heroKeys.get(i);
|
||||
if (heroKeys.size() > 1) odt.meta("Variante " + (i + 1) + ":");
|
||||
odt.speechHeld("„" + texts.getOrDefault(key, "(nicht übersetzt)") + "“");
|
||||
odt.meta("Key: " + key);
|
||||
odt.meta("Audio: " + audioPathForKey(key));
|
||||
}
|
||||
odt.empty();
|
||||
}
|
||||
}
|
||||
|
||||
List<Monologue> monologues = MonologueIO.load();
|
||||
if (!monologues.isEmpty()) {
|
||||
sb.append("=== Monologe ===\n\n");
|
||||
odt.h1("Monologe");
|
||||
odt.empty();
|
||||
for (Monologue m : monologues) {
|
||||
if (m.getHeroSteps() == null || m.getHeroSteps().isEmpty()) continue;
|
||||
sb.append("--- ").append(m.getId()).append(" ---\n");
|
||||
List<String> keys = m.getHeroSteps().stream()
|
||||
.filter(s -> s.getText() != null && !s.getText().id().isBlank())
|
||||
.map(s -> s.getText().id())
|
||||
.toList();
|
||||
appendStepLines(sb, keys, "HELD", texts);
|
||||
sb.append("\n");
|
||||
.map(s -> s.getText().id()).toList();
|
||||
if (keys.isEmpty()) continue;
|
||||
odt.h2(m.getId());
|
||||
odt.speakerHeld("HELD:");
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = keys.get(i);
|
||||
if (keys.size() > 1) odt.meta("Schritt " + (i + 1) + ":");
|
||||
odt.speechHeld("„" + texts.getOrDefault(key, "(nicht übersetzt)") + "“");
|
||||
odt.meta("Key: " + key);
|
||||
odt.meta("Audio: " + audioPathForKey(key));
|
||||
}
|
||||
odt.empty();
|
||||
}
|
||||
}
|
||||
|
||||
writeScript(ProjectRoot.resolve("dialog_script_held.txt"), sb.toString());
|
||||
writeOdt(ProjectRoot.resolve("dialog_script_held.odt"), odt);
|
||||
}
|
||||
|
||||
private List<String> conditions(DialogOption opt) {
|
||||
List<String> conds = new ArrayList<>();
|
||||
if (opt.getRequiresChapter() > 0)
|
||||
conds.add("Kapitel ≥ " + opt.getRequiresChapter());
|
||||
if (opt.getRequiresStatus() != null)
|
||||
conds.add("Status: " + opt.getRequiresStatus());
|
||||
if (opt.getRequiresQuestOpen() != null
|
||||
&& opt.getRequiresQuestOpen().getQuestId() != null
|
||||
&& !opt.getRequiresQuestOpen().getQuestId().isBlank())
|
||||
conds.add("Quest offen: " + opt.getRequiresQuestOpen().getQuestId());
|
||||
if (opt.getRequiresQuestComplete() != null
|
||||
&& opt.getRequiresQuestComplete().getQuestId() != null
|
||||
&& !opt.getRequiresQuestComplete().getQuestId().isBlank())
|
||||
conds.add("Quest erfüllt: " + opt.getRequiresQuestComplete().getQuestId());
|
||||
return conds;
|
||||
}
|
||||
|
||||
private Map<String, String> loadTexts() {
|
||||
@@ -1363,20 +1457,9 @@ public class DialogEditorView extends BorderPane {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
private static void appendStepLines(StringBuilder sb, List<String> keys,
|
||||
String label, Map<String, String> texts) {
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = keys.get(i);
|
||||
sb.append(keys.size() > 1 ? " " + label + " " + (i + 1) + ":\n" : " " + label + ":\n");
|
||||
sb.append(" Key: ").append(key).append("\n");
|
||||
sb.append(" Text: \"").append(texts.getOrDefault(key, "(nicht übersetzt)")).append("\"\n");
|
||||
sb.append(" Audio: ").append(audioPathForKey(key)).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeScript(Path out, String content) {
|
||||
private void writeOdt(Path out, OdtScriptExporter odt) {
|
||||
try {
|
||||
Files.writeString(out, content);
|
||||
odt.write(out);
|
||||
Dialogs.alert(Alert.AlertType.INFORMATION,
|
||||
"Skript exportiert nach:\n" + out, ButtonType.OK).showAndWait();
|
||||
} catch (IOException ex) {
|
||||
@@ -1399,7 +1482,7 @@ public class DialogEditorView extends BorderPane {
|
||||
return List.of(base + suffix);
|
||||
}
|
||||
|
||||
private static String audioPathForKey(String key) {
|
||||
static String audioPathForKey(String key) {
|
||||
if (key == null) return "—";
|
||||
if (key.startsWith("dialog.")) {
|
||||
String without = key.substring("dialog.".length());
|
||||
|
||||
@@ -46,10 +46,20 @@ public class MonologueEditorView extends SplitPane {
|
||||
// ── Liste ─────────────────────────────────────────────────────────────────
|
||||
|
||||
private VBox buildList() {
|
||||
listView.setStyle("-fx-control-inner-background: #2a2a3a; -fx-text-fill: #ddd;"
|
||||
+ " -fx-selection-bar: #3a5a8a; -fx-selection-bar-text: white;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
{ selectedProperty().addListener((obs, old, sel) -> applyStyle()); }
|
||||
@Override protected void updateItem(Monologue m, boolean empty) {
|
||||
super.updateItem(m, empty);
|
||||
setText(empty || m == null ? null : (m.getId().isBlank() ? "(kein Name)" : m.getId()));
|
||||
if (empty || m == null) { setText(null); setStyle(""); return; }
|
||||
setText(m.getId().isBlank() ? "(kein Name)" : m.getId());
|
||||
applyStyle();
|
||||
}
|
||||
private void applyStyle() {
|
||||
if (getItem() == null || isEmpty()) { setStyle(""); return; }
|
||||
String bg = isSelected() ? "#3a5a8a" : "#2a2a3a";
|
||||
setStyle("-fx-background-color: " + bg + "; -fx-text-fill: #cccccc;");
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty().addListener((obs, o, n) -> {
|
||||
@@ -94,11 +104,14 @@ public class MonologueEditorView extends SplitPane {
|
||||
|
||||
playOnceCheck = new CheckBox("Nur einmal abspielen");
|
||||
playOnceCheck.setSelected(true);
|
||||
playOnceCheck.setStyle("-fx-text-fill: #ccc;");
|
||||
|
||||
// Schritte
|
||||
stepsView = new ListView<>();
|
||||
stepsView.setPrefHeight(120);
|
||||
stepsView.setStyle("-fx-background-color: #1e1e2e; -fx-control-inner-background: #1e1e2e;");
|
||||
stepsView.setCellFactory(javafx.scene.control.cell.TextFieldListCell.forListView());
|
||||
stepsView.setEditable(true);
|
||||
|
||||
Button addStepBtn = new Button("+ Schritt");
|
||||
addStepBtn.setOnAction(e -> {
|
||||
@@ -111,22 +124,18 @@ public class MonologueEditorView extends SplitPane {
|
||||
String sel = stepsView.getSelectionModel().getSelectedItem();
|
||||
if (sel != null) stepsView.getItems().remove(sel);
|
||||
});
|
||||
|
||||
stepsView.setEditable(true);
|
||||
stepsView.setCellFactory(javafx.scene.control.cell.TextFieldListCell.forListView());
|
||||
|
||||
HBox stepBtns = new HBox(6, addStepBtn, delStepBtn);
|
||||
|
||||
// Quest-Folgen
|
||||
recvQuestField = questField("Quest erhalten (ID)");
|
||||
fulfillsQuestField = questField("Quest erfüllen (ID)");
|
||||
recvQuestField = questField("Quest wählen…");
|
||||
fulfillsQuestField = questField("Quest wählen…");
|
||||
|
||||
abortsQuestsView = new ListView<>();
|
||||
abortsQuestsView.setPrefHeight(80);
|
||||
abortsQuestsView.setStyle("-fx-background-color: #1e1e2e; -fx-control-inner-background: #1e1e2e;");
|
||||
Button addAbortBtn = new Button("+ Quest abbrechen");
|
||||
addAbortBtn.setOnAction(e -> {
|
||||
String id = showQuestPicker();
|
||||
String id = showQuestPickerDialog();
|
||||
if (id != null && !abortsQuestsView.getItems().contains(id)) abortsQuestsView.getItems().add(id);
|
||||
});
|
||||
Button delAbortBtn = new Button("- Entfernen");
|
||||
@@ -136,9 +145,6 @@ public class MonologueEditorView extends SplitPane {
|
||||
});
|
||||
HBox abortBtns = new HBox(6, addAbortBtn, delAbortBtn);
|
||||
|
||||
Label stepsLbl = sectionLabel("Schritte (Textschlüssel)");
|
||||
Label questLbl = sectionLabel("Quest-Folgen");
|
||||
|
||||
VBox form = new VBox(10);
|
||||
form.setPadding(new Insets(12));
|
||||
form.setStyle("-fx-background-color: #1e1e2e;");
|
||||
@@ -147,12 +153,13 @@ public class MonologueEditorView extends SplitPane {
|
||||
row("Kapitel (mind.):", chapterSpinner),
|
||||
playOnceCheck,
|
||||
new Separator(),
|
||||
stepsLbl, stepsView, stepBtns,
|
||||
sectionLabel("Schritte (Textschlüssel)"),
|
||||
stepsView, stepBtns,
|
||||
new Separator(),
|
||||
questLbl,
|
||||
sectionLabel("Quest-Folgen"),
|
||||
row("Erhält Quest:", recvQuestField),
|
||||
row("Erfüllt Quest:", fulfillsQuestField),
|
||||
new Label("Bricht Quests ab:"),
|
||||
sectionLabel("Bricht Quests ab:"),
|
||||
abortsQuestsView, abortBtns
|
||||
);
|
||||
|
||||
@@ -179,10 +186,7 @@ public class MonologueEditorView extends SplitPane {
|
||||
current = m;
|
||||
loading = true;
|
||||
try {
|
||||
if (m == null) {
|
||||
clearForm();
|
||||
return;
|
||||
}
|
||||
if (m == null) { clearForm(); return; }
|
||||
idField.setText(m.getId());
|
||||
chapterSpinner.getValueFactory().setValue(m.getRequiresChapter());
|
||||
playOnceCheck.setSelected(m.isPlayOnce());
|
||||
@@ -215,10 +219,9 @@ public class MonologueEditorView extends SplitPane {
|
||||
|
||||
List<DialogStep> steps = new ArrayList<>();
|
||||
for (String key : stepsView.getItems()) {
|
||||
if (!key.isBlank()) {
|
||||
if (!key.isBlank())
|
||||
steps.add(new DialogStep(new TextReference(key), new AudioReference(key)));
|
||||
}
|
||||
}
|
||||
current.setHeroSteps(steps);
|
||||
|
||||
current.setRecievesQuest(questRefFromText(recvQuestField.getText()));
|
||||
@@ -283,13 +286,13 @@ public class MonologueEditorView extends SplitPane {
|
||||
tf.setEditable(false);
|
||||
tf.setCursor(Cursor.HAND);
|
||||
tf.setOnMouseClicked(e -> {
|
||||
String id = showQuestPicker();
|
||||
String id = showQuestPickerDialog();
|
||||
if (id != null) tf.setText(id);
|
||||
});
|
||||
return tf;
|
||||
}
|
||||
|
||||
private String showQuestPicker() {
|
||||
private String showQuestPickerDialog() {
|
||||
Path questDir = ProjectRoot.resolve("blight-assets", "src", "main", "resources").resolve("quests");
|
||||
List<Quest> quests = QuestIO.loadAll(questDir);
|
||||
|
||||
@@ -310,11 +313,17 @@ public class MonologueEditorView extends SplitPane {
|
||||
}
|
||||
});
|
||||
chooser.getItems().setAll(quests);
|
||||
chooser.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
chooser.setPrefSize(380, 280);
|
||||
chooser.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;"
|
||||
+ " -fx-selection-bar: #3a5a8a;");
|
||||
chooser.setPrefSize(380, 300);
|
||||
|
||||
dlg.getDialogPane().setContent(quests.isEmpty()
|
||||
? new Label("Keine Quests gefunden.") : chooser);
|
||||
if (quests.isEmpty()) {
|
||||
Label hint = new Label("Keine Quests gefunden.\nQuests im Quest-Editor anlegen.");
|
||||
hint.setStyle("-fx-text-fill: #888; -fx-font-style: italic;");
|
||||
dlg.getDialogPane().setContent(hint);
|
||||
} else {
|
||||
dlg.getDialogPane().setContent(chooser);
|
||||
}
|
||||
dlg.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
|
||||
dlg.getDialogPane().setStyle("-fx-background-color: #252535;");
|
||||
|
||||
@@ -342,13 +351,13 @@ public class MonologueEditorView extends SplitPane {
|
||||
|
||||
private static Label sectionLabel(String text) {
|
||||
Label l = new Label(text);
|
||||
l.setStyle("-fx-font-weight: bold; -fx-text-fill: #aaa; -fx-font-size: 11;");
|
||||
l.setStyle("-fx-font-weight: bold; -fx-font-size: 12; -fx-text-fill: #88aacc;");
|
||||
return l;
|
||||
}
|
||||
|
||||
private static HBox row(String labelText, javafx.scene.Node control) {
|
||||
Label lbl = new Label(labelText);
|
||||
lbl.setMinWidth(130);
|
||||
lbl.setMinWidth(150);
|
||||
lbl.setStyle("-fx-text-fill: #aaa;");
|
||||
HBox.setHgrow(control, Priority.ALWAYS);
|
||||
HBox box = new HBox(8, lbl, control);
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package de.blight.editor.ui;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.*;
|
||||
|
||||
/** Builds an .odt (Open Document Text) ZIP archive for dialog script exports. */
|
||||
public class OdtScriptExporter {
|
||||
|
||||
private final StringBuilder body = new StringBuilder();
|
||||
|
||||
public void h1(String text) { para("P_H1", text); }
|
||||
public void h2(String text) { para("P_H2", text); }
|
||||
public void normal(String text) { para("P_normal", text); }
|
||||
public void meta(String text) { para("P_meta", text); }
|
||||
public void speakerHeld(String text) { para("P_speaker_held",text); }
|
||||
public void speakerNpc(String text) { para("P_speaker_npc", text); }
|
||||
public void contextNpc(String text) { para("P_context_npc", text); }
|
||||
|
||||
/** Hero speech text — italic + highlighted (yellow background). */
|
||||
public void speechHeld(String text) {
|
||||
body.append("<text:p text:style-name='P_speech_held'>")
|
||||
.append("<text:span text:style-name='C_hi'>").append(esc(text)).append("</text:span>")
|
||||
.append("</text:p>\n");
|
||||
}
|
||||
|
||||
/** NPC speech text — bold + highlighted (yellow background). */
|
||||
public void speechNpc(String text) {
|
||||
body.append("<text:p text:style-name='P_speech_npc'>")
|
||||
.append("<text:span text:style-name='C_hi'>").append(esc(text)).append("</text:span>")
|
||||
.append("</text:p>\n");
|
||||
}
|
||||
|
||||
public void empty() {
|
||||
body.append("<text:p text:style-name='P_normal'/>\n");
|
||||
}
|
||||
|
||||
private void para(String style, String text) {
|
||||
body.append("<text:p text:style-name='").append(style).append("'>")
|
||||
.append(esc(text)).append("</text:p>\n");
|
||||
}
|
||||
|
||||
public void write(Path out) throws IOException {
|
||||
byte[] content = buildContentXml().getBytes(StandardCharsets.UTF_8);
|
||||
byte[] manifest = MANIFEST.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] mime = "application/vnd.oasis.opendocument.text".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
try (ZipOutputStream zip = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(out)))) {
|
||||
// mimetype MUST be first entry and MUST use STORED (uncompressed)
|
||||
var crc = new CRC32();
|
||||
crc.update(mime);
|
||||
ZipEntry mimeEntry = new ZipEntry("mimetype");
|
||||
mimeEntry.setMethod(ZipEntry.STORED);
|
||||
mimeEntry.setSize(mime.length);
|
||||
mimeEntry.setCompressedSize(mime.length);
|
||||
mimeEntry.setCrc(crc.getValue());
|
||||
zip.putNextEntry(mimeEntry);
|
||||
zip.write(mime);
|
||||
zip.closeEntry();
|
||||
|
||||
zip.putNextEntry(new ZipEntry("META-INF/manifest.xml"));
|
||||
zip.write(manifest);
|
||||
zip.closeEntry();
|
||||
|
||||
zip.putNextEntry(new ZipEntry("content.xml"));
|
||||
zip.write(content);
|
||||
zip.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private String buildContentXml() {
|
||||
return """
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<office:document-content
|
||||
xmlns:office='urn:oasis:names:tc:opendocument:xmlns:office:1.0'
|
||||
xmlns:text='urn:oasis:names:tc:opendocument:xmlns:text:1.0'
|
||||
xmlns:style='urn:oasis:names:tc:opendocument:xmlns:style:1.0'
|
||||
xmlns:fo='urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'
|
||||
office:version='1.3'>
|
||||
<office:automatic-styles>
|
||||
""" + STYLES + """
|
||||
</office:automatic-styles>
|
||||
<office:body><office:text>
|
||||
""" + body + """
|
||||
</office:text></office:body>
|
||||
</office:document-content>
|
||||
""";
|
||||
}
|
||||
|
||||
private static final String STYLES =
|
||||
ps("P_H1", "fo:margin-top='0.5cm' fo:margin-bottom='0.2cm'",
|
||||
"fo:font-size='16pt' fo:font-weight='bold' fo:color='#003366'")
|
||||
+ ps("P_H2", "fo:margin-top='0.6cm' fo:margin-bottom='0.1cm'"
|
||||
+ " fo:border-top='0.5pt solid #aaaaaa' fo:padding-top='0.15cm'",
|
||||
"fo:font-size='12pt' fo:font-weight='bold' fo:color='#224466'")
|
||||
+ ps("P_normal", "", "fo:font-size='10pt'")
|
||||
+ ps("P_meta", "fo:margin-left='1.2cm'",
|
||||
"fo:font-size='9pt' fo:color='#888888'")
|
||||
+ ps("P_speaker_held","fo:margin-top='0.3cm' fo:margin-left='0.3cm'",
|
||||
"fo:font-size='10pt' fo:font-weight='bold' fo:font-style='italic' fo:color='#445577'")
|
||||
+ ps("P_speaker_npc", "fo:margin-top='0.3cm' fo:margin-left='0.3cm'",
|
||||
"fo:font-size='10pt' fo:font-weight='bold' fo:color='#222222'")
|
||||
+ ps("P_speech_held", "fo:margin-left='1.2cm' fo:margin-top='0.05cm' fo:margin-bottom='0.05cm'",
|
||||
"fo:font-size='11pt' fo:font-style='italic' fo:color='#334466'")
|
||||
+ ps("P_speech_npc", "fo:margin-left='1.2cm' fo:margin-top='0.05cm' fo:margin-bottom='0.05cm'",
|
||||
"fo:font-size='11pt' fo:color='#111111'")
|
||||
+ ps("P_context_npc", "fo:margin-left='1.2cm' fo:margin-top='0.02cm' fo:margin-bottom='0.02cm'",
|
||||
"fo:font-size='10pt' fo:color='#999999'")
|
||||
+ " <style:style style:name='C_hi' style:family='text'>\n"
|
||||
+ " <style:text-properties fo:font-weight='bold' fo:background-color='#ffffcc'/>\n"
|
||||
+ " </style:style>\n";
|
||||
|
||||
private static String ps(String name, String pProp, String tProp) {
|
||||
String s = " <style:style style:name='" + name + "' style:family='paragraph'>\n";
|
||||
if (!pProp.isBlank()) s += " <style:paragraph-properties " + pProp + "/>\n";
|
||||
if (!tProp.isBlank()) s += " <style:text-properties " + tProp + "/>\n";
|
||||
return s + " </style:style>\n";
|
||||
}
|
||||
|
||||
private static final String MANIFEST = """
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<manifest:manifest xmlns:manifest='urn:oasis:names:tc:opendocument:xmlns:manifest:1.0' manifest:version='1.3'>
|
||||
<manifest:file-entry manifest:full-path='/' manifest:media-type='application/vnd.oasis.opendocument.text'/>
|
||||
<manifest:file-entry manifest:full-path='content.xml' manifest:media-type='text/xml'/>
|
||||
</manifest:manifest>
|
||||
""";
|
||||
|
||||
private static String esc(String s) {
|
||||
if (s == null) return "";
|
||||
return s.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||
}
|
||||
}
|
||||
@@ -168,3 +168,73 @@ dialog.silas.erstaunt_ruhe.textnpc=Vielleicht wirke ich so. Und vielleicht habe
|
||||
|
||||
dialog.silas.abschluss_letzte_worte.textmainchar=(Der Held macht sich auf den Weg)
|
||||
dialog.silas.abschluss_letzte_worte.textnpc=Jetzt geh schon. Bevor ich es mir anders überlege und dich doch noch für die Steuerjagd anmelde.
|
||||
|
||||
# ── Björnson ─────────────────────────────────────────────────────────────────
|
||||
bjoernson.name=Björnson
|
||||
|
||||
# Dialog: Begegnung 1 – Der Sklavenring
|
||||
dialog.bjoernson.begruessung.textmainchar=Silas hat mich geschickt. Ich muss diese Fesseln loswerden. Sie sind alt... und sie schmerzen.
|
||||
dialog.bjoernson.begruessung.textnpc=Sklavenstahl. Mieses Zeug, gemacht zum Erniedrigen. Silas weiß, dass ich so ein Gerümpel in zwei Minuten von deinen Knochen meißeln kann. Normalerweise nehme ich für meine Zeit gutes Gold... aber von dir gibt's wohl nicht viel zu holen.
|
||||
|
||||
dialog.bjoernson.wer_bist_du.textmainchar=Wer bist du überhaupt?
|
||||
dialog.bjoernson.wer_bist_du.textnpc=Ich bin Björnson. Früher war ich ein Freibeuter und lebte mit den anderen im Süden der Insel, aber der Ganze Trubel... Wir aus dem Norden sind eigentlich nicht gemacht für Lärm und Heiterkeit, aber meine Schwestern und Brüder scheinen das vergessen zu haben.
|
||||
|
||||
dialog.bjoernson.aus_dem_norden.textmainchar=Aus dem Norden?
|
||||
dialog.bjoernson.aus_dem_norden.textnpc=Unsere Vorfahren kamen vor vielen Wintern hierher, und haben sich hier nieder gelassen. Weißt du überhaupt was Winter sind?
|
||||
dialog.bjoernson.aus_dem_norden.textnpc.1=Weißt du... ich vermisse den Lärm der Freibeuter nicht. Ich vermisse nicht ihr Geschrei und nicht ihre blöden Gesichter. Aber was ich vermisse, ist ein vernünftiges, dunkles Gerstenbier aus dem Norden.
|
||||
|
||||
dialog.bjoernson.bier_angebot.textmainchar=Vielleicht können wir uns bezüglich der Fesseln ja einig werden, wenn ich dir ein Bier organisiere?
|
||||
dialog.bjoernson.bier_angebot.textnpc=Schaff mir ein kühles Krüglein von diesem Bier hier hoch. Ohne dass es warm wird. Dann schlage ich dir das Eisen von den Handgelenken und wir sind quitt. Keine großen Reden, kein Gelaber. Nur Bier gegen Freiheit. Abgemacht?
|
||||
|
||||
dialog.bjoernson.abgemacht_bier.textmainchar=Abgemacht.
|
||||
|
||||
dialog.bjoernson.bier_geliefert.textmainchar=Kälte kenne ich nur von feuchten Zellennächten. Aber hier ist dein Bier!
|
||||
dialog.bjoernson.bier_geliefert.textnpc=Nicht dasselbe... Aber egal. Dann widmen wir uns mal deinem Problem!
|
||||
|
||||
# Dialog: Begegnung 2 – Ein echtes Schwert
|
||||
dialog.bjoernson.schwert_annehmen.textmainchar=Silas meint, ich bin bereit für eine echte Waffe. Er schickt mich zu dir, damit wir gemeinsam ein Schwert schmieden.
|
||||
dialog.bjoernson.schwert_annehmen.textnpc=Du schon wieder. Das Bier letztes Mal war brauchbar. Aber du kommst nicht für einen Plausch hoch, das sehe ich dir an. Was führt dich zu meinem Amboss?
|
||||
dialog.bjoernson.schwert_annehmen.textnpc.1=Silas... der Kerl denkt auch, Schmieden sei wie Fische fangen. "Zusammen schmieden"? Ich bin Schmied, kein Lehrmeister für Strandgut!
|
||||
dialog.bjoernson.schwert_annehmen.textnpc.2=Aber gut. Wer ein echtes Schwert führen will, muss wissen, was darin steckt. Für eine Klinge, die nicht beim ersten Schlag gegen eine Protektorats-Rüstung zersplittert, brauche ich vernünftiges Material. Nicht den Müll, den die Gezeiten anspülen.
|
||||
dialog.bjoernson.schwert_annehmen.textnpc.3=Tief in den Bergen liegt ein alter Magnetit-Stollen. Die Ader dort liefert reinstes Eisen. Aber seit Monden treibt sich dort ein aggressiver Blight-Wühler rum – ein Biesterzeug, das durch die Verderbnis der Erde durchgedreht ist. Es blockiert den Zugang.
|
||||
|
||||
dialog.bjoernson.ich_kuemmere_mich.textmainchar=Ich kümmere mich darum.
|
||||
dialog.bjoernson.ich_kuemmere_mich.textnpc=Das wirst du müssen. Schlag das Vieh tot, räum den Weg frei und bring mir eine Ladung des reinen Magnetiterzes mit. Wenn du das überlebst… dann schauen wir weiter!
|
||||
|
||||
dialog.bjoernson.erz_quest.textmainchar=Alles klar!
|
||||
|
||||
dialog.bjoernson.wie_schuerfern.textmainchar=Eine Sache noch... Wie kann ich das Erz schürfen?
|
||||
dialog.bjoernson.wie_schuerfern.textnpc=Das kann ich dir gerne beibringen, aber auch das wird dich etwas kosten was über einen Gerstensaft hinaus geht.
|
||||
dialog.bjoernson.wie_schuerfern.textnpc.1=Und schau mal neben der Mine, dort solltest du auch das entsprechende Werkzeug für deinen Job finden.
|
||||
|
||||
dialog.bjoernson.danke_bjoernson.textmainchar=Danke Björnson.
|
||||
dialog.bjoernson.danke_bjoernson.textnpc=Mhhhh...
|
||||
|
||||
# Dialog: Quest-Abschluss Schwert
|
||||
dialog.bjoernson.quests_erledigt.textmainchar=Ich habe alles erledigt!
|
||||
dialog.bjoernson.quests_erledigt.textnpc=Erstaunlich – damit hatte ich nicht gerechnet.
|
||||
|
||||
dialog.bjoernson.ich_bin_zaeh.textmainchar=Ich bin zäher als ich aussehe.
|
||||
dialog.bjoernson.ich_bin_zaeh.textnpc=In der Tat…
|
||||
|
||||
dialog.bjoernson.wie_stehts_abmachung.textmainchar=Wie steht es nun um unsere Abmachung? Du schmiedest mir das Schwert?
|
||||
dialog.bjoernson.wie_stehts_abmachung.textnpc=Oder ich bringe dir bei, wie du es dir selber schmieden kannst, die Wahl liegt bei dir.
|
||||
|
||||
dialog.bjoernson.nur_die_waffe.textmainchar=Ich brauche nur die Waffe…
|
||||
dialog.bjoernson.nur_die_waffe.textnpc=Alles klar…
|
||||
|
||||
dialog.bjoernson.selber_lernen.textmainchar=Ich würde es gerne selber lernen…
|
||||
dialog.bjoernson.selber_lernen.textnpc=Alles klar…
|
||||
|
||||
# Quests
|
||||
quest.kuehles_bier.name=Ein kühles Bier!
|
||||
quest.kuehles_bier.description=Björnson will ein kühles Gerstenbier. Bring ihm eines, ohne dass es warm wird – dann nimmt er dir die Fesseln ab.
|
||||
quest.kuehles_bier.success=Björnson hat sein Bier bekommen und schlägt dir die Eisen von den Handgelenken.
|
||||
|
||||
quest.blight_wuehler.name=Der Blight-Wühler
|
||||
quest.blight_wuehler.description=Ein aggressiver Blight-Wühler blockiert den Eingang zum Magnetit-Stollen in den Bergen. Besiege ihn und mach den Weg frei.
|
||||
quest.blight_wuehler.success=Der Blight-Wühler ist besiegt. Der Stollen ist wieder zugänglich.
|
||||
|
||||
quest.magnetiterz.name=Magnetiterz
|
||||
quest.magnetiterz.description=Schürfe 5 Einheiten reines Magnetiterz aus dem alten Stollen in den Bergen. Björnson braucht es für das Schwert.
|
||||
quest.magnetiterz.success=Du hast genug Magnetiterz gesammelt. Bring es zu Björnson.
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
bjoernson.name
|
||||
blutagave.description
|
||||
blutagave.name
|
||||
dialog.bjoernson.abgemacht_bier.textmainchar
|
||||
dialog.bjoernson.aus_dem_norden.textmainchar
|
||||
dialog.bjoernson.aus_dem_norden.textnpc
|
||||
dialog.bjoernson.aus_dem_norden.textnpc.1
|
||||
dialog.bjoernson.begruessung.textmainchar
|
||||
dialog.bjoernson.begruessung.textnpc
|
||||
dialog.bjoernson.bier_angebot.textmainchar
|
||||
dialog.bjoernson.bier_angebot.textnpc
|
||||
dialog.bjoernson.bier_geliefert.textmainchar
|
||||
dialog.bjoernson.bier_geliefert.textnpc
|
||||
dialog.bjoernson.danke_bjoernson.textmainchar
|
||||
dialog.bjoernson.danke_bjoernson.textnpc
|
||||
dialog.bjoernson.erz_quest.textmainchar
|
||||
dialog.bjoernson.ich_bin_zaeh.textmainchar
|
||||
dialog.bjoernson.ich_bin_zaeh.textnpc
|
||||
dialog.bjoernson.ich_kuemmere_mich.textmainchar
|
||||
dialog.bjoernson.ich_kuemmere_mich.textnpc
|
||||
dialog.bjoernson.nur_die_waffe.textmainchar
|
||||
dialog.bjoernson.nur_die_waffe.textnpc
|
||||
dialog.bjoernson.quests_erledigt.textmainchar
|
||||
dialog.bjoernson.quests_erledigt.textnpc
|
||||
dialog.bjoernson.schwert_annehmen.textmainchar
|
||||
dialog.bjoernson.schwert_annehmen.textnpc
|
||||
dialog.bjoernson.schwert_annehmen.textnpc.1
|
||||
dialog.bjoernson.schwert_annehmen.textnpc.2
|
||||
dialog.bjoernson.schwert_annehmen.textnpc.3
|
||||
dialog.bjoernson.selber_lernen.textmainchar
|
||||
dialog.bjoernson.selber_lernen.textnpc
|
||||
dialog.bjoernson.wer_bist_du.textmainchar
|
||||
dialog.bjoernson.wer_bist_du.textnpc
|
||||
dialog.bjoernson.wie_schuerfern.textmainchar
|
||||
dialog.bjoernson.wie_schuerfern.textnpc
|
||||
dialog.bjoernson.wie_schuerfern.textnpc.1
|
||||
dialog.bjoernson.wie_stehts_abmachung.textmainchar
|
||||
dialog.bjoernson.wie_stehts_abmachung.textnpc
|
||||
dialog.silas.0114af85-986f-4923-b77e-88529b868c0f.description
|
||||
dialog.silas.0114af85-986f-4923-b77e-88529b868c0f.textmainchar
|
||||
dialog.silas.0114af85-986f-4923-b77e-88529b868c0f.textnpc
|
||||
@@ -97,6 +133,15 @@ location.neu_1786533928723
|
||||
location.neu_1786546178407
|
||||
location.neu_1786606437840
|
||||
location.test
|
||||
quest.blight_wuehler.description
|
||||
quest.blight_wuehler.name
|
||||
quest.blight_wuehler.success
|
||||
quest.kuehles_bier.description
|
||||
quest.kuehles_bier.name
|
||||
quest.kuehles_bier.success
|
||||
quest.magnetiterz.description
|
||||
quest.magnetiterz.name
|
||||
quest.magnetiterz.success
|
||||
silas.name
|
||||
strandgut.description
|
||||
strandgut.name
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_16_0_06_baked_lod0.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_16_0_06_baked_lod0.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_16_0_06_baked_lod1.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_16_0_06_baked_lod1.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_16_0_06_baked_lod2.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_16_0_06_baked_lod2.j3o
Normal file
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_17_0_06.blvc.prebake
Normal file
BIN
blight-map/src/main/map/chunks/voxel_17_0_06.blvc.prebake
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_17_0_06_baked_lod0.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_17_0_06_baked_lod0.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_17_0_06_baked_lod1.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_17_0_06_baked_lod1.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_17_0_06_baked_lod2.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_17_0_06_baked_lod2.j3o
Normal file
Binary file not shown.
@@ -1,258 +0,0 @@
|
||||
=== Dialog-Skript: silas ===
|
||||
Exportiert: 2026-08-13
|
||||
|
||||
--- begruessung [ROOT] ---
|
||||
HERO:
|
||||
Key: dialog.silas.begruessung.textmainchar
|
||||
Text: "Hi! Ich suche nur einen Weg weg von diesem Strand."
|
||||
Audio: audio/dialog/silas/begruessung_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.begruessung.textnpc
|
||||
Text: "Woher kommst du?"
|
||||
Audio: audio/dialog/silas/begruessung_textnpc.ogg
|
||||
|
||||
--- antwort_sklave ---
|
||||
HERO:
|
||||
Key: dialog.silas.antwort_sklave.textmainchar
|
||||
Text: "Ich war Sklave des Protektorats – unser Schiff ist an den steilen Klippen gestrandet."
|
||||
Audio: audio/dialog/silas/antwort_sklave_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.antwort_sklave.textnpc
|
||||
Text: "Ein Sklave des Protektorats? Das Meer hat heute wohl einen Sinn für Ironie. Spült dich direkt vor meine Haustür. Immerhin bist du ehrlich… und sind wir nicht alle Sklaven des Protektorats? Ich jage ihr Wild, du hast ihre Steine geschleppt. Am Ende des Tages sitzen wir beide unter demselben Stiefel. Nur dass meiner schon etwas länger drückt."
|
||||
Audio: audio/dialog/silas/antwort_sklave_textnpc.ogg
|
||||
|
||||
--- antwort_gedaechtnislos ---
|
||||
HERO:
|
||||
Key: dialog.silas.antwort_gedaechtnislos.textmainchar
|
||||
Text: "Ich habe mein Gedächtnis verloren – ich bin kürzlich an dem Strand dort unten aufgewacht."
|
||||
Audio: audio/dialog/silas/antwort_gedaechtnislos_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.antwort_gedaechtnislos.textnpc
|
||||
Text: "Du siehst aus wie ein Sklave des Protektorats! Das Meer hat heute wohl einen Sinn für Ironie. Spült dich direkt vor meine Haustür. Aber sind wir nicht alle Sklaven des Protektorats."
|
||||
Audio: audio/dialog/silas/antwort_gedaechtnislos_textnpc.ogg
|
||||
|
||||
--- antwort_gesandter ---
|
||||
HERO:
|
||||
Key: dialog.silas.antwort_gesandter.textmainchar
|
||||
Text: "Ich bin Gesandter des Königs – unser Schiff ist gekentert und ich bin der einzige Überlebende."
|
||||
Audio: audio/dialog/silas/antwort_gesandter_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.antwort_gesandter.textnpc
|
||||
Text: "Ein Gesandter des Königs? Du siehst eher aus wie ein Sklave des Protektorats! Das Meer hat heute wohl einen Sinn für Ironie. Spült dich direkt vor meine Haustür. Aber sind wir nicht alle Sklaven des Protektorats."
|
||||
Audio: audio/dialog/silas/antwort_gesandter_textnpc.ogg
|
||||
|
||||
--- wer_bist_du ---
|
||||
HERO:
|
||||
Key: dialog.silas.wer_bist_du.textmainchar
|
||||
Text: "Wer bist du?"
|
||||
Audio: audio/dialog/silas/wer_bist_du_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.wer_bist_du.textnpc
|
||||
Text: "Silas. Früher haben wir diesen Pass bewacht, damit die Guanchen und wir in Frieden tauschen konnten. Heute bin ich nur noch ein Geist, der hier oben wartet, dass die Fäule auch den letzten Rest der Welt frisst."
|
||||
Audio: audio/dialog/silas/wer_bist_du_textnpc.ogg
|
||||
|
||||
--- wer_seid_ihr ---
|
||||
HERO:
|
||||
Key: dialog.silas.wer_seid_ihr.textmainchar
|
||||
Text: "Und wer seid „Ihr“?"
|
||||
Audio: audio/dialog/silas/wer_seid_ihr_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.wer_seid_ihr.textnpc
|
||||
Text: "Wer wir sind? Eine aussterbende Art, Junge. Ich bin einer der Jäger, die diesen Pass schon bewacht haben, als das Protektorat noch nichts weiter war als ein ferner Albtraum in den Köpfen gieriger Könige. Früher war das hier ein Geben und Nehmen: Wir haben das Wild reguliert, mit den Guanchen Fleisch gegen Kräuter getauscht und die Pfade sauber gehalten.
|
||||
Dann kamen die Eroberer mit ihren Vermessungsgeräten und ihren Gesetzen. Wollten mir vorschreiben, wann ich einen Hirsch erlegen darf und wie viel Steuer ich für jedes Stück Fell an Puerto Valor abtreten soll. Die meisten meiner Brüder sind geflohen – auf die Nachbarinseln oder zu den Piraten, nur um dem Joch zu entkommen. Aber ich? Ich bin zu alt, um vor ein paar aufgeblasenen Gockeln in Blechrüstungen wegzulaufen. Also sitze ich hier oben, lebe von dem, was der Wald noch hergibt, und sehe zu, wie die Fäule alles verschlingt, was mir mal etwas bedeutet hat.
|
||||
Für das Protektorat bin ich ein Wilderer. Für die Piraten ein Narr. Und für die Guanchen… nun, vielleicht der letzte Außenweltler, dem sie nicht sofort einen Speer in die Kehle jagen. Such dir aus, was dir am besten gefällt."
|
||||
Audio: audio/dialog/silas/wer_seid_ihr_textnpc.ogg
|
||||
|
||||
--- was_leuchten ---
|
||||
HERO:
|
||||
Key: dialog.silas.was_leuchten.textmainchar
|
||||
Text: "Was ist das für ein Zeug am Strand? Das Leuchten?"
|
||||
Audio: audio/dialog/silas/was_leuchten_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.was_leuchten.textnpc
|
||||
Text: "Das ist der Tod, Junge. Die Blight. Die arroganten Gecken in Puerto Valor haben den Vulkan angebohrt und jetzt blutet die Insel Gift. Wenn du klug bist, drehst du um und läufst zurück ins Wasser.
|
||||
Nicht alles auf dieser Insel will dich korrumpieren, Junge. Manche wollen dich einfach nur fressen. Ein wilder Hund hat keine Kristalle, die ihn schützen, aber er hat Hunger und Zähne wie Dolche. Behalt ihn im Auge, weich seinen Sprüngen aus und zeig ihm, wer der neue Herr im Dorf ist."
|
||||
Audio: audio/dialog/silas/was_leuchten_textnpc.ogg
|
||||
|
||||
--- ausruestung ---
|
||||
HERO:
|
||||
Key: dialog.silas.ausruestung.textmainchar
|
||||
Text: "Ich brauche Ausrüstung. Waffen. Und Informationen."
|
||||
Audio: audio/dialog/silas/ausruestung_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.ausruestung.textnpc
|
||||
Text: "Ausrüstung? Du siehst aus, als hätte dich das Meer einmal durch den Fleischwolf gedreht. Pass auf: Geh zurück zum Strand, dort wo du angespült wurdest. Bei diesem Sturm ist sicher nicht nur Sklavenfleisch am Riff hängen geblieben. Such im Treibgut. Mit etwas Glück findest du dort mehr als nur nasse Planken."
|
||||
Audio: audio/dialog/silas/ausruestung_textnpc.ogg
|
||||
|
||||
--- gibts_was_rum ---
|
||||
HERO:
|
||||
Key: dialog.silas.gibts_was_rum.textmainchar
|
||||
Text: "Und was ist mit dir? Hast du nichts übrig?"
|
||||
Audio: audio/dialog/silas/gibts_was_rum_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.gibts_was_rum.textnpc
|
||||
Text: "Ich bin kein Wohlfahrtsverband, Junge. Aber … du bist mir sympathisch, einen Schluck Insel-Rum kriegst du. Das Zeug brennt die Korruption aus der Kehle, zumindest für fünf Minuten. Trink."
|
||||
Audio: audio/dialog/silas/gibts_was_rum_textnpc.ogg
|
||||
|
||||
--- tipp_rum ---
|
||||
HERO:
|
||||
Key: dialog.silas.tipp_rum.textmainchar
|
||||
Text: "Und was ist mit dir? Hast du nichts übrig?"
|
||||
Audio: audio/dialog/silas/tipp_rum_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.tipp_rum.textnpc
|
||||
Text: "Ich bin kein Wohlfahrtsverband, Junge. Aber … einen Tipp gibt es umsonst: Insel-Rum ist das Beste, wenn du dich mal zu viel mit den wilden Tieren geprügelt hast."
|
||||
Audio: audio/dialog/silas/tipp_rum_textnpc.ogg
|
||||
|
||||
--- welchen_weg ---
|
||||
HERO:
|
||||
Key: dialog.silas.welchen_weg.textmainchar
|
||||
Text: "Welchen Weg soll ich nehmen, um zu den Lagern zu kommen?"
|
||||
Audio: audio/dialog/silas/welchen_weg_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.welchen_weg.textnpc
|
||||
Text: "Egal welchen Weg du nimmst – geh nicht so, wie du jetzt aussiehst. In diesen Lumpen erkennt dich jeder sofort als entflohenen Sklaven. Das Protektorat steckt dich direkt wieder in Ketten, und die Piraten… nun, die füttern die Haie mit Typen, die nichts wert sind.
|
||||
Hör zu: Wenn du den Hang Richtung Strand wieder ein Stück runtergehst, zweigt ein Pfad zu einem alten Fischerdorf ab. Die Leute dort sind schon lange weg, als die Fäule kam, aber in den Hütten findest du vielleicht ein paar alte Lumpen, die weniger nach 'Gefängnis' stinken. Es ist besser als nichts."
|
||||
Audio: audio/dialog/silas/welchen_weg_textnpc.ogg
|
||||
|
||||
--- abschluss_phase1 ---
|
||||
HERO:
|
||||
Key: dialog.silas.abschluss_phase1.textmainchar
|
||||
Text: "Alles klar. Ich mache mich auf den Weg."
|
||||
Audio: audio/dialog/silas/abschluss_phase1_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.abschluss_phase1.textnpc
|
||||
Text: "Also, hier ist der Plan: Such am Strand nach Brauchbarem, schau im Fischerdorf nach Kleidung und komm dann wieder zu mir. Wenn du dann nicht mehr aussiehst wie eine wandelnde Leiche, unterhalten wir uns darüber, wie du an den Monstern am Pass vorbeikommst. Jetzt beweg dich."
|
||||
Audio: audio/dialog/silas/abschluss_phase1_textnpc.ogg
|
||||
|
||||
--- rueckkehr_beobachtet ---
|
||||
HERO:
|
||||
Key: dialog.silas.rueckkehr_beobachtet.textmainchar
|
||||
Text: "(Ich kehre zu Silas zurück)"
|
||||
Audio: audio/dialog/silas/rueckkehr_beobachtet_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.rueckkehr_beobachtet.textnpc
|
||||
Text: "Sieh mal einer an. Das Meer hat dich ausgespückt und die Insel hat dich nicht wieder verschlungen. Ich hätte meine letzte Flasche Rum darauf verwettet, dass dich die Klippenbeßer am Hang in handliche Stücke zerlegen, noch bevor du das Dorf erreichst."
|
||||
Audio: audio/dialog/silas/rueckkehr_beobachtet_textnpc.ogg
|
||||
|
||||
--- ich_bin_zaeh ---
|
||||
HERO:
|
||||
Key: dialog.silas.ich_bin_zaeh.textmainchar
|
||||
Text: "Ich bin zäher, als ich aussehe."
|
||||
Audio: audio/dialog/silas/ich_bin_zaeh_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.ich_bin_zaeh.textnpc
|
||||
Text: "Das scheinst du wohl zu sein. Zumindest stinkst du jetzt weniger nach Sklavenpferch. Damit kommst du vielleicht an den Wachen der Festung vorbei, ohne dass sie direkt die Musketen laden."
|
||||
Audio: audio/dialog/silas/ich_bin_zaeh_textnpc.ogg
|
||||
|
||||
--- weg_ins_innere ---
|
||||
HERO:
|
||||
Key: dialog.silas.weg_ins_innere.textmainchar
|
||||
Text: "Du hast gesagt, wir unterhalten uns über den Weg ins Landesinnere, wenn ich bereit bin."
|
||||
Audio: audio/dialog/silas/weg_ins_innere_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.weg_ins_innere.textnpc
|
||||
Text: "Richtig. Hör zu, Junge: Die Pfade hier draußen sind tückisch geworden. Mein wichtigster Rat? Bleib auf den Wegen. Die Blight hat die Kreaturen in den Wäldern wahnsinnig gemacht. Abseits der Pfade lauern Dinge, für die dein kleiner Roststängel da am Gürtel nur ein Zahnstocher ist. Geh nur in die tiefe Wildnis, wenn du Rüstung am Leib und Stahl in der Hand hast, der diesen Namen auch verdient."
|
||||
Audio: audio/dialog/silas/weg_ins_innere_textnpc.ogg
|
||||
|
||||
--- was_ueber_insel ---
|
||||
HERO:
|
||||
Key: dialog.silas.was_ueber_insel.textmainchar
|
||||
Text: "Diese Insel… sie wirkt so fremdartig. Was kannst du mir über diesen Ort sagen?"
|
||||
Audio: audio/dialog/silas/was_ueber_insel_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.was_ueber_insel.textnpc
|
||||
Text: "Hör mir gut zu, Junge. Aeterna ist wie eine gespaltene Münze, und der große Vulkan in der Mitte – der Monte Fuego – ist der Amboss, auf dem alles geschmiedet wird. Der Westen ist ein einziges grünes Grab, erstickt von uralten Wäldern. Der Osten dagegen, hinter dem Berg, ist karg und trocken wie ein alter Knochen."
|
||||
Audio: audio/dialog/silas/was_ueber_insel_textnpc.ogg
|
||||
|
||||
--- info_osten ---
|
||||
HERO:
|
||||
Key: dialog.silas.info_osten.textmainchar
|
||||
Text: "Wer herrscht im Osten der Insel?"
|
||||
Audio: audio/dialog/silas/info_osten_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.info_osten.textnpc
|
||||
Text: "Die Protektoren. Sie haben sich im Südosten in Puerto Valor breitgemacht. Sie haben Mauern, Ordnung und das beste Equipment – aber sie haben auch einen Stock im Hintern und verlangen Gehorsam. Wenn du dort ihren Wein trinken willst, den sie in den Sonnental-Weinbergen anbauen, musst du nach ihrer Pfeife tanzen."
|
||||
Audio: audio/dialog/silas/info_osten_textnpc.ogg
|
||||
|
||||
--- info_westen ---
|
||||
HERO:
|
||||
Key: dialog.silas.info_westen.textmainchar
|
||||
Text: "Und was ist mit dem Westen?"
|
||||
Audio: audio/dialog/silas/info_westen_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.info_westen.textnpc
|
||||
Text: "Dort regiert das Chaos oder die Natur. Im Südwesten liegt Shipwreck Bay, das Nest der Freibeuter. Da stellt keiner Fragen über deine Vergangenheit, solange du zupacken kannst. Aber pass auf deinen Geldbeutel auf – und auf deinen Rücken. Tief im Nordwesten, im Schatten der Nebelgipfel, hausen die Guanchen in ihrer Siedlung Taganana. Du musst sie erst mal finden, aber wenn du Magie suchst, sind sie deine einzige Chance."
|
||||
Audio: audio/dialog/silas/info_westen_textnpc.ogg
|
||||
|
||||
--- info_dorf ---
|
||||
HERO:
|
||||
Key: dialog.silas.info_dorf.textmainchar
|
||||
Text: "Was hat es mit diesem Dorf hier auf sich?"
|
||||
Audio: audio/dialog/silas/info_dorf_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.info_dorf.textnpc
|
||||
Text: "Das hier im Norden ist San Pedro. Früher ein reiches Fischerdorf, jetzt nur noch Schutt und Gestrüpp. Ein guter Ort, um den Kampf gegen Klippenbeßer zu üben. Ein wilder Hund hat keine Kristalle, die ihn schützen, aber er hat Hunger und Zähne wie Dolche. Weich ihren Sprüngen aus, dann gehört das Fleisch dir."
|
||||
Audio: audio/dialog/silas/info_dorf_textnpc.ogg
|
||||
|
||||
--- info_pass ---
|
||||
HERO:
|
||||
Key: dialog.silas.info_pass.textmainchar
|
||||
Text: "Und der Weg durch den Pass?"
|
||||
Audio: audio/dialog/silas/info_pass_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.info_pass.textnpc
|
||||
Text: "Hinter meiner Hütte geht es steil bergauf. Oben am Kamm haben sich ein paar Corrupted eingenistet. Sie sind langsam, aber wenn sie dich einkesseln, war es das. Nutze den Platz, den du hast, und schlag erst zu, wenn du eine Lücke siehst. Sobald du an ihnen vorbei bist, liegt Aeterna vor dir."
|
||||
Audio: audio/dialog/silas/info_pass_textnpc.ogg
|
||||
|
||||
--- ich_weiss_genug ---
|
||||
HERO:
|
||||
Key: dialog.silas.ich_weiss_genug.textmainchar
|
||||
Text: "Ich glaub ich weiß alles, was ich brauche…"
|
||||
Audio: audio/dialog/silas/ich_weiss_genug_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.ich_weiss_genug.textnpc
|
||||
Text: "Du willst also blind ins Verderben rennen? Hier, nimm das, damit deine Leiche wenigstens in der Nähe eines Pfades gefunden wird."
|
||||
Audio: audio/dialog/silas/ich_weiss_genug_textnpc.ogg
|
||||
|
||||
--- karte_zeichnen ---
|
||||
HERO:
|
||||
Key: dialog.silas.karte_zeichnen.textmainchar
|
||||
Text: "(Silas zeichnet die Karte)"
|
||||
Audio: audio/dialog/silas/karte_zeichnen_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.karte_zeichnen.textnpc
|
||||
Text: "Du stellst verdammt viele Fragen für jemanden, der kaum eine Waffe halten kann. Aber Neugier hält einen hier oben am Leben.
|
||||
Komm näher. Ich zeichne dir das Ganze auf: San Pedro hier oben, Monte Fuego in der Mitte, Puerto Valor im trockenen Südosten und Shipwreck Bay im Wald des Südwestens. Der Pfad führt direkt hinauf zum Schlund… aber sag nicht, ich hätte dich nicht gewarnt. Hier… nimm die Karte."
|
||||
Audio: audio/dialog/silas/karte_zeichnen_textnpc.ogg
|
||||
|
||||
--- dankeschoen ---
|
||||
HERO:
|
||||
Key: dialog.silas.dankeschoen.textmainchar
|
||||
Text: "Danke, Silas. Ich werde vorsichtig sein."
|
||||
Audio: audio/dialog/silas/dankeschoen_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.dankeschoen.textnpc
|
||||
Text: "Das hoffe ich. Und hey… wenn du mal wieder in der Nähe bist und noch alle Gliedmaßen hast, schau auf einen Rum vorbei. Erzähl mir, was in der Welt da draußen vor sich geht. Ich will wissen, wer sich gerade gegenseitig an die Gurgel geht."
|
||||
Audio: audio/dialog/silas/dankeschoen_textnpc.ogg
|
||||
|
||||
--- erstaunt_ruhe ---
|
||||
HERO:
|
||||
Key: dialog.silas.erstaunt_ruhe.textmainchar
|
||||
Text: "Ich dachte, du willst hier oben deine Ruhe haben und hast die Welt da draußen längst abgeschrieben?"
|
||||
Audio: audio/dialog/silas/erstaunt_ruhe_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.erstaunt_ruhe.textnpc
|
||||
Text: "Vielleicht wirke ich so. Und vielleicht habe ich den Glauben an die Menschen verloren… aber ich habe diese Insel noch nicht ganz aufgegeben. Und ich hab so ein Gefühl bei dir, Junge. Nenn es Instinkt oder den Rum, aber ich glaube, du wirst auf Aeterna noch für ordentlich Wirbel sorgen. Ich will nur sichergehen, dass ich in der ersten Reihe sitze, wenn der Vorhang fällt."
|
||||
Audio: audio/dialog/silas/erstaunt_ruhe_textnpc.ogg
|
||||
|
||||
--- abschluss_letzte_worte ---
|
||||
HERO:
|
||||
Key: dialog.silas.abschluss_letzte_worte.textmainchar
|
||||
Text: "(Der Held macht sich auf den Weg)"
|
||||
Audio: audio/dialog/silas/abschluss_letzte_worte_textmainchar.ogg
|
||||
NPC:
|
||||
Key: dialog.silas.abschluss_letzte_worte.textnpc
|
||||
Text: "Jetzt geh schon. Bevor ich es mir anders überlege und dich doch noch für die Steuerjagd anmelde."
|
||||
Audio: audio/dialog/silas/abschluss_letzte_worte_textnpc.ogg
|
||||
|
||||
BIN
doc/Björnson.odt
Normal file
BIN
doc/Björnson.odt
Normal file
Binary file not shown.
Reference in New Issue
Block a user