diff --git a/assets/audio/life_reg.wav b/assets/audio/life_reg.wav new file mode 100644 index 0000000..9ef5f55 Binary files /dev/null and b/assets/audio/life_reg.wav differ diff --git a/assets/maps/mainmap.tmx b/assets/maps/mainmap.tmx index 62fd9df..7d25cdc 100644 --- a/assets/maps/mainmap.tmx +++ b/assets/maps/mainmap.tmx @@ -19,7 +19,7 @@ - + diff --git a/assets/maps/mystic.tiled-project b/assets/maps/mystic.tiled-project index 6b53d6a..bd56eb2 100644 --- a/assets/maps/mystic.tiled-project +++ b/assets/maps/mystic.tiled-project @@ -36,6 +36,16 @@ "type": "string", "value": "OBJECTS" }, + { + "name": "life", + "type": "int", + "value": 0 + }, + { + "name": "lifeReg", + "type": "float", + "value": 0 + }, { "name": "speed", "type": "float", diff --git a/assets/maps/objects.tsx b/assets/maps/objects.tsx index 6425ee0..cf0e1bd 100644 --- a/assets/maps/objects.tsx +++ b/assets/maps/objects.tsx @@ -4,6 +4,8 @@ + + diff --git a/assets/ui/skin.atlas b/assets/ui/skin.atlas index 6cb1c3b..4a49daa 100644 --- a/assets/ui/skin.atlas +++ b/assets/ui/skin.atlas @@ -6,21 +6,21 @@ filter: Nearest, Nearest repeat: none banner rotate: false - xy: 4, 602 + xy: 4, 626 size: 632, 164 orig: 632, 164 offset: 0, 0 index: -1 bar rotate: false - xy: 4, 461 + xy: 4, 485 size: 107, 8 orig: 107, 8 offset: 0, 0 index: -1 bar_frame rotate: false - xy: 644, 750 + xy: 644, 774 size: 130, 16 split: 3, 3, 3, 2 orig: 130, 16 @@ -28,7 +28,7 @@ bar_frame index: -1 button rotate: false - xy: 644, 726 + xy: 644, 750 size: 32, 16 split: 5, 5, 8, 6 orig: 32, 16 @@ -36,15 +36,50 @@ button index: -1 frame rotate: false - xy: 4, 477 + xy: 4, 501 size: 129, 117 split: 46, 49, 30, 54 orig: 129, 117 offset: 0, 0 index: -1 -selection +life_00 + rotate: false + xy: 782, 774 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +life_01 + rotate: false + xy: 4, 461 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +life_02 rotate: false xy: 141, 569 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +life_03 + rotate: false + xy: 173, 602 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +life_04 + rotate: false + xy: 644, 726 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +selection + rotate: false + xy: 141, 593 size: 24, 25 split: 8, 8, 9, 9 orig: 24, 25 diff --git a/assets/ui/skin.png b/assets/ui/skin.png index 8d66176..dd478e6 100644 Binary files a/assets/ui/skin.png and b/assets/ui/skin.png differ diff --git a/assets_raw/ui/life_00.png b/assets_raw/ui/life_00.png new file mode 100644 index 0000000..5c614dc Binary files /dev/null and b/assets_raw/ui/life_00.png differ diff --git a/assets_raw/ui/life_01.png b/assets_raw/ui/life_01.png new file mode 100644 index 0000000..f6f3b5d Binary files /dev/null and b/assets_raw/ui/life_01.png differ diff --git a/assets_raw/ui/life_02.png b/assets_raw/ui/life_02.png new file mode 100644 index 0000000..0f86309 Binary files /dev/null and b/assets_raw/ui/life_02.png differ diff --git a/assets_raw/ui/life_03.png b/assets_raw/ui/life_03.png new file mode 100644 index 0000000..09ee256 Binary files /dev/null and b/assets_raw/ui/life_03.png differ diff --git a/assets_raw/ui/life_04.png b/assets_raw/ui/life_04.png new file mode 100644 index 0000000..c9df14c Binary files /dev/null and b/assets_raw/ui/life_04.png differ diff --git a/core/src/main/java/io/github/com/quillraven/GdxGame.java b/core/src/main/java/io/github/com/quillraven/GdxGame.java index 02ff448..25ff9e2 100644 --- a/core/src/main/java/io/github/com/quillraven/GdxGame.java +++ b/core/src/main/java/io/github/com/quillraven/GdxGame.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.Application; import com.badlogic.gdx.Game; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputMultiplexer; +import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.Screen; import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver; import com.badlogic.gdx.graphics.GL20; @@ -117,8 +118,13 @@ public class GdxGame extends Game { return viewport; } - public InputMultiplexer getInputMultiplexer() { - return inputMultiplexer; + public void setInputProcessors(InputProcessor... processors) { + inputMultiplexer.clear(); + if (processors == null) return; + + for (InputProcessor processor : processors) { + inputMultiplexer.addProcessor(processor); + } } public AudioService getAudioService() { 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 f16a61d..7dc3e37 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 @@ -1,7 +1,9 @@ package io.github.com.quillraven.asset; public enum SoundAsset { - SWORD_HIT("sword_hit.wav"); + SWORD_HIT("sword_hit.wav"), + LIFE_REG("life_reg.wav"), + ; private final String path; diff --git a/core/src/main/java/io/github/com/quillraven/component/Life.java b/core/src/main/java/io/github/com/quillraven/component/Life.java new file mode 100644 index 0000000..d534b68 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/component/Life.java @@ -0,0 +1,35 @@ +package io.github.com.quillraven.component; + +import com.badlogic.ashley.core.Component; +import com.badlogic.ashley.core.ComponentMapper; +import com.badlogic.gdx.math.MathUtils; + +public class Life implements Component { + public static final ComponentMapper MAPPER = ComponentMapper.getFor(Life.class); + + private float maxLife; + private float life; + private float lifePerSec; + + public Life(int maxLife, float lifePerSec) { + this.maxLife = maxLife; + this.life = maxLife; + this.lifePerSec = lifePerSec; + } + + public float getMaxLife() { + return maxLife; + } + + public float getLife() { + return life; + } + + public void addLife(float value) { + this.life = MathUtils.clamp(life + value, 0f, maxLife); + } + + public float getLifePerSec() { + return lifePerSec; + } +} diff --git a/core/src/main/java/io/github/com/quillraven/component/Player.java b/core/src/main/java/io/github/com/quillraven/component/Player.java new file mode 100644 index 0000000..ea04f8f --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/component/Player.java @@ -0,0 +1,8 @@ +package io.github.com.quillraven.component; + +import com.badlogic.ashley.core.Component; +import com.badlogic.ashley.core.ComponentMapper; + +public class Player implements Component { + public static final ComponentMapper MAPPER = ComponentMapper.getFor(Player.class); +} 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 6acbd9b..bef3ea7 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 @@ -3,12 +3,18 @@ package io.github.com.quillraven.screen; import com.badlogic.ashley.core.Engine; import com.badlogic.ashley.core.EntitySystem; import com.badlogic.gdx.ScreenAdapter; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.World; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.utils.Disposable; +import com.badlogic.gdx.utils.viewport.FitViewport; +import com.badlogic.gdx.utils.viewport.Viewport; import io.github.com.quillraven.GdxGame; import io.github.com.quillraven.asset.MapAsset; +import io.github.com.quillraven.asset.SkinAsset; import io.github.com.quillraven.audio.AudioService; import io.github.com.quillraven.input.GameControllerState; import io.github.com.quillraven.input.KeyboardController; @@ -17,17 +23,24 @@ import io.github.com.quillraven.system.CameraSystem; import io.github.com.quillraven.system.ControllerSystem; import io.github.com.quillraven.system.FacingSystem; import io.github.com.quillraven.system.FsmSystem; +import io.github.com.quillraven.system.LifeSystem; 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.tiled.TiledService; +import io.github.com.quillraven.ui.model.GameViewModel; +import io.github.com.quillraven.ui.view.GameView; import java.util.function.Consumer; public class GameScreen extends ScreenAdapter { private final GdxGame game; + private final Stage stage; + private final Skin skin; + private final GameViewModel viewModel; + private final Viewport uiViewport; private final TiledService tiledService; private final Engine engine; private final TiledAshleySpawner tiledAshleySpawner; @@ -37,6 +50,10 @@ public class GameScreen extends ScreenAdapter { public GameScreen(GdxGame game) { this.game = game; + this.uiViewport = new FitViewport(320f, 180f); + this.stage = new Stage(uiViewport, game.getBatch()); + 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); @@ -50,6 +67,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 LifeSystem(this.viewModel)); this.engine.addSystem(new AnimationSystem(game.getAssetService())); this.engine.addSystem(new CameraSystem(game.getCamera())); this.engine.addSystem(new RenderSystem(game.getBatch(), game.getViewport(), game.getCamera())); @@ -59,10 +77,11 @@ public class GameScreen extends ScreenAdapter { @Override public void show() { - this.game.getInputMultiplexer().clear(); - this.game.getInputMultiplexer().addProcessor(keyboardController); + this.game.setInputProcessors(stage, keyboardController); keyboardController.setActiveState(GameControllerState.class); + 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; @@ -80,10 +99,21 @@ public class GameScreen extends ScreenAdapter { this.engine.removeAllEntities(); } + @Override + public void resize(int width, int height) { + super.resize(width, height); + uiViewport.update(width, height, true); + } + @Override public void render(float delta) { delta = Math.min(1 / 30f, delta); engine.update(delta); + + uiViewport.apply(); + stage.getBatch().setColor(Color.WHITE); + stage.act(delta); + stage.draw(); } @Override diff --git a/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java b/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java index c3b75c9..8fafc19 100644 --- a/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java +++ b/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java @@ -1,7 +1,7 @@ package io.github.com.quillraven.screen; -import com.badlogic.gdx.InputMultiplexer; import com.badlogic.gdx.ScreenAdapter; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.utils.viewport.FitViewport; @@ -20,7 +20,6 @@ public class MenuScreen extends ScreenAdapter { private final Stage stage; private final Skin skin; private final Viewport uiViewport; - private final InputMultiplexer inputMultiplexer; private final KeyboardController keyboardController; public MenuScreen(GdxGame game) { @@ -29,7 +28,6 @@ public class MenuScreen extends ScreenAdapter { this.stage = new Stage(uiViewport, game.getBatch()); this.skin = game.getAssetService().get(SkinAsset.DEFAULT); this.keyboardController = new KeyboardController(UiControllerState.class, null, stage); - this.inputMultiplexer = game.getInputMultiplexer(); } @Override @@ -39,9 +37,7 @@ public class MenuScreen extends ScreenAdapter { @Override public void show() { - this.inputMultiplexer.clear(); - this.inputMultiplexer.addProcessor(stage); - this.inputMultiplexer.addProcessor(keyboardController); + this.game.setInputProcessors(stage, keyboardController); this.stage.addActor(new MenuView(stage, skin, new MenuViewModel(game))); this.game.getAudioService().playMusic(MusicAsset.MENU); @@ -54,6 +50,8 @@ public class MenuScreen extends ScreenAdapter { @Override public void render(float delta) { + uiViewport.apply(); + stage.getBatch().setColor(Color.WHITE); stage.act(delta); stage.draw(); } diff --git a/core/src/main/java/io/github/com/quillraven/system/LifeSystem.java b/core/src/main/java/io/github/com/quillraven/system/LifeSystem.java new file mode 100644 index 0000000..c49f8ca --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/system/LifeSystem.java @@ -0,0 +1,52 @@ +package io.github.com.quillraven.system; + +import com.badlogic.ashley.core.Engine; +import com.badlogic.ashley.core.Entity; +import com.badlogic.ashley.core.EntityListener; +import com.badlogic.ashley.core.Family; +import com.badlogic.ashley.systems.IteratingSystem; +import io.github.com.quillraven.component.Life; +import io.github.com.quillraven.component.Player; +import io.github.com.quillraven.ui.model.GameViewModel; + +public class LifeSystem extends IteratingSystem implements EntityListener { + private final GameViewModel viewModel; + + public LifeSystem(GameViewModel viewModel) { + super(Family.all(Life.class).get()); + this.viewModel = viewModel; + } + + @Override + public void addedToEngine(Engine engine) { + super.addedToEngine(engine); + engine.addEntityListener(Family.all(Life.class, Player.class).get(), this); + } + + @Override + public void removedFromEngine(Engine engine) { + super.removedFromEngine(engine); + engine.removeEntityListener(this); + } + + @Override + public void entityAdded(Entity entity) { + Life life = Life.MAPPER.get(entity); + viewModel.updateLifeInfo(life.getMaxLife(), life.getLife()); + } + + @Override + public void entityRemoved(Entity entity) { + } + + @Override + protected void processEntity(Entity entity, float deltaTime) { + Life life = Life.MAPPER.get(entity); + if (life.getLife() == life.getMaxLife()) return; + + life.addLife(life.getLifePerSec() * deltaTime); + if (Player.MAPPER.get(entity) != null) { + viewModel.updateLifeInfo(life.getMaxLife(), life.getLife()); + } + } +} diff --git a/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java b/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java index f913175..1b6f7fe 100644 --- a/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java +++ b/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java @@ -35,8 +35,10 @@ import io.github.com.quillraven.component.Facing; import io.github.com.quillraven.component.Facing.FacingDirection; import io.github.com.quillraven.component.Fsm; import io.github.com.quillraven.component.Graphic; +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.Transform; public class TiledAshleySpawner { @@ -183,6 +185,8 @@ public class TiledAshleySpawner { addEntityMove(tile, entity); addEntityController(tileMapObject, entity); addEntityCameraFollow(tileMapObject, entity); + addEntityLife(tile, entity); + addEntityPlayer(tileMapObject, entity); entity.add(new Facing(FacingDirection.DOWN)); entity.add(new Fsm(entity)); entity.add(new Graphic(textureRegion, Color.WHITE.cpy())); @@ -190,6 +194,20 @@ public class TiledAshleySpawner { this.engine.addEntity(entity); } + private void addEntityPlayer(TiledMapTileMapObject tileMapObject, Entity entity) { + if ("Player".equals(tileMapObject.getName())) { + entity.add(new Player()); + } + } + + private void addEntityLife(TiledMapTile tile, Entity entity) { + int life = tile.getProperties().get("life", 0, Integer.class); + if (life == 0) return; + + float lifeReg = tile.getProperties().get("lifeReg", 0f, Float.class); + entity.add(new Life(life, lifeReg)); + } + private TextureRegion getTextureRegion(TiledMapTile tile) { String atlasAssetStr = tile.getProperties().get("atlasAsset", "OBJECTS", String.class); AtlasAsset atlasAsset = AtlasAsset.valueOf(atlasAssetStr); 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 new file mode 100644 index 0000000..2ae22c1 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/ui/model/GameViewModel.java @@ -0,0 +1,51 @@ +package io.github.com.quillraven.ui.model; + +import io.github.com.quillraven.GdxGame; +import io.github.com.quillraven.asset.SoundAsset; +import io.github.com.quillraven.audio.AudioService; + +public class GameViewModel extends ViewModel { + public static final String LIFE_POINTS = "lifePoints"; + public static final String MAX_LIFE = "maxLife"; + + private final AudioService audioService; + private int lifePoints; + private int maxLife; + + public GameViewModel(GdxGame game) { + super(game); + this.audioService = game.getAudioService(); + this.lifePoints = 0; + this.maxLife = 0; + } + + public void setMaxLife(int maxLife) { + if (this.maxLife != maxLife) { + this.propertyChangeSupport.firePropertyChange(MAX_LIFE, this.maxLife, maxLife); + } + this.maxLife = maxLife; + } + + public int getMaxLife() { + return maxLife; + } + + public void setLifePoints(int lifePoints) { + if (this.lifePoints != lifePoints) { + this.propertyChangeSupport.firePropertyChange(LIFE_POINTS, this.lifePoints, lifePoints); + if (this.lifePoints < lifePoints) { + audioService.playSound(SoundAsset.LIFE_REG); + } + } + this.lifePoints = lifePoints; + } + + public int getLifePoints() { + return lifePoints; + } + + public void updateLifeInfo(float maxLife, float life) { + setMaxLife((int) maxLife); + setLifePoints((int) life); + } +} diff --git a/core/src/main/java/io/github/com/quillraven/ui/model/ViewModel.java b/core/src/main/java/io/github/com/quillraven/ui/model/ViewModel.java index b75c760..7627fd5 100644 --- a/core/src/main/java/io/github/com/quillraven/ui/model/ViewModel.java +++ b/core/src/main/java/io/github/com/quillraven/ui/model/ViewModel.java @@ -2,7 +2,6 @@ package io.github.com.quillraven.ui.model; import io.github.com.quillraven.GdxGame; -import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; public abstract class ViewModel { @@ -14,7 +13,14 @@ public abstract class ViewModel { this.propertyChangeSupport = new PropertyChangeSupport(this); } - public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { - this.propertyChangeSupport.addPropertyChangeListener(propertyName, listener); + public void onPropertyChange(String propertyName, Class propType, OnPropertyChange consumer) { + this.propertyChangeSupport.addPropertyChangeListener(propertyName, evt -> + consumer.onChange(propType.cast(evt.getNewValue())) + ); + } + + @FunctionalInterface + public interface OnPropertyChange { + void onChange(T value); } } diff --git a/core/src/main/java/io/github/com/quillraven/ui/view/GameView.java b/core/src/main/java/io/github/com/quillraven/ui/view/GameView.java new file mode 100644 index 0000000..526a820 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/ui/view/GameView.java @@ -0,0 +1,49 @@ +package io.github.com.quillraven.ui.view; + +import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup; +import com.badlogic.gdx.scenes.scene2d.ui.Image; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.utils.Align; +import io.github.com.quillraven.ui.model.GameViewModel; + +public class GameView extends View { + private final HorizontalGroup lifeGroup; + + public GameView(Stage stage, Skin skin, GameViewModel viewModel) { + super(stage, skin, viewModel); + + this.lifeGroup = findActor("lifeGroup"); + updateLife(viewModel.getLifePoints()); + + viewModel.onPropertyChange(GameViewModel.LIFE_POINTS, Integer.class, this::updateLife); + } + + @Override + protected void setupUI() { + align(Align.bottomLeft); + setFillParent(true); + + HorizontalGroup horizontalGroup = new HorizontalGroup(); + horizontalGroup.setName("lifeGroup"); + horizontalGroup.padLeft(5.0f); + horizontalGroup.padBottom(5.0f); + horizontalGroup.space(5.0f); + add(horizontalGroup); + } + + private void updateLife(int lifePoints) { + lifeGroup.clear(); + + int maxLife = viewModel.getMaxLife(); + while (maxLife > 0) { + int imgIdx = MathUtils.clamp(lifePoints, 0, 4); + Image image = new Image(skin, "life_0" + imgIdx); + lifeGroup.addActor(image); + + maxLife -= 4; + lifePoints -= 4; + } + } +} diff --git a/sceneComposer.scmp b/sceneComposer.scmp index d167303..94385aa 100644 Binary files a/sceneComposer.scmp and b/sceneComposer.scmp differ