diff --git a/assets/abilities/cards/explotionCard.png b/assets/abilities/cards/explotionCard.png new file mode 100644 index 00000000..ef33353e Binary files /dev/null and b/assets/abilities/cards/explotionCard.png differ diff --git a/assets/abilities/cards/firetrailCard.png b/assets/abilities/cards/firetrailCard.png new file mode 100644 index 00000000..e416768c Binary files /dev/null and b/assets/abilities/cards/firetrailCard.png differ diff --git a/assets/abilities/cards/guardCard.png b/assets/abilities/cards/guardCard.png new file mode 100644 index 00000000..e978389e Binary files /dev/null and b/assets/abilities/cards/guardCard.png differ diff --git a/assets/abilities/cards/mirrorCard.png b/assets/abilities/cards/mirrorCard.png new file mode 100644 index 00000000..7443de62 Binary files /dev/null and b/assets/abilities/cards/mirrorCard.png differ diff --git a/assets/abilities/cards/shieldCard.png b/assets/abilities/cards/shieldCard.png new file mode 100644 index 00000000..9ed26f4a Binary files /dev/null and b/assets/abilities/cards/shieldCard.png differ diff --git a/assets/abilities/explosion-card-temp.png b/assets/abilities/explosion-card-temp.png new file mode 100644 index 00000000..b1befe02 Binary files /dev/null and b/assets/abilities/explosion-card-temp.png differ diff --git a/assets/abilities/explosion-card.png b/assets/abilities/explosion-card.png new file mode 100644 index 00000000..d7f8c40d Binary files /dev/null and b/assets/abilities/explosion-card.png differ diff --git a/assets/abilities/mirror-card.png b/assets/abilities/mirror-card.png new file mode 100644 index 00000000..6379f6eb Binary files /dev/null and b/assets/abilities/mirror-card.png differ diff --git a/assets/abilities/new_movement-card.png b/assets/abilities/new_movement-card.png new file mode 100644 index 00000000..6379f6eb Binary files /dev/null and b/assets/abilities/new_movement-card.png differ diff --git a/assets/abilities/shield-card.png b/assets/abilities/shield-card.png new file mode 100644 index 00000000..6379f6eb Binary files /dev/null and b/assets/abilities/shield-card.png differ diff --git a/assets/abilities/swap-card.png b/assets/abilities/swap-card.png new file mode 100644 index 00000000..6379f6eb Binary files /dev/null and b/assets/abilities/swap-card.png differ diff --git a/assets/skin/plain-james-ui.json b/assets/skin/plain-james-ui.json index b4d7349f..df70bf7b 100644 --- a/assets/skin/plain-james-ui.json +++ b/assets/skin/plain-james-ui.json @@ -108,7 +108,8 @@ com.badlogic.gdx.scenes.scene2d.ui.ImageButton$ImageButtonStyle: { over: round-light-gray checked: round-dark-gray checkedOver: round-light-gray - } + }, + } com.badlogic.gdx.scenes.scene2d.ui.ImageTextButton$ImageTextButtonStyle: { default: { @@ -255,4 +256,4 @@ com.badlogic.gdx.scenes.scene2d.ui.Window$WindowStyle: { stageBackground: white } } -} \ No newline at end of file +} diff --git a/chessevolved_model/src/main/kotlin/io/github/chessevolved/components/AbilityCardComponent.kt b/chessevolved_model/src/main/kotlin/io/github/chessevolved/components/AbilityCardComponent.kt new file mode 100644 index 00000000..85c01b03 --- /dev/null +++ b/chessevolved_model/src/main/kotlin/io/github/chessevolved/components/AbilityCardComponent.kt @@ -0,0 +1,17 @@ +package io.github.chessevolved.components +import com.badlogic.ashley.core.Component +import com.badlogic.ashley.core.ComponentMapper + +private object CurrentCardIdCounter { + var count = 0 +} + +class AbilityCardComponent : Component { + companion object { + val mapper: ComponentMapper = + ComponentMapper.getFor(AbilityCardComponent::class.java) + } + + val id = CurrentCardIdCounter.count++ + var isInInventory = false +} diff --git a/chessevolved_model/src/main/kotlin/io/github/chessevolved/entities/AbilityItemFactory.kt b/chessevolved_model/src/main/kotlin/io/github/chessevolved/entities/AbilityItemFactory.kt new file mode 100644 index 00000000..0c9b5bfa --- /dev/null +++ b/chessevolved_model/src/main/kotlin/io/github/chessevolved/entities/AbilityItemFactory.kt @@ -0,0 +1,55 @@ +package io.github.chessevolved.entities + +import com.badlogic.ashley.core.Engine +import com.badlogic.ashley.core.Entity +import com.badlogic.gdx.assets.AssetManager +import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.TextureRegion +import io.github.chessevolved.components.AbilityCardComponent +import io.github.chessevolved.components.AbilityComponent +import io.github.chessevolved.components.TextureRegionComponent +import io.github.chessevolved.enums.AbilityType + +class AbilityItemFactory( + private val engine: Engine, + private val assetManager: AssetManager, +) { + private fun getAbilityItemTexture(abilityType: AbilityType): TextureRegion = + when (abilityType) { + AbilityType.SHIELD -> TextureRegion(assetManager.get("abilities/cards/shieldCard.png", Texture::class.java)) + AbilityType.EXPLOSION -> TextureRegion(assetManager.get("abilities/cards/explosionCard.png", Texture::class.java)) + // TODO: update all cards + AbilityType.SWAP -> TextureRegion(assetManager.get("abilities/swap-card.png", Texture::class.java)) + AbilityType.MIRROR -> TextureRegion(assetManager.get("abilities/cards/mirrorCard.png", Texture::class.java)) + AbilityType.NEW_MOVEMENT -> TextureRegion(assetManager.get("abilities/new_movement-card.png", Texture::class.java)) + } + + fun createAbilityItem(abilityType: AbilityType): Entity { + var cooldown: Int = 3 + + when (abilityType) { + AbilityType.SHIELD -> { + cooldown = 3 + } + AbilityType.EXPLOSION -> { + cooldown = 2 + } + AbilityType.SWAP -> { + cooldown = 2 + } + AbilityType.MIRROR -> { + cooldown = 3 + } + AbilityType.NEW_MOVEMENT -> { + cooldown = 0 + } + } + + return Entity().apply { + add(AbilityComponent(abilityType, cooldown, 0)) + add(AbilityCardComponent()) + add(TextureRegionComponent(getAbilityItemTexture(abilityType))) + engine.addEntity(this) + } + } +} diff --git a/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/InputSystem.kt b/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/InputSystem.kt index c0202388..598e5b37 100644 --- a/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/InputSystem.kt +++ b/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/InputSystem.kt @@ -3,6 +3,8 @@ package io.github.chessevolved.systems import com.badlogic.ashley.core.Entity import com.badlogic.ashley.core.Family import com.badlogic.ashley.systems.IteratingSystem +import io.github.chessevolved.components.AbilityCardComponent +import io.github.chessevolved.components.AbilityComponent import io.github.chessevolved.components.CanBeCapturedComponent import io.github.chessevolved.components.CapturedComponent import io.github.chessevolved.components.ClickEventComponent @@ -26,6 +28,8 @@ class InputSystem : handlePieceClicked(entity) } else if (entity?.getComponent(WeatherEventComponent::class.java) != null) { handleBoardSquareClicked(entity) + } else if (entity?.getComponent(AbilityCardComponent::class.java) != null) { + handleAbilityCardClicked(entity) } entity?.remove(ClickEventComponent::class.java) @@ -33,16 +37,40 @@ class InputSystem : private fun handlePieceClicked(piece: Entity) { val selectionComponent = piece.getComponent(SelectionComponent::class.java) - val selectedPiece = engine.getEntitiesFor(Family.all(SelectionComponent::class.java).get()).firstOrNull() + val selectedEntity = engine.getEntitiesFor(Family.all(SelectionComponent::class.java).get()).firstOrNull() val canBeCapturedComponent = piece.getComponent(CanBeCapturedComponent::class.java) if (selectionComponent != null) { piece.remove(SelectionComponent::class.java) - } else if (selectedPiece != null && canBeCapturedComponent != null) { + } else if (selectedEntity != null && canBeCapturedComponent != null) { piece.add(CapturedComponent()) } else { - selectedPiece?.remove(SelectionComponent::class.java) - piece.add(SelectionComponent()) + if (selectedEntity != null && + AbilityCardComponent.mapper.get(selectedEntity) != null && + AbilityCardComponent.mapper.get(selectedEntity).isInInventory + ) { + if (AbilityComponent.mapper.get(piece) != null) { + println("Already has ability") + } else { + val abilityComponent = AbilityComponent.mapper.get(selectedEntity) + + if (abilityComponent != null) { + piece.add( + AbilityComponent( + abilityComponent.ability, + abilityComponent.abilityCooldownTime, + abilityComponent.currentAbilityCDTime, + ), + ) + } + + println("Ability got applied to piece!") + selectedEntity.removeAll() // Remove abilityCard-entity from the game. + } + } else { + selectedEntity?.remove(SelectionComponent::class.java) + piece.add(SelectionComponent()) + } } } @@ -54,6 +82,18 @@ class InputSystem : .firstOrNull() ?.add(MovementIntentComponent(position)) } + + private fun handleAbilityCardClicked(abilityCard: Entity) { + val selectionComponent = SelectionComponent.mapper.get(abilityCard) + val alreadySelectedEntity = engine.getEntitiesFor(Family.all(SelectionComponent::class.java).get()).firstOrNull() + + if (selectionComponent != null) { + abilityCard.remove(SelectionComponent::class.java) + } else { + alreadySelectedEntity?.remove(SelectionComponent::class.java) + abilityCard.add(SelectionComponent()) + } + } } class InputService { @@ -74,4 +114,32 @@ class InputService { entity?.add(ClickEventComponent()) } + + fun clickAbilityCardWithId(abilityCardId: Int) { + val entity = + EcsEngine + .getEntitiesFor(Family.all(AbilityCardComponent::class.java).get()) + .find { AbilityCardComponent.mapper.get(it).id == abilityCardId } + + entity?.add(ClickEventComponent()) + } + + fun confirmAbilityChoice() { + val entityWithSelectionComponent = + EcsEngine.getEntitiesFor(Family.all(SelectionComponent::class.java).get()).firstOrNull() ?: return + val abilityCardComponent = AbilityCardComponent.mapper.get(entityWithSelectionComponent) ?: return + if (abilityCardComponent.isInInventory) return + + abilityCardComponent.isInInventory = true + entityWithSelectionComponent.remove(SelectionComponent::class.java) + + // Remove all other not-in-inventory cards + EcsEngine + .getEntitiesFor(Family.all(AbilityCardComponent::class.java).get()) + .filter { + !AbilityCardComponent.mapper.get(it).isInInventory + }.forEach { + it.removeAll() + } + } } diff --git a/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/MovementSystem.kt b/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/MovementSystem.kt index 1613a1e8..143dab13 100644 --- a/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/MovementSystem.kt +++ b/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/MovementSystem.kt @@ -3,6 +3,7 @@ package io.github.chessevolved.systems import com.badlogic.ashley.core.Entity import com.badlogic.ashley.core.Family import com.badlogic.ashley.systems.IteratingSystem +import io.github.chessevolved.components.AbilityTriggerComponent import io.github.chessevolved.components.ActorComponent import io.github.chessevolved.components.FowComponent import io.github.chessevolved.components.MovementIntentComponent @@ -45,6 +46,8 @@ class MovementSystem( val pieceActorComponent = ActorComponent.mapper.get(entity) val pieceMovementRuleComponent = MovementRuleComponent.mapper.get(entity) + entity?.add(AbilityTriggerComponent(targetPosition, piecePositionComponent.position)) + piecePositionComponent.position = targetPosition pieceActorComponent.actor.setPosition(targetPosition.x.toFloat(), targetPosition.y.toFloat()) diff --git a/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/SelectionEntityListener.kt b/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/SelectionEntityListener.kt index 2b54c3d1..c2e06c92 100644 --- a/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/SelectionEntityListener.kt +++ b/chessevolved_model/src/main/kotlin/io/github/chessevolved/systems/SelectionEntityListener.kt @@ -4,6 +4,7 @@ import com.badlogic.ashley.core.Entity import com.badlogic.ashley.core.EntityListener import com.badlogic.ashley.core.Family import com.badlogic.gdx.graphics.Color +import io.github.chessevolved.components.AbilityCardComponent import io.github.chessevolved.components.CanBeCapturedComponent import io.github.chessevolved.components.HighlightComponent import io.github.chessevolved.components.MovementRuleComponent @@ -14,7 +15,9 @@ import io.github.chessevolved.components.ValidMovesComponent import io.github.chessevolved.components.WeatherEventComponent import io.github.chessevolved.singletons.EcsEngine -class SelectionEntityListener(private val boardSize: Int) : EntityListener { +class SelectionEntityListener( + private val boardSize: Int, +) : EntityListener { private val moveValidator = MoveValidator() private val capturableFamily = Family.all(CanBeCapturedComponent::class.java).get() @@ -27,6 +30,7 @@ class SelectionEntityListener(private val boardSize: Int) : EntityListener { .get() override fun entityAdded(entity: Entity?) { + if (AbilityCardComponent.mapper.get(entity) != null) return for (piece in EcsEngine.getEntitiesFor(capturableFamily)) { piece.remove(CanBeCapturedComponent::class.java) } @@ -51,6 +55,7 @@ class SelectionEntityListener(private val boardSize: Int) : EntityListener { } override fun entityRemoved(entity: Entity?) { + if (AbilityCardComponent.mapper.get(entity) != null) return entity?.remove(ValidMovesComponent::class.java) for (piece in EcsEngine.getEntitiesFor(capturableFamily)) { diff --git a/chessevolved_presenter/src/main/kotlin/io/github/chessevolved/presenters/GamePresenter.kt b/chessevolved_presenter/src/main/kotlin/io/github/chessevolved/presenters/GamePresenter.kt index 519fd27d..5238a303 100644 --- a/chessevolved_presenter/src/main/kotlin/io/github/chessevolved/presenters/GamePresenter.kt +++ b/chessevolved_presenter/src/main/kotlin/io/github/chessevolved/presenters/GamePresenter.kt @@ -11,10 +11,15 @@ import com.badlogic.gdx.scenes.scene2d.Stage import com.badlogic.gdx.utils.viewport.FitViewport import com.badlogic.gdx.utils.viewport.Viewport import io.github.chessevolved.Navigator +import io.github.chessevolved.components.AbilityCardComponent +import io.github.chessevolved.components.AbilityComponent import io.github.chessevolved.components.SelectionComponent +import io.github.chessevolved.components.TextureRegionComponent import io.github.chessevolved.data.Position +import io.github.chessevolved.entities.AbilityItemFactory import io.github.chessevolved.entities.BoardSquareFactory import io.github.chessevolved.entities.PieceFactory +import io.github.chessevolved.enums.AbilityType import io.github.chessevolved.enums.PieceType import io.github.chessevolved.enums.PlayerColor import io.github.chessevolved.enums.WeatherEvent @@ -23,11 +28,11 @@ import io.github.chessevolved.singletons.EcsEntityMapper import io.github.chessevolved.singletons.Game import io.github.chessevolved.singletons.Game.subscribeToGameUpdates import io.github.chessevolved.singletons.Game.unsubscribeFromGameUpdates -import io.github.chessevolved.singletons.GameSettings import io.github.chessevolved.singletons.Lobby import io.github.chessevolved.systems.AbilitySystem import io.github.chessevolved.systems.CaptureSystem import io.github.chessevolved.systems.FowRenderingSystem +import io.github.chessevolved.systems.InputService import io.github.chessevolved.systems.InputSystem import io.github.chessevolved.systems.MovementSystem import io.github.chessevolved.systems.RenderingSystem @@ -46,12 +51,16 @@ class GamePresenter( private val pieceFactory = PieceFactory(engine, assetManager) private val boardSquareFactory = BoardSquareFactory(engine, assetManager) + private val abilityItemFactory = AbilityItemFactory(engine, assetManager) private val gameCamera = OrthographicCamera() + private val gameUICamera = OrthographicCamera() private val boardWorldSize = 8 private val gameViewport: Viewport = FitViewport(boardWorldSize.toFloat(), boardWorldSize.toFloat(), gameCamera) + private val gameUIViewport: Viewport = + FitViewport(Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat(), gameUICamera) private lateinit var gameUIView: GameUIView private lateinit var gameBoardView: GameView private val gameBatch: SpriteBatch = SpriteBatch() @@ -63,11 +72,11 @@ class GamePresenter( private val selectionListener: SelectionEntityListener private val captureSystem: CaptureSystem private val inputSystem: InputSystem + private val inputService = InputService() private val abilitySystem: AbilitySystem private val visualEffectSystem: VisualEffectSystem private var navigatingToEndGame = false - private val isFowEnabled = GameSettings.isFOWEnabled() init { setupGameView() @@ -104,6 +113,10 @@ class GamePresenter( setupBoard() resize(Gdx.graphics.width, Gdx.graphics.height) + + val testAbilityCard = abilityItemFactory.createAbilityItem(AbilityType.EXPLOSION) + AbilityCardComponent.mapper.get(testAbilityCard).isInInventory = true + subscribeToGameUpdates(this.toString(), this::onGameStateUpdate) } @@ -120,6 +133,11 @@ class GamePresenter( assetManager.load(filename, Texture::class.java) } } + + AbilityType.entries.forEach { ability -> + val abilityName = ability.name.lowercase() + assetManager.load("abilities/$abilityName-card.png", Texture::class.java) + } } private fun setupGameView() { @@ -129,7 +147,8 @@ class GamePresenter( } } - gameUIView = GameUIView(gameViewport, gameCamera) + // TODO: Pass in if the player is the white player or not. + gameUIView = GameUIView(gameUIViewport, true, ::onSelectAbilityCardButtonClicked) gameUIView.init() gameBoardView = GameView(gameUIView.getStage(), gameViewport) @@ -137,8 +156,10 @@ class GamePresenter( gameStage = gameBoardView.getStage() - gameCamera.position.set(boardWorldSize / 2f, boardWorldSize / 2f, 0f) + gameCamera.position.set(boardWorldSize / 2f, boardWorldSize / 2f, 1f) + gameUICamera.position.set(boardWorldSize / 2f, boardWorldSize / 2f, 0f) gameCamera.update() + gameUICamera.update() } private fun setupBoard() { @@ -263,13 +284,17 @@ class GamePresenter( Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) gameViewport.apply() + gameCamera.update() gameBatch.projectionMatrix = gameCamera.combined gameBatch.begin() engine.update(Gdx.graphics.deltaTime) gameBatch.end() - gameBoardView.render() + // gameBoardView.render() + + updateAbilityCardInventoryView() + gameUIViewport.apply() gameUIView.render() } @@ -277,8 +302,10 @@ class GamePresenter( width: Int, height: Int, ) { - gameViewport.update(width, height, false) + gameViewport.update(width, height, true) + gameUIViewport.update(width, height, false) gameBoardView.resize(width, height) + gameUIView.resize(width, height) } override fun dispose() { @@ -320,6 +347,80 @@ class GamePresenter( } } } + AbilityType.entries.forEach { ability -> + val abilityName = ability.name.lowercase() + val filename = "abilities/$abilityName-card.png" + if (assetManager.isLoaded(filename)) { + assetManager.unload(filename) + } + } + } + + private fun updateAbilityCardInventoryView() { + // Update if abilityPickPrompt should be showing or not. + val allAbilityCards = EcsEngine.getEntitiesFor(Family.all(AbilityCardComponent::class.java).get()) + val abilityCardsNotInInventory = + allAbilityCards.filter { + !AbilityCardComponent.mapper.get(it).isInInventory + } + + if (abilityCardsNotInInventory.isNotEmpty()) { + val abilityCards = + abilityCardsNotInInventory.associateBy { + GameUIView.AbilityCardInformation( + TextureRegionComponent.mapper + .get(it) + .region + ?.texture, + AbilityCardComponent.mapper.get(it).id, + ) + } + gameUIView.promptPickAbility(abilityCards.keys, ::onAbilityCardClicked) + } else { + gameUIView.hidePromptPickAbility() + } + + // Update if any new cards have appeared/disappeared from inventory. + val abilityCardsInInventory = + allAbilityCards.filter { + AbilityCardComponent.mapper.get(it).isInInventory + } + + val abilityCards = + abilityCardsInInventory.associateBy { + GameUIView.AbilityCardInformation( + TextureRegionComponent.mapper + .get(it) + .region + ?.texture, + AbilityCardComponent.mapper.get(it).id, + ) + } + + gameUIView.updateCardsInInventory(abilityCards.keys, ::onAbilityCardClicked) + + // Update if a card has been selected or deselected. + val selectedAbilityCardEntity = + EcsEngine.getEntitiesFor(Family.all(AbilityCardComponent::class.java, SelectionComponent::class.java).get()).firstOrNull() + if (selectedAbilityCardEntity != null) { + gameUIView.selectCardFromInventory( + AbilityComponent.mapper + .get(selectedAbilityCardEntity) + .ability + .abilityDescription, + AbilityCardComponent.mapper.get(selectedAbilityCardEntity).id, + ) + } else { + gameUIView.selectCardFromInventory("", -1) + } + } + + fun onAbilityCardClicked(idOfAbilityClicked: Int) { + inputService.clickAbilityCardWithId(idOfAbilityClicked) + } + + fun onSelectAbilityCardButtonClicked() { + inputService.confirmAbilityChoice() } override fun setInputProcessor() { diff --git a/chessevolved_shared/src/main/kotlin/io/github/chessevolved/enums/AbilityType.kt b/chessevolved_shared/src/main/kotlin/io/github/chessevolved/enums/AbilityType.kt index 4f55941e..adb824b9 100644 --- a/chessevolved_shared/src/main/kotlin/io/github/chessevolved/enums/AbilityType.kt +++ b/chessevolved_shared/src/main/kotlin/io/github/chessevolved/enums/AbilityType.kt @@ -1,9 +1,11 @@ package io.github.chessevolved.enums -enum class AbilityType { - SHIELD, - EXPLOSION, - SWAP, - MIRROR, - NEW_MOVEMENT, +enum class AbilityType( + val abilityDescription: String, +) { + SHIELD("Blocks attacks from opponent pieces"), + EXPLOSION("Causes an explosion around the tile that your piece moves to"), + SWAP("Swaps position with a non royal piece"), + MIRROR("Mirrors an ability used against this piece"), + NEW_MOVEMENT("No cluerino"), } diff --git a/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameUIView.kt b/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameUIView.kt index b94f8884..8bbdf2dc 100644 --- a/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameUIView.kt +++ b/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameUIView.kt @@ -1,75 +1,251 @@ package io.github.chessevolved.views import com.badlogic.gdx.Gdx -import com.badlogic.gdx.graphics.OrthographicCamera +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.Pixmap +import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.scenes.scene2d.Stage -import com.badlogic.gdx.utils.viewport.FitViewport +import com.badlogic.gdx.scenes.scene2d.ui.ImageButton +import com.badlogic.gdx.scenes.scene2d.ui.ImageButton.ImageButtonStyle +import com.badlogic.gdx.scenes.scene2d.ui.Label +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.badlogic.gdx.scenes.scene2d.ui.TextButton +import com.badlogic.gdx.scenes.scene2d.ui.TextField +import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable import com.badlogic.gdx.utils.viewport.Viewport +import ktx.actors.onClick +import ktx.scene2d.imageButton import ktx.scene2d.label import ktx.scene2d.scene2d import ktx.scene2d.table +import ktx.scene2d.textButton +import ktx.scene2d.textField +import kotlin.math.min class GameUIView( /** * This variable can be used to make sure the ui doesn't overlap the game-board. */ private val gameViewport: Viewport, - private val gameCamera: OrthographicCamera, + private val isWhitePlayer: Boolean, + private val onPickAbilityCardButtonClicked: () -> Unit, ) : IView { private lateinit var stage: Stage - private lateinit var viewport: Viewport + private val blackColor = Color(0.37f, 0.5f, 0.6f, 0.8f) + private val whiteColor = Color(1f, 1f, 1f, 0.8f) + private val abilityCards = HashMap() + private var sizeOfAbilityCards = min(100, (Gdx.graphics.width - 10) / (abilityCards.size + 1)) + private var promptedAmountOfPickableAbilities = 0 + private lateinit var abilityCardInventory: Table + private lateinit var abilityPickerWindow: Table + private lateinit var blackTimer: TextField + private lateinit var whiteTimer: TextField + private lateinit var abilityDescriptionLabel: Label + private lateinit var abilityInfoTable: Table + private lateinit var pickAbilityButton: TextButton + + data class AbilityCardInformation( + val texture: Texture? = Texture(Gdx.files.internal("pieces/pawn-white.png")), + val id: Int, + ) override fun init() { - viewport = - FitViewport( - Gdx.graphics.width.toFloat(), - Gdx.graphics.height.toFloat(), - ) + stage = Stage(gameViewport) + + val blackInfoBox = + scene2d.table { + textField(if (isWhitePlayer) "Black: Opponent" else "Black: You") { + color = blackColor + isDisabled = true + }.cell(growX = true) + + blackTimer = + textField("Time: 10:00") { + color = blackColor + isDisabled = true + } + row() + } + + val bgPixmap = Pixmap(1, 1, Pixmap.Format.RGB565) + bgPixmap.setColor(Color.WHITE) + bgPixmap.fill() + val textureRegionDrawableBg = TextureRegionDrawable(TextureRegion(Texture(bgPixmap))) + + abilityInfoTable = + scene2d.table { + color = blackColor + label("Ability Information:") { + setAlignment(1) + }.cell(growX = true, colspan = 2) + row() + abilityDescriptionLabel = + label("") { + wrap = true + setAlignment(1) + }.cell(growX = true, colspan = 2) + setBackground(textureRegionDrawableBg) + } + + bgPixmap.dispose() + + abilityPickerWindow = + scene2d.table { + defaults().width(100f).height(100f).pad(4f) + isVisible = false + } - stage = - Stage() + abilityCardInventory = + scene2d.table { + defaults().width(sizeOfAbilityCards.toFloat()).height(sizeOfAbilityCards.toFloat()) + } + + val whiteInfoBox = + scene2d.table { + textField(if (isWhitePlayer) "White: You" else "White: Opponent") { + color = whiteColor + isDisabled = true + }.cell(growX = true) + + whiteTimer = + textField("Time: 10:00") { + color = whiteColor + isDisabled = true + } + } val root = scene2d.table { setFillParent(true) - defaults().pad(10f) - label("Settings 1") { - it.colspan(2).padBottom(50f).center() - setColor(1f, 0f, 0f, 0.5f) - } - row() - label("Settings 2") { it.colspan(2).padBottom(50f).center() } - row() - label("Settings 3") { it.colspan(2).padBottom(50f).center() } - row() - label("Settings 4") { - it.colspan(2).padBottom(50f).center() - } - row() - label("Settings 5") { it.colspan(2).padBottom(50f).center() } - row() - label("Settings 6") { it.colspan(2).padBottom(50f).center() } - row() - label("Settings") { it.colspan(2).padBottom(50f).center() } - row() - label("Settings") { it.colspan(2).padBottom(50f).center() } + add(blackInfoBox) + .growX() + .top() + .padBottom(10f) row() - label("Settings") { it.colspan(2).padBottom(50f).center() } + add(abilityInfoTable.top()) + .growX() + .expandY() + .top() + .height(100f) row() - label("Settings") { it.colspan(2).padBottom(50f).center() } + add(abilityPickerWindow).height(200f) row() - label("Settings") { it.colspan(2).padBottom(50f).center() } + pickAbilityButton = + textButton("Select Ability") { + onClick { onPickAbilityCardButtonClicked() } + }.cell(padTop = -10f) row() - label("Settings") { it.colspan(2).padBottom(50f).center() } + add(abilityCardInventory) + .growX() + .growY() + .expandY() + .bottom() + .height(120f) row() + add(whiteInfoBox).growX() + } - label("Fog of War:") { it.right() } - row() + stage.addActor(root) + } - label("Board Size (8-16):") { it.right() } - row() + fun promptPickAbility( + abilityCards: Set, + onAbilityPickedListener: (idOfAbilityClicked: Int) -> Unit, + ) { + if (promptedAmountOfPickableAbilities == abilityCards.size) return + promptedAmountOfPickableAbilities = abilityCards.size + abilityPickerWindow.reset() + abilityPickerWindow.isVisible = true + pickAbilityButton.isVisible = true + + abilityCards.forEach { + val imgButtonStyle = ImageButtonStyle() + val id = it.id + val texture = it.texture + abilityPickerWindow + .add( + scene2d.imageButton { + style = imgButtonStyle + onClick { onAbilityPickedListener(id) } + style.up = TextureRegionDrawable(TextureRegion(texture)) + style.down = TextureRegionDrawable(TextureRegion(texture)) + style.over = TextureRegionDrawable(TextureRegion(texture)).tint(Color(0.5f, 0.5f, 0.5f, 1f)) + }, + ).width(100f) + .height(100f) + } + } + + fun hidePromptPickAbility() { + if (!abilityPickerWindow.isVisible) return + promptedAmountOfPickableAbilities = 0 + abilityPickerWindow.isVisible = false + pickAbilityButton.isVisible = false + } + + fun updateCardsInInventory( + abilityCards: Set, + onAbilityUsed: (abilityId: Int) -> Unit, + ) { + // Avoid updating inventory if inventory haven't lost/gained cards. + if (abilityCards.size == this.abilityCards.size) return + + abilityCardInventory.clear() + this.abilityCards.clear() + abilityCards.forEach { + addAbilityCardToInventory(it, onAbilityUsed) + } + } + + private fun addAbilityCardToInventory( + abilityCardInformation: AbilityCardInformation, + onAbilityUsed: (idOfAbilityClicked: Int) -> Unit, + ) { + sizeOfAbilityCards = min(100, (Gdx.graphics.width - 10) / (abilityCards.size + 1)) + + val imgButtonStyle = ImageButtonStyle() + val abilityCard = + scene2d.imageButton { + style = imgButtonStyle + style.up = TextureRegionDrawable(TextureRegion(abilityCardInformation.texture)) + style.down = TextureRegionDrawable(TextureRegion(abilityCardInformation.texture)) + style.over = TextureRegionDrawable(TextureRegion(abilityCardInformation.texture)).tint(Color(0.5f, 0.5f, 0.5f, 1f)) + onClick { + onAbilityUsed(abilityCardInformation.id) + } } - stage.addActor(root) + abilityCardInventory.add(abilityCard) + + abilityCards[abilityCardInformation.id] = abilityCard + + abilityCardInventory.cells.forEach { + it.width(sizeOfAbilityCards.toFloat()).height(sizeOfAbilityCards.toFloat()) + } + } + + fun selectCardFromInventory( + abilityInformation: String, + abilityCardId: Int, + ) { + abilityDescriptionLabel.setText(abilityInformation) + abilityInfoTable.isVisible = abilityInformation != "" + + abilityCards.forEach { + it.value.width = sizeOfAbilityCards.toFloat() + it.value.height = sizeOfAbilityCards.toFloat() + } + + abilityCards[abilityCardId]?.width = sizeOfAbilityCards.toFloat() + 20f + abilityCards[abilityCardId]?.height = sizeOfAbilityCards.toFloat() + 20f + } + + fun updateWhiteTimer(time: Int) { + whiteTimer.text = "Time: $time" + } + + fun updateBlackTimer(time: Int) { + blackTimer.text = "Time: $time" } override fun render() { diff --git a/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameView.kt b/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameView.kt index 6da1d056..f54450eb 100644 --- a/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameView.kt +++ b/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/GameView.kt @@ -15,8 +15,8 @@ class GameView( override fun init() { gameStage = Stage(gameViewport) - inputMultiplexer.addProcessor(gameStage) inputMultiplexer.addProcessor(uiStage) + inputMultiplexer.addProcessor(gameStage) } fun getStage(): Stage = gameStage