diff --git a/README.md b/README.md index 5ea3b69..aad5cf6 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,4 @@ https://kenmi-art.itch.io/cute-fantasy-rpg https://yubatake.bandcamp.com/album/jrpg-collection https://crusenho.itch.io/complete-ui-essential-pack https://xdeviruchi.itch.io/8-bit-fantasy-adventure-music-pack +https://sfxr.me/ diff --git a/assets/audio/trap.wav b/assets/audio/trap.wav new file mode 100644 index 0000000..2e962d0 Binary files /dev/null and b/assets/audio/trap.wav differ diff --git a/assets/graphics/objects.atlas b/assets/graphics/objects.atlas index c87c44d..cdd59d4 100644 --- a/assets/graphics/objects.atlas +++ b/assets/graphics/objects.atlas @@ -361,3 +361,17 @@ player/walk_up orig: 32, 32 offset: 0, 0 index: 5 +trap/idle_down + rotate: false + xy: 38, 13 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: 0 +trap/idle_down + rotate: false + xy: 410, 129 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: 1 diff --git a/assets/graphics/objects.png b/assets/graphics/objects.png index d945c67..a1d600d 100644 Binary files a/assets/graphics/objects.png and b/assets/graphics/objects.png differ diff --git a/assets/maps/mainmap.tmx b/assets/maps/mainmap.tmx index 7d25cdc..0d417d6 100644 --- a/assets/maps/mainmap.tmx +++ b/assets/maps/mainmap.tmx @@ -1,21 +1,27 @@ - + - + - eJylkjsPwjAMhLPykCoeEjDBxkgzARPqBEywla2w9lfkp3OVYumwTAgwfFIb+c7xOcE5twRzgxUIXzAyzkpQxO8t2Cn2hmYNesZ5ET1uoCXuoIo1G0PD/92sC3CI2gs4EWejb+c5i3l48gjkUxm6XHgm9vGZWslSPBpQOztbQe9BZ8rURr0g/bheMr0mPHl3rXvdAe/CU8ZWD00qK/F5kM8vezvG2fgO1rvJ9WJt+aFev+9/6YOBYvimNnW3MZgophn9n9wdbaQ= + eJylkjsOwjAQRF3z6SI+HVBDzwGIa6BGqYEa+tAHLpCz+G5MJK80Wm2MgeJJibUz6511cM6twdJgA8IXTIyzEhTxew8OiqOh2YKBcV5EjwdoiSeoYs3O0PB/N+sKnKL2Bi7E1ejbeS5iHp48AvlUhi4Xnol9fKZWshSPBtQ92Qp6DzpTpjbqBenH9ZLpPeHJu2vVDngXnjK2emhSWYnPi3x+2ds5zsZ3sN5Nrhdryw/1+n3/yxCMFOOe2tTdpmCmmGf0fwPl7aDo - eJxjYBgFo2AUjALKgTES24hIPQAWrABm + eJxjYBgFo2AUjALKgQcQC0DZ7kTqAQAhOACg - + + + + + + + @@ -29,5 +35,6 @@ + diff --git a/assets/maps/mystic.tiled-project b/assets/maps/mystic.tiled-project index bd56eb2..a107de7 100644 --- a/assets/maps/mystic.tiled-project +++ b/assets/maps/mystic.tiled-project @@ -30,6 +30,11 @@ "type": "string", "value": "" }, + { + "name": "animationSpeed", + "type": "float", + "value": 0 + }, { "name": "atlasAsset", "propertyType": "AtlasAsset", diff --git a/assets/maps/objects.tsx b/assets/maps/objects.tsx index cf0e1bd..ef674dd 100644 --- a/assets/maps/objects.tsx +++ b/assets/maps/objects.tsx @@ -1,9 +1,10 @@ - + + @@ -35,4 +36,11 @@ + + + + + + + diff --git a/assets/maps/objects/trap.png b/assets/maps/objects/trap.png new file mode 100644 index 0000000..655ce7e Binary files /dev/null and b/assets/maps/objects/trap.png differ diff --git a/assets/maps/tileset.png b/assets/maps/tileset.png index 6d57881..489a4cd 100644 Binary files a/assets/maps/tileset.png and b/assets/maps/tileset.png differ diff --git a/assets/maps/tileset.tsx b/assets/maps/tileset.tsx index c508304..04271e4 100644 --- a/assets/maps/tileset.tsx +++ b/assets/maps/tileset.tsx @@ -1,6 +1,6 @@ - - + + @@ -32,71 +32,79 @@ + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -105,21 +113,21 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/assets_raw/map/Water_Ripple_00.png b/assets_raw/map/Water_Ripple_00.png new file mode 100644 index 0000000..eb4cc12 Binary files /dev/null and b/assets_raw/map/Water_Ripple_00.png differ diff --git a/assets_raw/map/Water_Ripple_01.png b/assets_raw/map/Water_Ripple_01.png new file mode 100644 index 0000000..4e565ad Binary files /dev/null and b/assets_raw/map/Water_Ripple_01.png differ diff --git a/assets_raw/map/Water_Ripple_02.png b/assets_raw/map/Water_Ripple_02.png new file mode 100644 index 0000000..ee22e3d Binary files /dev/null and b/assets_raw/map/Water_Ripple_02.png differ diff --git a/assets_raw/map/Water_Ripple_03.png b/assets_raw/map/Water_Ripple_03.png new file mode 100644 index 0000000..3b2f515 Binary files /dev/null and b/assets_raw/map/Water_Ripple_03.png differ diff --git a/assets_raw/map/tileset.png b/assets_raw/map/tileset.png index f238bc2..a73a03b 100644 Binary files a/assets_raw/map/tileset.png and b/assets_raw/map/tileset.png differ diff --git a/assets_raw/objects/trap/idle_down_00.png b/assets_raw/objects/trap/idle_down_00.png new file mode 100644 index 0000000..655ce7e Binary files /dev/null and b/assets_raw/objects/trap/idle_down_00.png differ diff --git a/assets_raw/objects/trap/idle_down_01.png b/assets_raw/objects/trap/idle_down_01.png new file mode 100644 index 0000000..57babdb Binary files /dev/null and b/assets_raw/objects/trap/idle_down_01.png differ diff --git a/core/src/main/java/io/github/com/quillraven/asset/SoundAsset.java b/core/src/main/java/io/github/com/quillraven/asset/SoundAsset.java index 7dc3e37..805bd8c 100644 --- a/core/src/main/java/io/github/com/quillraven/asset/SoundAsset.java +++ b/core/src/main/java/io/github/com/quillraven/asset/SoundAsset.java @@ -3,6 +3,7 @@ package io.github.com.quillraven.asset; public enum SoundAsset { SWORD_HIT("sword_hit.wav"), LIFE_REG("life_reg.wav"), + TRAP("trap.wav"), ; private final String path; diff --git a/core/src/main/java/io/github/com/quillraven/component/Animation2D.java b/core/src/main/java/io/github/com/quillraven/component/Animation2D.java index bff88bf..151d3d3 100644 --- a/core/src/main/java/io/github/com/quillraven/component/Animation2D.java +++ b/core/src/main/java/io/github/com/quillraven/component/Animation2D.java @@ -68,10 +68,6 @@ public class Animation2D implements Component { return playMode; } - public float getSpeed() { - return speed; - } - public void setSpeed(float speed) { this.speed = speed; } diff --git a/core/src/main/java/io/github/com/quillraven/component/Tiled.java b/core/src/main/java/io/github/com/quillraven/component/Tiled.java new file mode 100644 index 0000000..10dc50b --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/component/Tiled.java @@ -0,0 +1,25 @@ +package io.github.com.quillraven.component; + +import com.badlogic.ashley.core.Component; +import com.badlogic.ashley.core.ComponentMapper; +import com.badlogic.gdx.maps.MapObject; + +public class Tiled implements Component { + public static final ComponentMapper MAPPER = ComponentMapper.getFor(Tiled.class); + + private final int id; + private final MapObject mapObjectRef; + + public Tiled(MapObject mapObjectRef) { + this.id = mapObjectRef.getProperties().get("id", -1, Integer.class); + this.mapObjectRef = mapObjectRef; + } + + public int getId() { + return id; + } + + public MapObject getMapObjectRef() { + return mapObjectRef; + } +} diff --git a/core/src/main/java/io/github/com/quillraven/component/Trigger.java b/core/src/main/java/io/github/com/quillraven/component/Trigger.java new file mode 100644 index 0000000..961e683 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/component/Trigger.java @@ -0,0 +1,29 @@ +package io.github.com.quillraven.component; + +import com.badlogic.ashley.core.Component; +import com.badlogic.ashley.core.ComponentMapper; +import com.badlogic.ashley.core.Entity; + +public class Trigger implements Component { + public static final ComponentMapper MAPPER = ComponentMapper.getFor(Trigger.class); + + private final String name; + private Entity triggeringEntity; + + public Trigger(String name) { + this.name = name; + this.triggeringEntity = null; + } + + public String getName() { + return name; + } + + public void setTriggeringEntity(Entity triggeringEntity) { + this.triggeringEntity = triggeringEntity; + } + + public Entity getTriggeringEntity() { + return triggeringEntity; + } +} diff --git a/core/src/main/java/io/github/com/quillraven/input/IdleControllerState.java b/core/src/main/java/io/github/com/quillraven/input/IdleControllerState.java index 428f4d9..c05362a 100644 --- a/core/src/main/java/io/github/com/quillraven/input/IdleControllerState.java +++ b/core/src/main/java/io/github/com/quillraven/input/IdleControllerState.java @@ -4,8 +4,4 @@ public class IdleControllerState implements ControllerState { @Override public void keyDown(Command command) { } - - @Override - public void keyUp(Command command) { - } } diff --git a/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java b/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java index bef3ea7..904118f 100644 --- a/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java +++ b/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java @@ -28,7 +28,8 @@ import io.github.com.quillraven.system.PhysicDebugRenderSystem; import io.github.com.quillraven.system.PhysicMoveSystem; import io.github.com.quillraven.system.PhysicSystem; import io.github.com.quillraven.system.RenderSystem; -import io.github.com.quillraven.tiled.TiledAshleySpawner; +import io.github.com.quillraven.system.TriggerSystem; +import io.github.com.quillraven.tiled.TiledAshleyConfigurator; import io.github.com.quillraven.tiled.TiledService; import io.github.com.quillraven.ui.model.GameViewModel; import io.github.com.quillraven.ui.view.GameView; @@ -43,7 +44,7 @@ public class GameScreen extends ScreenAdapter { private final Viewport uiViewport; private final TiledService tiledService; private final Engine engine; - private final TiledAshleySpawner tiledAshleySpawner; + private final TiledAshleyConfigurator tiledAshleyConfigurator; private final World physicWorld; private final KeyboardController keyboardController; private final AudioService audioService; @@ -55,11 +56,11 @@ public class GameScreen extends ScreenAdapter { this.skin = game.getAssetService().get(SkinAsset.DEFAULT); this.viewModel = new GameViewModel(game); this.audioService = game.getAudioService(); - this.tiledService = new TiledService(game.getAssetService()); this.physicWorld = new World(Vector2.Zero, true); this.physicWorld.setAutoClearForces(false); + this.tiledService = new TiledService(game.getAssetService(), this.physicWorld); this.engine = new Engine(); - this.tiledAshleySpawner = new TiledAshleySpawner(this.engine, this.physicWorld, this.game.getAssetService()); + this.tiledAshleyConfigurator = new TiledAshleyConfigurator(this.engine, this.physicWorld, this.game.getAssetService()); this.keyboardController = new KeyboardController(GameControllerState.class, engine, null); // add ECS systems @@ -67,6 +68,7 @@ public class GameScreen extends ScreenAdapter { this.engine.addSystem(new PhysicSystem(physicWorld, 1 / 60f)); this.engine.addSystem(new FacingSystem()); this.engine.addSystem(new FsmSystem()); + this.engine.addSystem(new TriggerSystem(audioService)); this.engine.addSystem(new LifeSystem(this.viewModel)); this.engine.addSystem(new AnimationSystem(game.getAssetService())); this.engine.addSystem(new CameraSystem(game.getCamera())); @@ -83,12 +85,12 @@ public class GameScreen extends ScreenAdapter { this.stage.addActor(new GameView(stage, skin, this.viewModel)); Consumer renderConsumer = this.engine.getSystem(RenderSystem.class)::setMap; - Consumer ashleySpawnerConsumer = this.tiledAshleySpawner::loadMapObjects; Consumer cameraConsumer = this.engine.getSystem(CameraSystem.class)::setMap; Consumer audioConsumer = this.audioService::setMap; - this.tiledService.setMapChangeConsumer( - renderConsumer.andThen(ashleySpawnerConsumer).andThen(cameraConsumer).andThen(audioConsumer) - ); + this.tiledService.setMapChangeConsumer(renderConsumer.andThen(cameraConsumer).andThen(audioConsumer)); + this.tiledService.setLoadTriggerConsumer(tiledAshleyConfigurator::onLoadTrigger); + this.tiledService.setLoadObjectConsumer(tiledAshleyConfigurator::onLoadObject); + this.tiledService.setLoadTileConsumer(tiledAshleyConfigurator::onLoadTile); TiledMap startMap = this.tiledService.loadMap(MapAsset.MAIN); this.tiledService.setMap(startMap); diff --git a/core/src/main/java/io/github/com/quillraven/system/PhysicSystem.java b/core/src/main/java/io/github/com/quillraven/system/PhysicSystem.java index a749aa9..6408911 100644 --- a/core/src/main/java/io/github/com/quillraven/system/PhysicSystem.java +++ b/core/src/main/java/io/github/com/quillraven/system/PhysicSystem.java @@ -7,11 +7,17 @@ import com.badlogic.ashley.core.Family; import com.badlogic.ashley.systems.IteratingSystem; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.physics.box2d.Body; +import com.badlogic.gdx.physics.box2d.Contact; +import com.badlogic.gdx.physics.box2d.ContactImpulse; +import com.badlogic.gdx.physics.box2d.ContactListener; +import com.badlogic.gdx.physics.box2d.Manifold; import com.badlogic.gdx.physics.box2d.World; import io.github.com.quillraven.component.Physic; +import io.github.com.quillraven.component.Player; import io.github.com.quillraven.component.Transform; +import io.github.com.quillraven.component.Trigger; -public class PhysicSystem extends IteratingSystem implements EntityListener { +public class PhysicSystem extends IteratingSystem implements EntityListener, ContactListener { private final World world; private final float interval; @@ -22,6 +28,7 @@ public class PhysicSystem extends IteratingSystem implements EntityListener { this.world = world; this.interval = interval; this.accumulator = 0f; + world.setContactListener(this); } @Override @@ -86,4 +93,42 @@ public class PhysicSystem extends IteratingSystem implements EntityListener { } } + @Override + public void beginContact(Contact contact) { + Object userDataA = contact.getFixtureA().getBody().getUserData(); + Object userDataB = contact.getFixtureB().getBody().getUserData(); + + if (!(userDataA instanceof Entity entityA) || !(userDataB instanceof Entity entityB)) { + return; + } + + playerTriggerContact(entityA, entityB); + } + + private static void playerTriggerContact(Entity entityA, Entity entityB) { + Trigger trigger = Trigger.MAPPER.get(entityA); + boolean isPlayer = Player.MAPPER.get(entityB) != null; + if (trigger != null && isPlayer) { + trigger.setTriggeringEntity(entityB); + return; + } + + trigger = Trigger.MAPPER.get(entityB); + isPlayer = Player.MAPPER.get(entityA) != null; + if (trigger != null && isPlayer) { + trigger.setTriggeringEntity(entityA); + } + } + + @Override + public void endContact(Contact contact) { + } + + @Override + public void preSolve(Contact contact, Manifold oldManifold) { + } + + @Override + public void postSolve(Contact contact, ContactImpulse impulse) { + } } diff --git a/core/src/main/java/io/github/com/quillraven/system/TriggerSystem.java b/core/src/main/java/io/github/com/quillraven/system/TriggerSystem.java new file mode 100644 index 0000000..e79f89d --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/system/TriggerSystem.java @@ -0,0 +1,75 @@ +package io.github.com.quillraven.system; + +import com.badlogic.ashley.core.Entity; +import com.badlogic.ashley.core.Family; +import com.badlogic.ashley.systems.IteratingSystem; +import com.badlogic.ashley.utils.ImmutableArray; +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.utils.GdxRuntimeException; +import com.badlogic.gdx.utils.Timer; +import io.github.com.quillraven.asset.SoundAsset; +import io.github.com.quillraven.audio.AudioService; +import io.github.com.quillraven.component.Animation2D; +import io.github.com.quillraven.component.Life; +import io.github.com.quillraven.component.Tiled; +import io.github.com.quillraven.component.Trigger; + +public class TriggerSystem extends IteratingSystem { + private final AudioService audioService; + + public TriggerSystem(AudioService audioService) { + super(Family.all(Trigger.class).get()); + this.audioService = audioService; + } + + @Override + protected void processEntity(Entity entity, float deltaTime) { + Trigger trigger = Trigger.MAPPER.get(entity); + if (trigger.getTriggeringEntity() == null) return; + + fireTrigger(trigger.getName(), trigger.getTriggeringEntity()); + trigger.setTriggeringEntity(null); + } + + private Entity getByTiledId(int tiledId) { + ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Tiled.class).get()); + for (Entity entity : entities) { + if (Tiled.MAPPER.get(entity).getId() == tiledId) { + return entity; + } + } + return null; + } + + private void fireTrigger(String triggerName, Entity triggeringEntity) { + switch (triggerName) { + case "trap_trigger" -> trapTrigger(triggeringEntity); + default -> throw new GdxRuntimeException("Unsupported trigger: " + triggerName); + } + } + + private void trapTrigger(Entity triggeringEntity) { + Entity trapEntity = getByTiledId(15); + if (trapEntity != null) { + // play trap animation + Animation2D animation2D = Animation2D.MAPPER.get(trapEntity); + animation2D.setSpeed(1f); + animation2D.setPlayMode(Animation.PlayMode.NORMAL); + audioService.playSound(SoundAsset.TRAP); + // reset animation + Timer.schedule(new Timer.Task() { + @Override + public void run() { + animation2D.setSpeed(0f); + animation2D.setType(Animation2D.AnimationType.IDLE); + } + }, 2.5f); + + // damage player + Life life = Life.MAPPER.get(triggeringEntity); + if (life.getLife() > 2) { + life.addLife(-2f); + } + } + } +} diff --git a/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java b/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleyConfigurator.java similarity index 63% rename from core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java rename to core/src/main/java/io/github/com/quillraven/tiled/TiledAshleyConfigurator.java index 1b6f7fe..4dc15b1 100644 --- a/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java +++ b/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleyConfigurator.java @@ -7,13 +7,10 @@ import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.FileTextureData; -import com.badlogic.gdx.maps.MapLayer; import com.badlogic.gdx.maps.MapObject; import com.badlogic.gdx.maps.MapObjects; import com.badlogic.gdx.maps.objects.RectangleMapObject; -import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.maps.tiled.TiledMapTile; -import com.badlogic.gdx.maps.tiled.TiledMapTileLayer; import com.badlogic.gdx.maps.tiled.objects.TiledMapTileMapObject; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; @@ -21,7 +18,6 @@ import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.physics.box2d.BodyDef; import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; import com.badlogic.gdx.physics.box2d.FixtureDef; -import com.badlogic.gdx.physics.box2d.PolygonShape; import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.utils.GdxRuntimeException; import io.github.com.quillraven.GdxGame; @@ -39,10 +35,12 @@ import io.github.com.quillraven.component.Life; import io.github.com.quillraven.component.Move; import io.github.com.quillraven.component.Physic; import io.github.com.quillraven.component.Player; +import io.github.com.quillraven.component.Tiled; import io.github.com.quillraven.component.Transform; +import io.github.com.quillraven.component.Trigger; -public class TiledAshleySpawner { - private static final Vector2 DEFAULT_SCALING = new Vector2(1f, 1f); +public class TiledAshleyConfigurator { + private static final Vector2 DEFAULT_PHYSIC_SCALING = new Vector2(1f, 1f); private final Engine engine; private final World physicWorld; @@ -50,7 +48,7 @@ public class TiledAshleySpawner { private final Vector2 tmpVec2; private final AssetService assetService; - public TiledAshleySpawner(Engine engine, World physicWorld, AssetService assetService) { + public TiledAshleyConfigurator(Engine engine, World physicWorld, AssetService assetService) { this.engine = engine; this.physicWorld = physicWorld; this.tmpMapObjects = new MapObjects(); @@ -58,120 +56,49 @@ public class TiledAshleySpawner { this.assetService = assetService; } - public void loadMapObjects(TiledMap tiledMap) { - for (MapLayer layer : tiledMap.getLayers()) { - if (layer instanceof TiledMapTileLayer tileLayer) { - loadTileLayer(tileLayer); - } else if ("objects".equals(layer.getName())) { - loadObjectLayer(layer); - } else if ("trigger".equals(layer.getName())) { - loadTriggerLayer(layer); - } - } - - spawnMapBoundary(tiledMap); + public void onLoadTile(TiledMapTile tile, float x, float y) { + createBody(tile.getObjects(), + new Vector2(x, y), + DEFAULT_PHYSIC_SCALING, + BodyDef.BodyType.StaticBody, + Vector2.Zero, + "environment"); } - private void spawnMapBoundary(TiledMap tiledMap) { - // create four boxes for the map boundary (left, right, bottom and top edge) - int width = tiledMap.getProperties().get("width", 0, Integer.class); - int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class); - int height = tiledMap.getProperties().get("height", 0, Integer.class); - int tileH = tiledMap.getProperties().get("tileheight", 0, Integer.class); - float mapW = width * tileW * GdxGame.UNIT_SCALE; - float mapH = height * tileH * GdxGame.UNIT_SCALE; - float halfW = mapW * 0.5f; - float halfH = mapH * 0.5f; - float boxThickness = 0.5f; - - BodyDef bodyDef = new BodyDef(); - bodyDef.type = BodyType.StaticBody; - bodyDef.position.setZero(); - bodyDef.fixedRotation = true; - Body body = physicWorld.createBody(bodyDef); - - // left edge - PolygonShape shape = new PolygonShape(); - shape.setAsBox(boxThickness, halfH, new Vector2(-boxThickness, halfH), 0f); - body.createFixture(shape, 0f).setFriction(0f); - shape.dispose(); - // right edge - shape = new PolygonShape(); - shape.setAsBox(boxThickness, halfH, new Vector2(mapW + boxThickness, halfH), 0f); - body.createFixture(shape, 0f).setFriction(0f); - shape.dispose(); - // bottom edge - shape = new PolygonShape(); - shape.setAsBox(halfW, boxThickness, new Vector2(halfW, -boxThickness), 0f); - body.createFixture(shape, 0f).setFriction(0f); - shape.dispose(); - // top edge - shape = new PolygonShape(); - shape.setAsBox(halfW, boxThickness, new Vector2(halfW, mapH + boxThickness), 0f); - body.createFixture(shape, 0f).setFriction(0f); - shape.dispose(); - } - - private void loadTileLayer(TiledMapTileLayer tileLayer) { - for (int y = 0; y < tileLayer.getHeight(); y++) { - for (int x = 0; x < tileLayer.getWidth(); x++) { - TiledMapTileLayer.Cell cell = tileLayer.getCell(x, y); - if (cell == null) continue; - - TiledMapTile tile = cell.getTile(); - createBody(tile.getObjects(), - new Vector2(x, y), - DEFAULT_SCALING, - BodyType.StaticBody, - Vector2.Zero, - "environment"); - } + public void onLoadTrigger(String triggerName, MapObject mapObject) { + if (mapObject instanceof RectangleMapObject rectMapObj) { + Entity entity = this.engine.createEntity(); + Rectangle rect = rectMapObj.getRectangle(); + addEntityTransform( + rect.getX(), rect.getY(), 0, + rect.getWidth(), rect.getHeight(), + 1f, 1f, + 0, + entity); + addEntityPhysic( + rectMapObj, + BodyDef.BodyType.StaticBody, + tmpVec2.set(rect.getX(), rect.getY()).scl(GdxGame.UNIT_SCALE), + entity); + entity.add(new Trigger(triggerName)); + entity.add(new Tiled(rectMapObj)); + this.engine.addEntity(entity); + } else { + throw new GdxRuntimeException("Unsupported map object type for trigger: " + mapObject.getClass().getSimpleName()); } } - private void loadTriggerLayer(MapLayer triggerLayer) { - for (MapObject mapObject : triggerLayer.getObjects()) { - if (mapObject instanceof RectangleMapObject rectMapObj) { - Entity entity = this.engine.createEntity(); - Rectangle rect = rectMapObj.getRectangle(); - addEntityTransform( - rect.getX(), rect.getY(), - rect.getWidth(), rect.getHeight(), - 1f, 1f, - 0, - entity); - addEntityPhysic( - rectMapObj, - BodyType.StaticBody, - tmpVec2.set(rect.getX(), rect.getY()).scl(GdxGame.UNIT_SCALE), - entity); - this.engine.addEntity(entity); - } else { - throw new GdxRuntimeException("Unsupported trigger: " + mapObject.getClass().getSimpleName()); - } - } - } - - private void loadObjectLayer(MapLayer objectLayer) { - for (MapObject mapObject : objectLayer.getObjects()) { - if (mapObject instanceof TiledMapTileMapObject tileMapObject) { - spawnEntityOf(tileMapObject); - } else { - throw new GdxRuntimeException("Unsupported object: " + mapObject.getClass().getSimpleName()); - } - } - } - - private void spawnEntityOf(TiledMapTileMapObject tileMapObject) { + public void onLoadObject(TiledMapTileMapObject tileMapObject) { Entity entity = this.engine.createEntity(); TiledMapTile tile = tileMapObject.getTile(); TextureRegion textureRegion = getTextureRegion(tile); String classType = tile.getProperties().get("type", "", String.class); float sortOffsetY = tile.getProperties().get("sortOffsetY", 0, Integer.class); sortOffsetY *= GdxGame.UNIT_SCALE; + int z = tile.getProperties().get("z", 1, Integer.class); addEntityTransform( - tileMapObject.getX(), tileMapObject.getY(), + tileMapObject.getX(), tileMapObject.getY(), z, textureRegion.getRegionWidth(), textureRegion.getRegionHeight(), tileMapObject.getScaleX(), tileMapObject.getScaleY(), sortOffsetY, @@ -190,6 +117,7 @@ public class TiledAshleySpawner { entity.add(new Facing(FacingDirection.DOWN)); entity.add(new Fsm(entity)); entity.add(new Graphic(textureRegion, Color.WHITE.cpy())); + entity.add(new Tiled(tileMapObject)); this.engine.addEntity(entity); } @@ -258,8 +186,9 @@ public class TiledAshleySpawner { AtlasAsset atlasAsset = AtlasAsset.valueOf(atlasAssetStr); FileTextureData textureData = (FileTextureData) tile.getTextureRegion().getTexture().getTextureData(); String atlasKey = textureData.getFileHandle().nameWithoutExtension(); + float speed = tile.getProperties().get("animationSpeed", 0f, Float.class); - entity.add(new Animation2D(atlasAsset, atlasKey, animationType, Animation.PlayMode.LOOP, 1f)); + entity.add(new Animation2D(atlasAsset, atlasKey, animationType, Animation.PlayMode.LOOP, speed)); } private void addEntityPhysic(MapObject mapObject, @SuppressWarnings("SameParameterValue") BodyType bodyType, Vector2 relativeTo, Entity entity) { @@ -305,7 +234,7 @@ public class TiledAshleySpawner { } private static void addEntityTransform( - float x, float y, + float x, float y, int z, float w, float h, float scaleX, float scaleY, float sortOffsetY, @@ -318,7 +247,7 @@ public class TiledAshleySpawner { position.scl(GdxGame.UNIT_SCALE); size.scl(GdxGame.UNIT_SCALE); - entity.add(new Transform(position, 0, size, scaling, 0f, sortOffsetY)); + entity.add(new Transform(position, z, size, scaling, 0f, sortOffsetY)); } } diff --git a/core/src/main/java/io/github/com/quillraven/tiled/TiledService.java b/core/src/main/java/io/github/com/quillraven/tiled/TiledService.java index f8b077a..6cf4d83 100644 --- a/core/src/main/java/io/github/com/quillraven/tiled/TiledService.java +++ b/core/src/main/java/io/github/com/quillraven/tiled/TiledService.java @@ -1,19 +1,43 @@ package io.github.com.quillraven.tiled; +import com.badlogic.gdx.maps.MapLayer; +import com.badlogic.gdx.maps.MapObject; +import com.badlogic.gdx.maps.objects.RectangleMapObject; import com.badlogic.gdx.maps.tiled.TiledMap; +import com.badlogic.gdx.maps.tiled.TiledMapTile; +import com.badlogic.gdx.maps.tiled.TiledMapTileLayer; +import com.badlogic.gdx.maps.tiled.objects.TiledMapTileMapObject; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.Body; +import com.badlogic.gdx.physics.box2d.BodyDef; +import com.badlogic.gdx.physics.box2d.PolygonShape; +import com.badlogic.gdx.physics.box2d.World; +import com.badlogic.gdx.utils.GdxRuntimeException; +import io.github.com.quillraven.GdxGame; import io.github.com.quillraven.asset.AssetService; import io.github.com.quillraven.asset.MapAsset; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class TiledService { private final AssetService assetService; - private Consumer mapChangeConsumer; + private final World physicWorld; + private TiledMap currentMap; - public TiledService(AssetService assetService) { + private Consumer mapChangeConsumer; + private BiConsumer loadTriggerConsumer; + private Consumer loadObjectConsumer; + private LoadTileConsumer loadTileConsumer; + + public TiledService(AssetService assetService, World physicWorld) { this.assetService = assetService; + this.physicWorld = physicWorld; this.mapChangeConsumer = null; + this.loadTriggerConsumer = null; + this.loadObjectConsumer = null; + this.loadTileConsumer = null; this.currentMap = null; } @@ -29,6 +53,7 @@ public class TiledService { } this.currentMap = tiledMap; + loadMapObjects(tiledMap); if (this.mapChangeConsumer != null) { this.mapChangeConsumer.accept(tiledMap); } @@ -37,4 +62,116 @@ public class TiledService { public void setMapChangeConsumer(Consumer mapChangeConsumer) { this.mapChangeConsumer = mapChangeConsumer; } + + public void setLoadObjectConsumer(Consumer loadObjectConsumer) { + this.loadObjectConsumer = loadObjectConsumer; + } + + public void setLoadTriggerConsumer(BiConsumer loadTriggerConsumer) { + this.loadTriggerConsumer = loadTriggerConsumer; + } + + public void setLoadTileConsumer(LoadTileConsumer loadTileConsumer) { + this.loadTileConsumer = loadTileConsumer; + } + + public void loadMapObjects(TiledMap tiledMap) { + for (MapLayer layer : tiledMap.getLayers()) { + if (layer instanceof TiledMapTileLayer tileLayer) { + loadTileLayer(tileLayer); + } else if ("objects".equals(layer.getName())) { + loadObjectLayer(layer); + } else if ("trigger".equals(layer.getName())) { + loadTriggerLayer(layer); + } + } + + spawnMapBoundary(tiledMap); + } + + private void spawnMapBoundary(TiledMap tiledMap) { + // create four boxes for the map boundary (left, right, bottom and top edge) + int width = tiledMap.getProperties().get("width", 0, Integer.class); + int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class); + int height = tiledMap.getProperties().get("height", 0, Integer.class); + int tileH = tiledMap.getProperties().get("tileheight", 0, Integer.class); + float mapW = width * tileW * GdxGame.UNIT_SCALE; + float mapH = height * tileH * GdxGame.UNIT_SCALE; + float halfW = mapW * 0.5f; + float halfH = mapH * 0.5f; + float boxThickness = 0.5f; + + BodyDef bodyDef = new BodyDef(); + bodyDef.type = BodyDef.BodyType.StaticBody; + bodyDef.position.setZero(); + bodyDef.fixedRotation = true; + Body body = physicWorld.createBody(bodyDef); + + // left edge + PolygonShape shape = new PolygonShape(); + shape.setAsBox(boxThickness, halfH, new Vector2(-boxThickness, halfH), 0f); + body.createFixture(shape, 0f).setFriction(0f); + shape.dispose(); + // right edge + shape = new PolygonShape(); + shape.setAsBox(boxThickness, halfH, new Vector2(mapW + boxThickness, halfH), 0f); + body.createFixture(shape, 0f).setFriction(0f); + shape.dispose(); + // bottom edge + shape = new PolygonShape(); + shape.setAsBox(halfW, boxThickness, new Vector2(halfW, -boxThickness), 0f); + body.createFixture(shape, 0f).setFriction(0f); + shape.dispose(); + // top edge + shape = new PolygonShape(); + shape.setAsBox(halfW, boxThickness, new Vector2(halfW, mapH + boxThickness), 0f); + body.createFixture(shape, 0f).setFriction(0f); + shape.dispose(); + } + + private void loadTileLayer(TiledMapTileLayer tileLayer) { + if (loadTileConsumer == null) return; + + for (int y = 0; y < tileLayer.getHeight(); y++) { + for (int x = 0; x < tileLayer.getWidth(); x++) { + TiledMapTileLayer.Cell cell = tileLayer.getCell(x, y); + if (cell == null) continue; + + loadTileConsumer.accept(cell.getTile(), x, y); + } + } + } + + private void loadTriggerLayer(MapLayer triggerLayer) { + if (loadTriggerConsumer == null) return; + + for (MapObject mapObject : triggerLayer.getObjects()) { + if (mapObject.getName() == null || mapObject.getName().isBlank()) { + throw new GdxRuntimeException("Trigger must have a name: " + mapObject); + } + + if (mapObject instanceof RectangleMapObject rectMapObj) { + loadTriggerConsumer.accept(mapObject.getName(), rectMapObj); + } else { + throw new GdxRuntimeException("Unsupported trigger: " + mapObject.getClass().getSimpleName()); + } + } + } + + private void loadObjectLayer(MapLayer objectLayer) { + if (loadObjectConsumer == null) return; + + for (MapObject mapObject : objectLayer.getObjects()) { + if (mapObject instanceof TiledMapTileMapObject tileMapObject) { + loadObjectConsumer.accept(tileMapObject); + } else { + throw new GdxRuntimeException("Unsupported object: " + mapObject.getClass().getSimpleName()); + } + } + } + + @FunctionalInterface + public interface LoadTileConsumer { + void accept(TiledMapTile tile, float x, float y); + } } diff --git a/core/src/main/java/io/github/com/quillraven/ui/model/GameViewModel.java b/core/src/main/java/io/github/com/quillraven/ui/model/GameViewModel.java index 2ae22c1..841d200 100644 --- a/core/src/main/java/io/github/com/quillraven/ui/model/GameViewModel.java +++ b/core/src/main/java/io/github/com/quillraven/ui/model/GameViewModel.java @@ -33,7 +33,7 @@ public class GameViewModel extends ViewModel { public void setLifePoints(int lifePoints) { if (this.lifePoints != lifePoints) { this.propertyChangeSupport.firePropertyChange(LIFE_POINTS, this.lifePoints, lifePoints); - if (this.lifePoints < lifePoints) { + if (this.lifePoints != 0 && this.lifePoints < lifePoints) { audioService.playSound(SoundAsset.LIFE_REG); } }