Anzeige von Area und Location Namen korrigiert
This commit is contained in:
@@ -986,6 +986,7 @@ public class WorldScene extends BaseAppState {
|
||||
ambientSounds = new de.blight.game.state.AmbientSoundSystem();
|
||||
app.getStateManager().attach(ambientSounds);
|
||||
|
||||
app.getStateManager().attach(new de.blight.game.state.NameBannerState());
|
||||
musicSystem = new de.blight.game.state.MusicSystem();
|
||||
app.getStateManager().attach(musicSystem);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package de.blight.game.state;
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.state.BaseAppState;
|
||||
import com.jme3.math.Vector3f;
|
||||
import de.blight.common.LocationIO;
|
||||
import de.blight.common.model.Location;
|
||||
import de.blight.common.LocationZoneIO;
|
||||
import de.blight.common.PlacedLocationZone;
|
||||
import de.blight.common.model.MainCharacter;
|
||||
import de.blight.lang.TextResolver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -15,55 +16,95 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* Verfolgt die Spieler-Position und feuert Location-Trigger wenn der Charakter
|
||||
* eine Location betritt (Übergang outside→inside).
|
||||
* eine Location-Zone betritt (Übergang outside→inside). Zeigt den Location-Namen via NameBannerState an.
|
||||
*/
|
||||
public class LocationState extends BaseAppState {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LocationState.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(LocationState.class);
|
||||
private static final String PREFIX = "location.";
|
||||
|
||||
private final MainCharacter character;
|
||||
private List<Location> locations;
|
||||
private final Set<String> active = new HashSet<>();
|
||||
|
||||
/** Referenz auf die Node/Control, von der wir die Spieler-Position lesen. */
|
||||
private final MainCharacter character;
|
||||
private final com.jme3.scene.Node playerNode;
|
||||
|
||||
private Application app;
|
||||
private List<PlacedLocationZone> zones;
|
||||
private final Set<String> active = new HashSet<>();
|
||||
private boolean warmStart = true;
|
||||
|
||||
public LocationState(MainCharacter character, com.jme3.scene.Node playerNode) {
|
||||
this.character = character;
|
||||
this.playerNode = playerNode;
|
||||
}
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
protected void initialize(Application app) {
|
||||
protected void initialize(Application application) {
|
||||
app = application;
|
||||
try {
|
||||
locations = LocationIO.load();
|
||||
log.info("{} Location(s) geladen.", locations.size());
|
||||
zones = LocationZoneIO.load();
|
||||
log.info("{} Location-Zone(n) geladen.", zones.size());
|
||||
} catch (Exception e) {
|
||||
log.error("Locations nicht ladbar", e);
|
||||
locations = List.of();
|
||||
log.error("Location-Zones nicht ladbar", e);
|
||||
zones = List.of();
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void cleanup(Application application) {}
|
||||
@Override protected void onEnable() {}
|
||||
@Override protected void onDisable() {}
|
||||
|
||||
// ── Update ────────────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
if (locations.isEmpty() || playerNode == null) return;
|
||||
if (zones.isEmpty() || playerNode == null) return;
|
||||
Vector3f pos = playerNode.getWorldTranslation();
|
||||
float px = pos.x, pz = pos.z;
|
||||
|
||||
for (Location loc : locations) {
|
||||
boolean inside = loc.contains(px, pz);
|
||||
boolean wasInside = active.contains(loc.getId());
|
||||
if (inside && !wasInside) {
|
||||
active.add(loc.getId());
|
||||
loc.entered(character);
|
||||
log.debug("Location betreten: {}", loc.getId());
|
||||
} else if (!inside) {
|
||||
active.remove(loc.getId());
|
||||
for (PlacedLocationZone zone : zones) {
|
||||
String id = zone.nameId();
|
||||
boolean inside = pointInPolygon(px, pz, zone.pointsX(), zone.pointsZ());
|
||||
boolean wasInside = active.contains(id);
|
||||
if (inside) {
|
||||
active.add(id);
|
||||
if (!wasInside && !warmStart) {
|
||||
zone.triggers().stream()
|
||||
.filter(t -> t.isTriggarable(character))
|
||||
.forEach(t -> t.fire(character));
|
||||
showName(id);
|
||||
log.debug("Location betreten: {}", id);
|
||||
}
|
||||
} else {
|
||||
active.remove(id);
|
||||
}
|
||||
}
|
||||
warmStart = false;
|
||||
}
|
||||
|
||||
@Override protected void cleanup(Application app) {}
|
||||
@Override protected void onEnable() {}
|
||||
@Override protected void onDisable() {}
|
||||
// ── Name display ──────────────────────────────────────────────────────────
|
||||
|
||||
private void showName(String nameId) {
|
||||
String shortId = nameId.startsWith(PREFIX) ? nameId.substring(PREFIX.length()) : nameId;
|
||||
String key = nameId + ".name";
|
||||
String resolved = TextResolver.get().resolveOrId(key);
|
||||
String name = resolved.equals(key) ? shortId : resolved;
|
||||
NameBannerState banner = app.getStateManager().getState(NameBannerState.class);
|
||||
if (banner != null) banner.show(name, NameBannerState.PRIORITY_LOCATION);
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
private static boolean pointInPolygon(float px, float pz, float[] xs, float[] zs) {
|
||||
int n = xs.length;
|
||||
boolean inside = false;
|
||||
for (int i = 0, j = n - 1; i < n; j = i++) {
|
||||
float xi = xs[i], zi = zs[i];
|
||||
float xj = xs[j], zj = zs[j];
|
||||
if ((zi > pz) != (zj > pz) && (px < (xj - xi) * (pz - zi) / (zj - zi) + xi)) {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,12 @@ import com.jme3.asset.AssetManager;
|
||||
import com.jme3.audio.AudioData;
|
||||
import com.jme3.audio.AudioNode;
|
||||
import com.jme3.audio.AudioSource;
|
||||
import com.jme3.font.BitmapFont;
|
||||
import com.jme3.font.BitmapText;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
import de.blight.common.AreaDefinition;
|
||||
import de.blight.common.AreaDefinitionIO;
|
||||
import de.blight.common.AreaIO;
|
||||
import de.blight.common.PlacedArea;
|
||||
import de.blight.game.config.NinePatch;
|
||||
import de.blight.lang.TextResolver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -40,12 +33,9 @@ import java.util.Map;
|
||||
*/
|
||||
public class MusicSystem extends BaseAppState {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MusicSystem.class);
|
||||
private static final float CHECK_INTERVAL = 0.25f;
|
||||
private static final float FADE_DURATION = 3f;
|
||||
private static final float AREA_NAME_SHOW = 10f;
|
||||
private static final float AREA_NAME_FADE = 1.5f;
|
||||
private static final float AREA_NAME_SIZE = 24f;
|
||||
private static final Logger log = LoggerFactory.getLogger(MusicSystem.class);
|
||||
private static final float CHECK_INTERVAL = 0.25f;
|
||||
private static final float FADE_DURATION = 3f;
|
||||
|
||||
private static final int SLOT_DAY = 0;
|
||||
private static final int SLOT_NIGHT = 1;
|
||||
@@ -70,13 +60,6 @@ public class MusicSystem extends BaseAppState {
|
||||
private float checkTimer = 0f;
|
||||
private boolean isDaytime = true;
|
||||
|
||||
private BitmapText areaNameText;
|
||||
private Node areaNamePanel = null;
|
||||
private Material areaNamePanelMat = null;
|
||||
private float areaNameTimer = 0f;
|
||||
private boolean areaNameFadingIn = false;
|
||||
private float areaNameFadeInElapsed = 0f;
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
@@ -86,13 +69,6 @@ public class MusicSystem extends BaseAppState {
|
||||
rootNode = app.getRootNode();
|
||||
audioSettings = app.getStateManager().getState(AudioSettingsState.class);
|
||||
|
||||
BitmapFont font = assets.loadFont("Interface/Fonts/Default.fnt");
|
||||
areaNameText = new BitmapText(font);
|
||||
areaNameText.setSize(AREA_NAME_SIZE * 2f);
|
||||
areaNameText.setLocalScale(0.5f, 0.5f, 1f);
|
||||
areaNameText.setColor(new ColorRGBA(1f, 0.95f, 0.8f, 0f));
|
||||
app.getGuiNode().attachChild(areaNameText);
|
||||
|
||||
try {
|
||||
Map<String, AreaDefinition> defs = new java.util.HashMap<>();
|
||||
try {
|
||||
@@ -133,8 +109,6 @@ public class MusicSystem extends BaseAppState {
|
||||
phases.clear();
|
||||
activeSlot.clear();
|
||||
pendingSlot.clear();
|
||||
if (areaNamePanel != null) { app.getGuiNode().detachChild(areaNamePanel); areaNamePanel = null; }
|
||||
if (areaNameText != null) app.getGuiNode().detachChild(areaNameText);
|
||||
}
|
||||
|
||||
@Override protected void onEnable() {}
|
||||
@@ -160,25 +134,6 @@ public class MusicSystem extends BaseAppState {
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
// area name display: fade-in → visible → fade-out
|
||||
if (areaNameFadingIn) {
|
||||
areaNameFadeInElapsed += tpf;
|
||||
float alpha = Math.min(areaNameFadeInElapsed / AREA_NAME_FADE, 1f);
|
||||
setAreaNameAlpha(alpha);
|
||||
if (areaNameFadeInElapsed >= AREA_NAME_FADE) {
|
||||
areaNameFadingIn = false;
|
||||
setAreaNameAlpha(1f);
|
||||
}
|
||||
} else if (areaNameTimer > 0f) {
|
||||
areaNameTimer -= tpf;
|
||||
if (areaNameTimer <= 0f) {
|
||||
areaNameTimer = 0f;
|
||||
setAreaNameAlpha(0f);
|
||||
} else if (areaNameTimer < AREA_NAME_FADE) {
|
||||
setAreaNameAlpha(areaNameTimer / AREA_NAME_FADE);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.isEmpty()) return;
|
||||
|
||||
// per-area fade/finish handling + live volume update for active tracks
|
||||
@@ -304,47 +259,12 @@ public class MusicSystem extends BaseAppState {
|
||||
// ── Area-Name-Anzeige ─────────────────────────────────────────────────────
|
||||
|
||||
private void showAreaName(int i) {
|
||||
String key = i < areaNameKeys.size() ? areaNameKeys.get(i) : "";
|
||||
String shortId = i < areaShortIds.size() ? areaShortIds.get(i) : key;
|
||||
String key = i < areaNameKeys.size() ? areaNameKeys.get(i) : "";
|
||||
String shortId = i < areaShortIds.size() ? areaShortIds.get(i) : key;
|
||||
String resolved = TextResolver.get().resolveOrId(key);
|
||||
String name = resolved.equals(key) ? shortId : resolved;
|
||||
if (name.isBlank()) return;
|
||||
|
||||
areaNameText.setText(name);
|
||||
float tw = areaNameText.getLineWidth() * 0.5f;
|
||||
float th = areaNameText.getLineHeight() * 0.5f;
|
||||
float sw = app.getCamera().getWidth();
|
||||
float sh = app.getCamera().getHeight();
|
||||
|
||||
float padX = 20f, padY = 10f;
|
||||
float textX = Math.round((sw - tw) / 2f);
|
||||
float textY = sh - 40f;
|
||||
float panelX = Math.round(textX - padX);
|
||||
float panelY = Math.round(textY - th - padY);
|
||||
float panelW = Math.round(tw + padX * 2f);
|
||||
float panelH = Math.round(th + padY * 2f);
|
||||
|
||||
areaNameText.setLocalTranslation(textX, textY, 0f);
|
||||
|
||||
if (areaNamePanel != null) app.getGuiNode().detachChild(areaNamePanel);
|
||||
areaNamePanel = NinePatch.panel(assets).build(panelX, panelY, panelW, panelH, -1f);
|
||||
areaNamePanelMat = null;
|
||||
for (Spatial s : areaNamePanel.getChildren()) {
|
||||
if (s instanceof Geometry g) { areaNamePanelMat = g.getMaterial(); break; }
|
||||
}
|
||||
app.getGuiNode().attachChild(areaNamePanel);
|
||||
|
||||
setAreaNameAlpha(0f);
|
||||
areaNameFadingIn = true;
|
||||
areaNameFadeInElapsed = 0f;
|
||||
areaNameTimer = AREA_NAME_SHOW;
|
||||
}
|
||||
|
||||
private void setAreaNameAlpha(float alpha) {
|
||||
areaNameText.setColor(new ColorRGBA(1f, 0.95f, 0.8f, alpha));
|
||||
if (areaNamePanelMat != null) {
|
||||
areaNamePanelMat.setColor("Color", new ColorRGBA(1f, 1f, 1f, alpha));
|
||||
}
|
||||
NameBannerState banner = app.getStateManager().getState(NameBannerState.class);
|
||||
if (banner != null) banner.show(name, NameBannerState.PRIORITY_AREA);
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package de.blight.game.state;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.app.state.BaseAppState;
|
||||
import com.jme3.font.BitmapFont;
|
||||
import com.jme3.font.BitmapText;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
import de.blight.game.config.NinePatch;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* Zeigt einen Bereichs- oder Ortsnamen als eingeblendetes Banner (Fade-in → sichtbar → Fade-out).
|
||||
* Mehrere gleichzeitige Aufrufe werden in einer Priority-Queue gepuffert:
|
||||
* Priorität 0 = Area (zuerst), Priorität 1 = Location (danach).
|
||||
*/
|
||||
public class NameBannerState extends BaseAppState {
|
||||
|
||||
public static final int PRIORITY_AREA = 0;
|
||||
public static final int PRIORITY_LOCATION = 1;
|
||||
|
||||
private static final float SHOW_DURATION = 10f;
|
||||
private static final float FADE_DURATION = 1.5f;
|
||||
private static final float FONT_SIZE = 24f;
|
||||
|
||||
private record QueuedName(String name, int priority) {}
|
||||
|
||||
private SimpleApplication app;
|
||||
private BitmapText text;
|
||||
private Node panel = null;
|
||||
private Material panelMat = null;
|
||||
private float showTimer = 0f;
|
||||
private boolean fadingIn = false;
|
||||
private float fadeInElapsed = 0f;
|
||||
private final PriorityQueue<QueuedName> queue =
|
||||
new PriorityQueue<>(Comparator.comparingInt(QueuedName::priority));
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
protected void initialize(Application application) {
|
||||
app = (SimpleApplication) application;
|
||||
BitmapFont font = app.getAssetManager().loadFont("Interface/Fonts/Default.fnt");
|
||||
text = new BitmapText(font);
|
||||
text.setSize(FONT_SIZE * 2f);
|
||||
text.setLocalScale(0.5f, 0.5f, 1f);
|
||||
text.setColor(new ColorRGBA(1f, 0.95f, 0.8f, 0f));
|
||||
app.getGuiNode().attachChild(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup(Application application) {
|
||||
if (panel != null) { app.getGuiNode().detachChild(panel); panel = null; }
|
||||
if (text != null) app.getGuiNode().detachChild(text);
|
||||
}
|
||||
|
||||
@Override protected void onEnable() {}
|
||||
@Override protected void onDisable() {}
|
||||
|
||||
// ── Update ────────────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
if (fadingIn) {
|
||||
fadeInElapsed += tpf;
|
||||
setAlpha(Math.min(fadeInElapsed / FADE_DURATION, 1f));
|
||||
if (fadeInElapsed >= FADE_DURATION) {
|
||||
fadingIn = false;
|
||||
setAlpha(1f);
|
||||
}
|
||||
} else if (showTimer > 0f) {
|
||||
showTimer -= tpf;
|
||||
if (showTimer <= 0f) {
|
||||
showTimer = 0f;
|
||||
setAlpha(0f);
|
||||
if (!queue.isEmpty()) {
|
||||
showNow(queue.poll().name());
|
||||
}
|
||||
} else if (showTimer < FADE_DURATION) {
|
||||
setAlpha(showTimer / FADE_DURATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── API ───────────────────────────────────────────────────────────────────
|
||||
|
||||
public void show(String name, int priority) {
|
||||
if (name == null || name.isBlank()) return;
|
||||
if (!fadingIn && showTimer <= 0f) {
|
||||
showNow(name);
|
||||
} else {
|
||||
queue.offer(new QueuedName(name, priority));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Internals ─────────────────────────────────────────────────────────────
|
||||
|
||||
private void showNow(String name) {
|
||||
text.setText(name);
|
||||
float tw = text.getLineWidth() * 0.5f;
|
||||
float th = text.getLineHeight() * 0.5f;
|
||||
float sw = app.getCamera().getWidth();
|
||||
float sh = app.getCamera().getHeight();
|
||||
|
||||
float padX = 20f, padY = 10f;
|
||||
float textX = Math.round((sw - tw) / 2f);
|
||||
float textY = sh - 40f;
|
||||
|
||||
text.setLocalTranslation(textX, textY, 0f);
|
||||
|
||||
if (panel != null) app.getGuiNode().detachChild(panel);
|
||||
panel = NinePatch.panel(app.getAssetManager()).build(
|
||||
Math.round(textX - padX),
|
||||
Math.round(textY - th - padY),
|
||||
Math.round(tw + padX * 2f),
|
||||
Math.round(th + padY * 2f),
|
||||
-1f);
|
||||
panelMat = null;
|
||||
for (Spatial s : panel.getChildren()) {
|
||||
if (s instanceof Geometry g) { panelMat = g.getMaterial(); break; }
|
||||
}
|
||||
app.getGuiNode().attachChild(panel);
|
||||
|
||||
setAlpha(0f);
|
||||
fadingIn = true;
|
||||
fadeInElapsed = 0f;
|
||||
showTimer = SHOW_DURATION;
|
||||
}
|
||||
|
||||
private void setAlpha(float alpha) {
|
||||
text.setColor(new ColorRGBA(1f, 0.95f, 0.8f, alpha));
|
||||
if (panelMat != null) panelMat.setColor("Color", new ColorRGBA(1f, 1f, 1f, alpha));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user