Skip to content

Commit

Permalink
Merge pull request #45 from SWA-group-1/refactoring
Browse files Browse the repository at this point in the history
Refactoring and minor fixes
  • Loading branch information
le0-0 authored Apr 28, 2021
2 parents cfab19f + 8451c7e commit 4d79e7b
Show file tree
Hide file tree
Showing 25 changed files with 287 additions and 84 deletions.
Binary file modified android/debug/android-debug.apk
Binary file not shown.
2 changes: 1 addition & 1 deletion android/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name">frontend</string>
<string name="app_name">Corona Defense</string>

</resources>
4 changes: 3 additions & 1 deletion core/src/com/coronadefense/Game.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.InputMultiplexer
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.coronadefense.receiver.Receiver
import com.coronadefense.states.StateManager
import com.coronadefense.states.menuStates.MainMenuState

/**
* Class for launching the game.
*/
class Game : ApplicationAdapter() {
companion object {
var sprites: SpriteBatch? = null
Expand Down
4 changes: 4 additions & 0 deletions core/src/com/coronadefense/api/ApiClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const val baseUrl = "http://35.228.171.73:5000"
//const val baseUrl = "http://localhost:5000"
const val firebaseUrl = "https://firebasestorage.googleapis.com/v0/b/coronadefense-1.appspot.com/o/"

/**
* Object to send requests to the API.
* Used to fetch config files from Firebase, set up lobbies, and communicate user input to the game on the server.
*/
object ApiClient {
private val client = HttpClient {
install(JsonFeature) {
Expand Down
3 changes: 3 additions & 0 deletions core/src/com/coronadefense/receiver/IMessageType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package com.coronadefense.receiver

import com.coronadefense.receiver.messages.IMessage

/**
* Interface for the different kinds of messages received from the broadcaster.
*/
interface IMessageType {
/**
* Byte code used to differentiate between message types.
Expand Down
3 changes: 3 additions & 0 deletions core/src/com/coronadefense/receiver/MessageTypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ private fun parseBool(byte: Byte): Boolean {
return byte == 0x01.toByte();
}

/**
* Enum for parsing of the bytes from all the different sorts of messages from the broadcaster.
*/
@ExperimentalUnsignedTypes
enum class MessageType : IMessageType {
PING {
Expand Down
4 changes: 3 additions & 1 deletion core/src/com/coronadefense/receiver/Receiver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ private const val SERVER_ADDRESS: String = "35.228.171.73"
* Object that connects to the backend and listens to broadcaster sending game information.
*/
object Receiver {
// Single observer, since one client will only ever have one observer to the Receiver.
// This makes it easier to remove the observer when the client no longer needs to listen.
var observer: IReceiverObserver? = null

private val socketBuilder = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp()
private var socket: Socket? = null

/**
* Attempt to connect to backend broadcaster.
* @return The connection number of the
* @return The connection number of the socket
*/
@ExperimentalUnsignedTypes
suspend fun connectAsync(): Long {
Expand Down
29 changes: 25 additions & 4 deletions core/src/com/coronadefense/states/GameObserver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,52 @@ import com.coronadefense.types.gameObjects.Tower
import com.coronadefense.utils.DIFFICULTY
import kotlinx.coroutines.runBlocking

/**
* Class for observing the Receiver, and store the state of the game for use by the Lobby-/PlayStates.
* @param lobbyId ID of the lobby connected to this game.
* @param lobbyName Name of the lobby connected to this game.
* @param accessToken Token for the current connection, for use in API requests.
* @param playerCount Initial player count.
*/
class GameObserver(
val lobbyId: Long,
val lobbyName: String,
val accessToken: Long,
var playerCount: Int
): IReceiverObserver {
// Stage to be fetched after StartGameRequest.
var gameStage: GameStage? = null

// State of the current game
// "fight" for wave phase
// "input" for placement phase
// "lobby" for game setup
// "leave" for terminating the game
var gameState: String? = null

val placedTowers: MutableList<Tower> = mutableListOf()
var money: Int? = null
var health: Int? = null

var timeConfirmed: Float = 0f // time confirmed animations
val pathToPathAnimations = mutableListOf<PathToPathAnimationMessage>() //Intruders
val healthAnimations = mutableListOf<HealthAnimationMessage>()
val moneyAnimations = mutableListOf<MoneyAnimationMessage>()
val boardToPathAnimations = mutableListOf<BoardToPathAnimationMessage>()

// Lists of animations to be rendered in turn by PlayStateWave.
val pathToPathAnimations = mutableListOf<PathToPathAnimationMessage>() // Intruders
val boardToPathAnimations = mutableListOf<BoardToPathAnimationMessage>() // Projectiles
val healthAnimations = mutableListOf<HealthAnimationMessage>() // Health point updates
val moneyAnimations = mutableListOf<MoneyAnimationMessage>() // Money updates

// Selected difficulty of the game.
var difficulty: DIFFICULTY? = null

var endGame = false
var endGameMessage: EndGameMessage? = null

// Whether the lobby has timed out.
var socketClosed = false

// Function to leave the lobby and disconnect from the Receiver.
// Empty catch block, since a failed LeaveLobbyRequest means that the lobby is already terminated backend.
fun leaveLobby() {
runBlocking {
try {
Expand All @@ -56,6 +75,7 @@ class GameObserver(
}
}

// When the game is started, store the difficulty and fetch the GameStage.
override fun handleGameModeMessage(message: GameModeMessage) {
println(message)

Expand Down Expand Up @@ -120,6 +140,7 @@ class GameObserver(
)
}

// Sell tower functionality not implemented frontend due to time constraints.
override fun handleTowerRemovedMessage(message: TowerRemovedMessage) {
println(message)
}
Expand Down
16 changes: 13 additions & 3 deletions core/src/com/coronadefense/states/InputState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.coronadefense.states

import com.badlogic.gdx.Gdx
import com.badlogic.gdx.InputMultiplexer
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.utils.viewport.StretchViewport
Expand All @@ -11,27 +10,37 @@ import com.coronadefense.Game
import com.coronadefense.utils.Constants.GAME_HEIGHT
import com.coronadefense.utils.Constants.GAME_WIDTH

/**
* Abstract class for states that need user input.
* Extends the State abstract class for the most basic common state functionality.
* @param stateManager Manager of all states.
*/
abstract class InputState(
stateManager: StateManager
) : State(stateManager) {
// libGDX viewport and stage, making the stage protected for use by subclasses.
private val viewport: Viewport = StretchViewport(GAME_WIDTH, GAME_HEIGHT, camera)
protected val stage: Stage = Stage(viewport, Game.sprites)

// A list of buttons to make disposal of listeners easier.
// All subclasses that add buttons must add them to this list for listener disposal.
protected val buttons: MutableList<Image> = mutableListOf()

// adds the stage as an input processor
init {
// Adds the stage as an input processor.
val inputMultiplexer: InputMultiplexer = Gdx.input.inputProcessor as InputMultiplexer;
if (!inputMultiplexer.processors.contains(stage)) {
inputMultiplexer.addProcessor(stage)
}
}

fun draw() {
// Draws all 'actors' (mainly buttons) on the stage.
stage.draw()
}

// removes the stage as an input processor, clears the stage and disposes it
override fun dispose() {
// Removes the stage as an input processor.
val inputMultiplexer: InputMultiplexer = Gdx.input.inputProcessor as InputMultiplexer;
if (inputMultiplexer.processors.contains(stage)) {
inputMultiplexer.removeProcessor(stage)
Expand All @@ -40,6 +49,7 @@ abstract class InputState(
stage.clear()
stage.dispose()

// Clears listeners from all buttons.
for (button in buttons) {
button.clearListeners()
}
Expand Down
24 changes: 16 additions & 8 deletions core/src/com/coronadefense/states/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.coronadefense.utils.Constants.GAME_HEIGHT
import com.coronadefense.utils.Constants.GAME_WIDTH

abstract class State(protected var stateManager: StateManager) {
protected val camera: OrthographicCamera = OrthographicCamera()
init {
camera.setToOrtho(false, GAME_WIDTH, GAME_HEIGHT)
}
abstract fun update(deltaTime: Float)
abstract fun render(sprites: SpriteBatch)
abstract fun dispose()
/**
* Abstract class for most basic of common functionality between game states.
* @param stateManager Manager of all game states.
*/
abstract class State(
protected var stateManager: StateManager
) {
protected val camera: OrthographicCamera = OrthographicCamera()

init {
camera.setToOrtho(false, GAME_WIDTH, GAME_HEIGHT)
}

abstract fun update(deltaTime: Float)
abstract fun render(sprites: SpriteBatch)
abstract fun dispose()
}
36 changes: 22 additions & 14 deletions core/src/com/coronadefense/states/StateManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@ package com.coronadefense.states
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import java.util.*

/**
* Class for managing which game state is currently rendered.
*/
class StateManager {
// Allows for stacking states, though this is currently not really used as we only keep one state in it at a time.
private val states: Stack<State> = Stack()

private val states: Stack<State> = Stack()
fun push(state: State) {
states.push(state)
}
fun set(state: State) {
states.pop().dispose()
states.push(state)
}
fun update(deltaTime: Float) {
states.peek().update(deltaTime)
}
fun render(sprites: SpriteBatch) {
states.peek().render(sprites)
}
fun push(state: State) {
states.push(state)
}

// When a new game state is set, dispose the old one.
fun set(state: State) {
states.pop().dispose()
states.push(state)
}

fun update(deltaTime: Float) {
states.peek().update(deltaTime)
}

fun render(sprites: SpriteBatch) {
states.peek().render(sprites)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import com.coronadefense.utils.Constants
import com.coronadefense.utils.Font
import com.coronadefense.utils.Textures

/**
* State for when the game ends, showing either a victory or a defeat screen.
* Extends InputState to enable user input on the back button.
* @param stateManager Manager of all game states.
* @param endGameMessage Message that triggered this state, to determine win/loss state.
*/
class EndGameState(
stateManager: StateManager,
endGameMessage: EndGameMessage
Expand All @@ -18,6 +24,8 @@ class EndGameState(

private val font = Font(20)
private val bigFont = Font(30)

// Determines the message to display to the user based on their placing.
private val highscoreMessage: String = when (endGameMessage.onHighScoreList) {
in 1..10 -> "You made the highscore list, your current position is ${endGameMessage.onHighScoreList}!"
0 -> "You did not make it onto the highscore list."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import kotlinx.coroutines.launch

/**
* State to show the list of highscores between players in Corona Defense.
* Extends InputState to allow user input on the back button.
* @param stateManager Manager of all game states.
*/
class HighscoreListState(
Expand All @@ -30,9 +31,13 @@ class HighscoreListState(
private val title = "HIGHSCORES"

private var highscoreList: List<HighScore>? = null

// Calculates positions here to spare calculations in the render method.
private val listPositionX: Float = (GAME_WIDTH - LIST_ITEM_WIDTH) * 0.5f
private val listPositionsY: MutableList<Float> = mutableListOf()

init {
// Launches a coroutine to fetch the high score list, and calculates the list items' Y positions based on its length.
GlobalScope.launch {
highscoreList = ApiClient.highScoreListRequest()
for (index in highscoreList!!.indices) {
Expand All @@ -59,6 +64,7 @@ class HighscoreListState(
(GAME_HEIGHT - font.height(title)) * 0.5f + MENU_TITLE_OFFSET
)

// Checks that the highscoreList is fetched, then loops through it to display each highscore.
highscoreList?.let {
for ((index, highscore) in highscoreList!!.withIndex()) {
font.draw(
Expand Down
Loading

0 comments on commit 4d79e7b

Please sign in to comment.