Skip to content
Snippets Groups Projects
Commit ac0dae72 authored by Helena Steine Tysland's avatar Helena Steine Tysland
Browse files

Merge branch 'master' into '41-make-the-players-stand-before-countdown'

# Conflicts:
#   core/src/tdt4240/netrunner/view/controllers/PlayerRenderer.kt
parents e8970d44 fef78ec8
Branches 41-make-the-players-stand-before-countdown
No related tags found
1 merge request!12Resolve "make the players stand before countdown"
Pipeline #218915 passed
Showing
with 298 additions and 77 deletions
......@@ -9,6 +9,7 @@ image: jangrewe/gitlab-ci-android
test-ubuntu:
stage: test
script:
- apt clean
- apt update
- apt install -y curl unzip zip sed openjdk-17-jdk
# Note; if/when UI-based tests are added, this script must be modified to allow for virtual displays.
......@@ -22,6 +23,7 @@ deploy:
name: production
url: 10.212.27.55
script:
- apt clean
- apt update
- apt install -y openssh-client
# Required to get the SSH agent to run or add it to the environment or whatever. I don't know, I don't normally hotpatch SSH agents into CI scripts for fun
......
# Netrunner
## Project structure
The project has six primary components:
* `android/`: Contains all Android-specific parts of the game. This module has very little code in it, as it mostly has the boilerplate Android stuff, and the launcher. Additionally, it has Android-specific dependencies, though they're managed via Gradle.
* `assets/`: Contains all the game assets, such as graphics. This part contains no code.
* `core/`: Contains all the game logic.
* `desktop/`: Contains all the desktop-specific parts of the game. Similarly to the Android component, it mostly consists of boilerplate desktop stuff, as well as desktop-specific dependencies, also managed via Gradle.
* `model/`: Contains all the data structures. This module is shared between core and server, as both those modules need access to shared data objects to function optimally.
* `server/`: Contains the core code for the server.
Additionally, the following important files and folders exist:
* `etc/`: contains files that are copied to `/etc/` on the Linux-based server.
* `.gitlab-ci.yml`: Contains the CI definitions, as well as the system used for deploying directly to the server
* `server-bootstrap.sh`: This file is exclusively intended for use on the official server. It installs the required dependencies for the server, and installs the `etc/` folder to the system.
## Building, running, and deploying.
Building components can be done from the command line or your favorite IDE. This will not be demonstrated in detail, as this is fairly straight-forward for the components involved.
### Server
#### Alternative 1: Official server
Unless otherwise configured, the game connects to a centrally configured server. This server can be used for production use, requiring no further instructions; skip straight to the "Game" header.
#### Alternative 2: Setting up a debug server (not intended for production use)
**Note:** this must be re-done on each network, as your IP will not remain constant. Redoing it on known networks may also be necessary depending on network settings, but you'll notice that if the game fails to connect.
For debug purposes, it's often more convenient to use a local server. Due to the IP issues this introduces, you're required to specify an IP to connect to.
This is done by creating `assets/local-game.properties`. This file must contain:
```
netrunner.server-addr=xxx.xxx.xxx.xxx
netrunner.server-port=5000
```
The port is 5000 by default in the debug build, so this generally doesn't need to be changed. To find the IP to use, this depends on which configuration you're running
###### Desktop debugging
For desktop debugging, the IP can simply be `127.0.0.1`. The next option works regardless.
###### Android debugging/remote server access
To connect to the server from Android, you have to specify the local IP. How you go about this depends on your OS:
* Linux: `ifconfig`, `ip a`, look for the correct network adapter, or `hostname -I`. Note that some commands may require additional packages. GUI options may exist for different distros, but linking all of them is an exercise in futility. That said, gnome-based DEs shows the IP in the network settings.
* Windows: `ipconfig`, or GUI options. See: https://www.digitalcitizen.life/find-ip-address-windows/ for instructions
* MacOS: `ifconfig`, or GUI options. See: https://www.wikihow.com/Find-Your-IP-Address-on-a-Mac for instructions
###### Running the server
To run a debug version of the server, you can run `./gradlew server:dist` (or `.\gradlew.bat server:dist` on Windows)
### Game
#### Compiling and running locally (recommended for end-users)
Compiling the game locally is best done through Android Studio, and by running the AndroidLauncher in the android module. No steps are required beyond getting the target, and hitting "run" in Android Studio. The build system downloads all the required dependencies.
#### Building deployment builds
Deployment produces the relevant binaries, and they have to be installed and/or run outside Android Studio, or your favorite editor. These are production packages, and setup is a bit more complicated than just running from within Android Studio (the, by far, recommended approach).
**Note:** prior to deployment, specifically of the desktop and Android apps, `assets/game-local.properties` MUST be removed. This may change in the future if we figure out how to do conditional asset inclusion in different builds.
To deploy the project, the `:dist` target can be used. Example commands:
* Desktop: `./gradlew desktop:dist`
* Server: `./gradlew server:dist`
Android is an exception, and uses a different target: `./gradlew android:assembleRelease`
Note that `./gradlew` has to be replaced with `.\gradlew.bat` on Windows.
See also [LibGDX' own documentation on deployment](https://libgdx.com/wiki/deployment/deploying-your-application)
assets/bg_menu.png

186 KiB

assets/bg_menu_v2.png

221 KiB

assets/ui_down.png

14 KiB

assets/ui_exit.png

16.1 KiB

assets/ui_up.png

13.8 KiB

......@@ -13,7 +13,7 @@ import org.slf4j.LoggerFactory
import tdt4240.netrunner.game.Client
import tdt4240.netrunner.model.game.data.PlayerColor
import tdt4240.netrunner.view.LoadingScreen
import tdt4240.netrunner.view.controllers.Animation
import tdt4240.netrunner.view.util.Animation
import tdt4240.netrunner.view.util.TextureRepository
import java.util.Properties
import kotlin.system.exitProcess
......
......@@ -5,6 +5,7 @@ import io.socket.client.Ack
import org.slf4j.LoggerFactory
import tdt4240.netrunner.Netrunner
import tdt4240.netrunner.model.game.EcsEngine
import tdt4240.netrunner.model.game.components.level.FinishLineComponent
import tdt4240.netrunner.model.game.data.PlayerColor
import tdt4240.netrunner.model.requests.JoinGameRequest
import tdt4240.netrunner.model.response.EntityData
......@@ -43,6 +44,7 @@ class GameController(val game: Netrunner) {
addController(GroundRenderer(this@GameController, this))
addController(PlatformRenderer(this@GameController, this))
addController(PowerUpRenderer(this@GameController, this))
addController(FinishLineRenderer(this@GameController, this))
addController(CameraController(this@GameController, this))
}
......
......@@ -5,6 +5,7 @@ import com.badlogic.gdx.ScreenAdapter
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton
......@@ -22,24 +23,27 @@ import tdt4240.netrunner.game.Client
import tdt4240.netrunner.game.GameController
import tdt4240.netrunner.model.response.StatusResponse
import tdt4240.netrunner.model.util.gsonImpl
import tdt4240.netrunner.view.game.PostGameLeaderboardRenderer
import tdt4240.netrunner.view.util.TextureRepository
class GameScreen(private val game: Netrunner, private val controller: GameController) : ScreenAdapter() {
// I'm 99.9999% sure game.uiViewport means the HUD won't be affected by the game camera and viewport
private val stage = Stage(game.uiViewport)
private var cam = OrthographicCamera(Netrunner.MIN_WIDTH, Netrunner.MIN_HEIGHT)
val gameViewport = FitViewport(Netrunner.MIN_WIDTH, Netrunner.MIN_HEIGHT, cam)
private val leaderboardRenderer = PostGameLeaderboardRenderer(controller)
companion object {
private val logger = LoggerFactory.getLogger(Netrunner::class.java)
}
val gameViewport = FitViewport(Netrunner.MIN_WIDTH, Netrunner.MIN_HEIGHT, cam)
init {
val buttonStyle = TextButton.TextButtonStyle()
buttonStyle.font = game.skin.getFont("default-font")
buttonStyle.downFontColor = Color.RED
val exitButton = TextButton("Exit", buttonStyle) //bruk evt. game.skin
val exitButton = ImageButton(TextureRegionDrawable(TextureRepository.exitButton)) //bruk evt. game.skin
exitButton.apply {
width = 70f
height = 70f
}
//if we want an exact position
//exitButton.setPosition(1000f, 10f)
// stage.addActor(exitButton)
......@@ -70,7 +74,9 @@ class GameScreen(private val game: Netrunner, private val controller: GameContro
val table = Table()
table.setFillParent(true)
table.row()
table.add(exitButton).pad(50f).expand().top().right()
table.add(exitButton).pad(30f).expand().top().right()
.width(exitButton.width)
.height(exitButton.height)
table.row()
stage.addActor(table)
......@@ -79,8 +85,8 @@ class GameScreen(private val game: Netrunner, private val controller: GameContro
imageButtonUpStyle.imageUp = drawableUp
imageButtonUpStyle.imageDown = drawableUp
val buttonUp = ImageButton(imageButtonUpStyle)
buttonUp.height = 80f
buttonUp.width = 80f
buttonUp.height = BUTTON_DIMS
buttonUp.width = BUTTON_DIMS
buttonUp.setPosition(Netrunner.MIN_WIDTH - buttonUp.width - 20f, 100f)
buttonUp.addListener(object : ClickListener() {
override fun clicked(event: InputEvent?, x: Float, y: Float) {
......@@ -98,8 +104,8 @@ class GameScreen(private val game: Netrunner, private val controller: GameContro
imageButtonDownStyle.imageUp = drawableDown
imageButtonDownStyle.imageDown = drawableDown
val buttonDown = ImageButton(imageButtonDownStyle)
buttonDown.height = 80f
buttonDown.width = 80f
buttonDown.height = BUTTON_DIMS
buttonDown.width = BUTTON_DIMS
buttonDown.setPosition(Netrunner.MIN_WIDTH - buttonUp.width - 20f, 20f)
buttonDown.addListener(object : ClickListener() {
override fun clicked(event: InputEvent?, x: Float, y: Float) {
......@@ -137,6 +143,8 @@ class GameScreen(private val game: Netrunner, private val controller: GameContro
stage.viewport.apply()
stage.act(delta)
stage.draw()
leaderboardRenderer.render()
}
override fun resize(width: Int, height: Int) {
......@@ -147,6 +155,13 @@ class GameScreen(private val game: Netrunner, private val controller: GameContro
override fun dispose() {
stage.dispose()
controller.deregisterGameEvents()
leaderboardRenderer.dispose()
}
companion object {
private val logger = LoggerFactory.getLogger(Netrunner::class.java)
const val BUTTON_DIMS = 100f
}
}
......@@ -12,6 +12,7 @@ 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.ClickListener
import com.badlogic.gdx.utils.Align
import tdt4240.netrunner.Netrunner
import tdt4240.netrunner.model.game.data.PlayerColor
import tdt4240.netrunner.view.util.StageUtils
......@@ -58,19 +59,24 @@ class MenuScreen(private val game: Netrunner) : ScreenAdapter() {
})
val table = Table()
table.pad(20f)
table.setFillParent(true)
table.align(Align.left)
table.row().align(Align.left)
table.add(label)
table.row()
table.add(usernameInput).size(500f, 100f)
table.row()
table.row().align(Align.left)
table.add(usernameInput).size(400f, 100f)
table.row().align(Align.left)
table.add(avatarLabel)
table.row()
table.row().align(Align.left)
table.add(colorSelectBox).size(400f, 100f)
table.row().align(Align.center)
table.add(playButton)
.padTop(20f)
.size(400f, 100f)
table.row()
table.add(playButton).pad(20f).size(300f, 100f)
table.row()
//table.add(chosenColorLabel)
table.row()
stage.addActor(table)
Gdx.input.inputProcessor = stage
......
package tdt4240.netrunner.view.controllers
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import tdt4240.netrunner.game.GameController
import tdt4240.netrunner.model.game.EcsController
import tdt4240.netrunner.model.game.EcsEngine
import tdt4240.netrunner.model.game.components.dynamic.PositionComponent
import tdt4240.netrunner.model.game.components.level.FinishLineComponent
import tdt4240.netrunner.model.game.components.living.DimensionComponent
class FinishLineRenderer(val gController: GameController, engine: EcsEngine) : EcsController(engine) {
override fun render() {
for (entity in super.ecs.getEntitiesByComponent(FinishLineComponent::class.java)) {
val pos = entity.getComponent(PositionComponent::class.java)!!
val dims = entity.getComponent(DimensionComponent::class.java)!!
gController.game.apply {
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled)
shapeRenderer.color = Color.GREEN
shapeRenderer.rect(pos.pos.x, pos.pos.y, dims.dims.x, dims.dims.y)
shapeRenderer.end()
}
}
}
}
\ No newline at end of file
package tdt4240.netrunner.view.controllers
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import tdt4240.netrunner.game.GameController
import tdt4240.netrunner.model.game.EcsController
......@@ -10,6 +11,8 @@ class GroundRenderer(val gController: GameController, engine: EcsEngine) : EcsCo
override fun render() {
gController.game.shapeRenderer.apply {
begin(ShapeRenderer.ShapeType.Filled)
color = Color.WHITE
val ground = super.ecs.getEntitiesByComponent(GroundComponent::class.java)
for (block in ground) {
......
package tdt4240.netrunner.view.game
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.scenes.scene2d.ui.Dialog
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.Window
import tdt4240.netrunner.Netrunner
import tdt4240.netrunner.game.GameController
import tdt4240.netrunner.model.game.components.living.PlayerComponent
class PostGameLeaderboardRenderer(private val gController: GameController) {
private val stage = Stage(gController.game.uiViewport)
private val table = Table(gController.game.skin)
init {
table.setFillParent(true)
val window = Window("Leaderboard", gController.game.skin)
window.addActor(table)
window.width = Netrunner.MIN_WIDTH / 2f
window.height = Netrunner.MIN_HEIGHT * 2f / 3f
// Why the actual hell does centering a window require another table?
// This is so unnecessary
stage.addActor(Table(gController.game.skin).apply {
setFillParent(true)
center()
add(window).center().width(window.width).height(window.height)
})
}
fun render() {
val ecs = gController.engine
val players = ecs.getEntitiesByComponent(PlayerComponent::class.java)
var showList = false
val sortedPlayers = players.map {
it.getComponent(PlayerComponent::class.java)!!
}.onEach {
if (it.uid == gController.playerUid && it.finishPosition > 0) {
showList = true
}
}.map { it.finishPosition to it.username }.sortedBy { if (it.first < 0) 1000 else it.first}
if (showList) {
table.clear()
table.apply {
row().colspan(2)
add("Leaderboard", "default-font", Color.WHITE).pad(5f)
for (player in sortedPlayers) {
if (player.first == 0) continue
row()
add(player.second, "default-font", Color.WHITE).pad(5f)
add(if(player.first > 0) player.first.toString() else "DNF")
}
row()
}
gController.game.uiViewport.apply()
stage.act()
stage.draw()
}
}
fun dispose() {
stage.dispose()
}
}
\ No newline at end of file
package tdt4240.netrunner.view.controllers
package tdt4240.netrunner.view.util
import com.badlogic.gdx.graphics.g2d.TextureRegion
class Animation(val region: TextureRegion, val Count: Int, val cycleTime: Double) {
class Animation(val region: TextureRegion, val count: Int, val frameDuration: Double) {
private val frames = mutableListOf<TextureRegion>()
private var maxFrameTime = 0.0
private var currentFrameTime = 0.0
private var frameCount = 0
private var frame = 0
private var frameIndex = 0
private var timer = 0.0
init {
val frameWidth = region.regionWidth / Count
for (i in 0 until Count) {
val frameWidth = region.regionWidth / count
for (i in 0 until count) {
frames.add(TextureRegion(region, i * frameWidth, 0, frameWidth, region.regionHeight))
}
frameCount = Count
maxFrameTime = cycleTime / frameCount
}
fun update(dt: Double) {
currentFrameTime += dt
if (currentFrameTime > maxFrameTime){
frame++
fun update(deltaTime: Double) {
timer += deltaTime
while (timer > frameDuration) {
timer -= frameDuration
frameIndex++
if (frameIndex >= count) {
frameIndex = 0
}
}
if (frame >= frameCount)
frame = 0
}
fun getFrame(): TextureRegion {
return frames[frame]
return frames[frameIndex]
}
}
\ No newline at end of file
......@@ -6,7 +6,7 @@ import tdt4240.netrunner.Netrunner
object StageUtils {
fun injectBackground(stage: Stage) {
stage.addActor(Image(TextureRepository.parallaxTextures[0]).apply {
stage.addActor(Image(TextureRepository.menuBackground).apply {
x = 0f
y = 0f
width = Netrunner.MIN_WIDTH
......
......@@ -20,10 +20,13 @@ object TextureRepository {
Texture(Gdx.files.internal("tile-platform-right.png"))
)
val menuBackground = Texture(Gdx.files.internal("bg_menu_v2.png"))
val runningCharacterBitmaps: MutableMap<PlayerColor, Texture> = mutableMapOf()
val upButton = Texture(Gdx.files.internal("up.png"))
val downButton = Texture(Gdx.files.internal("down.png"))
val upButton = Texture(Gdx.files.internal("ui_up.png"))
val downButton = Texture(Gdx.files.internal("ui_down.png"))
val exitButton = Texture(Gdx.files.internal("ui_exit.png"))
init {
for (color in PlayerColor.values()) {
......
# Building, running, and deploying.
Building components can be done from the command line or your favorite IDE. This will not be demonstrated in detail, as this is fairly straight-forward for the components involved.
## Official server
Unless otherwise configured, the game connects to a centrally configured server. This server can be used for production use, requiring no further instructions.
## Setting up a debug environment
**Note:** this must be re-done on each network, as your IP will not remain constant. Redoing it on known networks may also be necessary depending on network settings, but you'll notice that if the game fails to connect.
For debug purposes, it's often more convenient to use a local server. Due to the IP issues this introduces, you're required to specify an IP to connect to.
This is done by creating `assets/local-game.properties`. This file must contain:
```
netrunner.server-addr=xxx.xxx.xxx.xxx
netrunner.server-port=5000
```
The port is 5000 by default in the debug build, so this generally doesn't need to be changed. To find the IP to use, this depends on which configuration you're running
### Desktop debugging
For desktop debugging, the IP can simply be `127.0.0.1`. The next option works regardless.
### Android debugging/remote server access
To connect to the server from Android, you have to specify the local IP. How you go about this depends on your OS:
* Linux: `ifconfig`, `ip a`, look for the correct network adapter, or `hostname -I`. Note that some commands may require additional packages. GUI options may exist for different distros, but linking all of them is an exercise in futility. That said, gnome-based DEs shows the IP in the network settings.
* Windows: `ipconfig`, or GUI options. See: https://www.digitalcitizen.life/find-ip-address-windows/ for instructions
* MacOS: `ifconfig`, or GUI options. See: https://www.wikihow.com/Find-Your-IP-Address-on-a-Mac for instructions
### Running the server
To run a debug version of the server, you can run `./gradlew server:dist` (or `.\gradlew.bat server:dist` on Windows)
## Deploying
**Note:** prior to deployment, specifically of the desktop and Android apps, `assets/game-local.properties` MUST be removed. This may change in the future if we figure out how to do conditional asset inclusion in different builds.
To deploy the project, the `:dist` target can be used. Example commands:
* Desktop: `./gradlew desktop:dist`
* Server: `./gradlew server:dist`
Android is an exception, and uses a different target: `./gradlew android:assembleRelease`
Note that `./gradlew` has to be replaced with `.\gradlew.bat` on Windows.
See also [LibGDX' own documentation on deployment](https://libgdx.com/wiki/deployment/deploying-your-application)
\ No newline at end of file
......@@ -3,6 +3,8 @@ package tdt4240.netrunner.model.game.components.dynamic
import tdt4240.netrunner.model.game.components.Component
import tdt4240.netrunner.model.util.Vec2f
data class VelocityComponent(var velocity: Vec2f) : Component {
data class VelocityComponent(
var velocity: Vec2f,
) : Component {
override val componentName: String = this::class.java.name
}
\ No newline at end of file
package tdt4240.netrunner.model.game.components.dynamic
import tdt4240.netrunner.model.game.components.Component
data class VelocityModifier(
// Required
val speedModifier: Float,
// Optional support data object
val id: Long = 0,
// Optional support data object
val timestamp: Long = 0,
) {
}
class VelocityModifierComponent(
/**
* <ModifierID, ModifierData>
* Note that the ModifierData consists of a List of VelocityModifiers, a support class defining
* common variables.
* Note that the usage of the variables is up to the controller. The exact meanings of the variables
* may vary by entity, but this aims to provide a unified interface.
*
* Invalid uses are not validated by the component, as this would be controller behavior extending
* far outside the scope of what can be reasoned to be within the scope of a data object.
*
*
*/
val mods: MutableMap<String, MutableList<VelocityModifier>> = mutableMapOf()
) : Component {
override val componentName: String = this::class.java.name
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment