Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,16 @@ public abstract class EntityScript<T extends IEntity> extends AbstractScript<T>

/// Called from the entity's loaded event after its environment is available.
/// @throws Exception if initialization fails.
protected void onLoaded() throws Exception { this.loaded(); }
protected void onLoaded() throws Exception {}

/// Called when the entity controller is detached or the entity is removed.
/// @throws Exception if cleanup fails.
protected void onUnloaded() throws Exception { this.unloaded(); }
protected void onUnloaded() throws Exception {}

/// Called for messages delivered to the attached entity.
/// @param event The message event.
/// @throws Exception if handling fails.
protected void onMessage(EntityMessageEvent event) throws Exception {
this.message(event);
if (event != null) {
this.onMessage(event.getMessage(), event.getSource());
}
Expand Down Expand Up @@ -137,28 +136,6 @@ public int getMaxHealth() {
return this.host() instanceof de.gurkenlabs.litiengine.entities.ICombatEntity combat ? combat.getHitPoints().getMax() : 0;
}

/// Legacy callback invoked after the entity environment becomes available.
///
/// @deprecated Override [#onLoaded()] in new scripts.
/// @throws Exception if handling fails.
@Deprecated
protected void loaded() throws Exception {}

/// Legacy callback invoked when the script is unloaded.
///
/// @deprecated Override [#onUnloaded()] in new scripts.
/// @throws Exception if handling fails.
@Deprecated
protected void unloaded() throws Exception {}

/// Legacy callback invoked when an entity message is received.
///
/// @param event The message event.
/// @throws Exception if handling fails.
/// @deprecated Override [#onMessage(EntityMessageEvent)] or [#onMessage(String, Object)] in new scripts.
@Deprecated
protected void message(EntityMessageEvent event) throws Exception {}

final void dispatchMessage(EntityMessageEvent event) throws Exception {
this.onMessage(event);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ public abstract class EnvironmentScript extends AbstractScript<Environment> {
@Override protected final void detached() throws Exception { this.onUnloaded(); }

/// Called after the environment has loaded and is the current world environment.
protected void onLoaded() throws Exception { this.loaded(); }
protected void onLoaded() throws Exception {}

/// Called when the environment binding is detached during unload.
protected void onUnloaded() throws Exception { this.unloaded(); }
protected void onUnloaded() throws Exception {}

/// Called when the attached environment is cleared while it remains active.
protected void onCleared() throws Exception {}
Expand All @@ -23,14 +23,6 @@ protected void onEntityAdded(IEntity entity) throws Exception {}
/// Called when an entity is removed from the attached environment.
protected void onEntityRemoved(IEntity entity) throws Exception {}

/// @deprecated Override [#onLoaded()] in new scripts.
@Deprecated
protected void loaded() throws Exception {}

/// @deprecated Override [#onUnloaded()] in new scripts.
@Deprecated
protected void unloaded() throws Exception {}

final void dispatchCleared() throws Exception {
this.onCleared();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ public abstract class GameScript extends AbstractScript<Object> {
@Override protected final void detached() throws Exception { this.onStopped(); }

/// Called after the binding enters the running game lifecycle.
protected void onStarted() throws Exception { this.started(); }
protected void onStarted() throws Exception {}

/// Called before the binding leaves the game lifecycle.
protected void onStopped() throws Exception { this.stopped(); }
protected void onStopped() throws Exception {}

/// Loads an environment map by map name.
public void loadMap(String mapName) {
Expand Down Expand Up @@ -57,11 +57,4 @@ public void exit() {
Game.terminate();
}

/// @deprecated Override [#onStarted()] in new scripts.
@Deprecated
protected void started() throws Exception {}

/// @deprecated Override [#onStopped()] in new scripts.
@Deprecated
protected void stopped() throws Exception {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import de.gurkenlabs.litiengine.ILaunchable;
import de.gurkenlabs.litiengine.IUpdateable;
import de.gurkenlabs.litiengine.entities.IEntity;
import de.gurkenlabs.litiengine.graphics.ICamera;
import de.gurkenlabs.litiengine.resources.Resources;
import de.gurkenlabs.litiengine.tweening.TweenFunction;
import java.awt.geom.Point2D;
Expand Down Expand Up @@ -46,7 +47,7 @@ public Thread newThread(Runnable r) {

private static final Logger log = Logger.getLogger(SoundEngine.class.getName());
private Point2D listenerLocation;
private UnaryOperator<Point2D> listenerLocationCallback = old -> Game.world().camera().getFocus();
private UnaryOperator<Point2D> listenerLocationCallback = SoundEngine::getCameraFocus;
private int maxDist = DEFAULT_MAX_DISTANCE;
private MusicPlayback music;
private final Collection<MusicPlayback> allMusic = ConcurrentHashMap.newKeySet();
Expand Down Expand Up @@ -497,7 +498,7 @@ public void setListenerLocationCallback(UnaryOperator<Point2D> callback) {

@Override
public void start() {
listenerLocation = Game.world().camera().getFocus();
listenerLocation = getCameraFocus(listenerLocation);
}

@Override
Expand Down Expand Up @@ -563,6 +564,13 @@ void addSound(SFXPlayback playback) {
this.sounds.add(playback);
}

private static Point2D getCameraFocus(Point2D fallback) {
ICamera camera = Game.world().camera();
return camera != null
? camera.getFocus()
: fallback != null ? fallback : new Point2D.Double(0, 0);
}

private SFXPlayback playSound(
Sound sound, Supplier<Point2D> supplier, boolean loop, int range, float volume) {
if (sound == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package de.gurkenlabs.litiengine;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -86,6 +87,15 @@ public void started() {
assertTrue(started.wasCalled);
}

@Test
void soundEngineUpdateToleratesMissingCamera() {
Game.terminate();
Game.init(Game.COMMANDLINE_ARG_NOGUI);
Game.world().setCamera(null);

assertDoesNotThrow(Game.audio()::update);
}

@Test
void vetoedExitPreparationDoesNotDetachRunningGameScripts() {
Game.terminate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand Down Expand Up @@ -57,6 +58,19 @@ void detachScripts() {
JavaEntityScript.reset();
}

@Test
void legacyLifecycleBridgeMethodsAreNotPartOfScriptApi() {
assertThrows(NoSuchMethodException.class, () -> EntityScript.class.getDeclaredMethod("loaded"));
assertThrows(NoSuchMethodException.class, () -> EntityScript.class.getDeclaredMethod("unloaded"));
assertThrows(
NoSuchMethodException.class,
() -> EntityScript.class.getDeclaredMethod("message", EntityMessageEvent.class));
assertThrows(NoSuchMethodException.class, () -> EnvironmentScript.class.getDeclaredMethod("loaded"));
assertThrows(NoSuchMethodException.class, () -> EnvironmentScript.class.getDeclaredMethod("unloaded"));
assertThrows(NoSuchMethodException.class, () -> GameScript.class.getDeclaredMethod("started"));
assertThrows(NoSuchMethodException.class, () -> GameScript.class.getDeclaredMethod("stopped"));
}

@Test
void bindingCodecPreservesOrderStateAndParameters() {
ScriptBinding second = new ScriptBinding("second");
Expand Down Expand Up @@ -585,9 +599,9 @@ public static final class JavaEntityScript extends EntityScript<TestEntity> {

public JavaEntityScript() {}

@Override protected void loaded() { loaded++; configuredSpeed = this.speed; }
@Override protected void unloaded() { unloaded++; }
@Override protected void message(EntityMessageEvent event) { messages++; }
@Override protected void onLoaded() { loaded++; configuredSpeed = this.speed; }
@Override protected void onUnloaded() { unloaded++; }
@Override protected void onMessage(EntityMessageEvent event) { messages++; }
@Override public void update() { updates++; }

static void reset() {
Expand Down Expand Up @@ -1265,7 +1279,7 @@ public static final class RecoverableFailScript extends EntityScript<TestEntity>
static int updateCount = 0;

@Override
protected void loaded() {
protected void onLoaded() {
if (shouldFailLoad) {
throw new RuntimeException("Simulated load failure");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import de.gurkenlabs.utiliti.view.components.SpritesheetImportPanel;
import de.gurkenlabs.utiliti.view.renderers.WorkspaceRenderer;
import de.gurkenlabs.utiliti.view.components.Tray;
import de.gurkenlabs.utiliti.view.components.Toast;
import de.gurkenlabs.utiliti.view.components.UI;
import de.gurkenlabs.utiliti.view.dialogs.ConfirmDialog;
import de.gurkenlabs.utiliti.view.dialogs.EditorFileChooser;
Expand Down Expand Up @@ -63,6 +64,7 @@
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
Expand All @@ -80,6 +82,7 @@ public class Editor extends Screen {
private static UserPreferences preferences;

private final List<Runnable> loadedCallbacks;
private final List<BiConsumer<Path, Path>> projectPathChangedCallbacks;

private final MapComponent mapComponent;
private ResourceBundle gameFile = new ResourceBundle();
Expand Down Expand Up @@ -107,6 +110,7 @@ private Editor() {
Game.scripts().setEnabled(false);
Game.physics().setEnabled(false);
this.loadedCallbacks = new CopyOnWriteArrayList<>();
this.projectPathChangedCallbacks = new CopyOnWriteArrayList<>();
this.mapComponent = new MapComponent();
this.mapComponent.onMapLoaded(map -> this.windowMetadataDirty.set(true));
}
Expand Down Expand Up @@ -361,6 +365,7 @@ public void shutdown() {
}

public void setProjectPath(Path projectPath) {
Path previousProjectPath = this.projectPath;
this.projectPath = projectPath;
this.currentResourceFile = projectPath;
this.projectModel = projectPath == null ? null : this.projectBuildService.resolve(projectPath);
Expand All @@ -370,9 +375,24 @@ public void setProjectPath(Path projectPath) {
Game.scripts().setProjectJavaVersion(Runtime.version().feature());
}
this.windowMetadataDirty.set(true);
if (!Objects.equals(previousProjectPath, projectPath)) {
for (BiConsumer<Path, Path> callback : this.projectPathChangedCallbacks) {
callback.accept(previousProjectPath, projectPath);
}
}
de.gurkenlabs.utiliti.view.components.UI.updateRunControlStates();
}

public void onProjectPathChanged(BiConsumer<Path, Path> callback) {
if (callback != null) {
this.projectPathChangedCallbacks.add(callback);
}
}

public void removeProjectPathChangedListener(BiConsumer<Path, Path> callback) {
this.projectPathChangedCallbacks.remove(callback);
}

static long buildConfigurationStamp(Path projectRoot) {
if (projectRoot == null) return 0;
List<Path> candidates = new ArrayList<>();
Expand Down Expand Up @@ -413,6 +433,10 @@ public void create() {
return;
}

if (!UI.notifyPendingChanges()) {
return;
}

if (Game.world().environment() != null) {
Game.world().unloadEnvironment();
}
Expand Down Expand Up @@ -454,6 +478,7 @@ public void create() {
}

this.setCurrentStatus(Resources.strings().get("status_project_created"));
Toast.show(Resources.strings().get("status_project_created"));
}

public void load() {
Expand Down Expand Up @@ -487,6 +512,7 @@ public void close(boolean force) {
UI.getAssetController().refresh();
}
this.setCurrentStatus(Resources.strings().get("status_gamefile_closed"));
Toast.show(Resources.strings().get("status_gamefile_closed"));
}

public void load(Path gameFile, boolean force) {
Expand Down Expand Up @@ -584,6 +610,7 @@ public void load(Path gameFile, boolean force) {

this.gamefileLoaded();
this.setCurrentStatus(Resources.strings().get("status_gamefile_loaded"));
Toast.show(Resources.strings().get("status_gamefile_loaded"));
} finally {
Cursors.apply(Cursors.DEFAULT);
log.log(Level.INFO, "Loading gamefile {0} took: {1} ms", new Object[] {gameFile, (System.nanoTime() - currentTime) / 1000000.0});
Expand Down Expand Up @@ -677,6 +704,7 @@ public void loadAsync(Path gameFile, boolean force, Runnable onComplete) {

this.gamefileLoaded();
this.setCurrentStatus(Resources.strings().get("status_gamefile_loaded"));
Toast.show(Resources.strings().get("status_gamefile_loaded"));
if (onComplete != null) {
onComplete.run();
}
Expand Down Expand Up @@ -1318,11 +1346,13 @@ private void saveGameFile(Path target) {
getCurrentResourceFile()
});
this.setCurrentStatus(Resources.strings().get("status_gamefile_saved"));
Toast.show(Resources.strings().get("status_gamefile_saved"));

this.saveMaps();
} catch (IOException e) {
log.log(Level.SEVERE, "Failed to save game file: " + e.getMessage(), e);
this.setCurrentStatus(Resources.strings().get("status_gamefile_save_error", e.getMessage()));
Toast.show(Resources.strings().get("status_gamefile_save_error", e.getMessage()));
}
}

Expand Down
Loading
Loading