Skip to content

Commit

Permalink
Release 1.2. Added setting "Respawn or final death of the balls"
Browse files Browse the repository at this point in the history
  • Loading branch information
andrzej-nov committed Apr 19, 2022
1 parent ecfdf8f commit 21f5735
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 118 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ The game has been implemented using following tools and libraries:
- [libKTX 1.10.0-rc1](https://libktx.github.io/)
- [ShapeDrawer 2.5.0](https://github.com/earlygrey/shapedrawer#shape-drawer)
- [Universal Tween Engine 6.3.3](https://github.com/AurelienRibon/universal-tween-engine)
- Free icons from https://www.flaticon.com/

The `ios` module is present in the project and compiling, but I did not tested it because I do not have Apple devices
and tools for that. If you make it work, I would gratefully accept the pull request.
2 changes: 1 addition & 1 deletion ToDo.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
### Simple project TODO list

- [ ] Add the game setting to switch between unlimited game (respawning balls) and limited one (where balls do not respawn)
- [x] Add the game setting to switch between unlimited game (respawning balls) and limited one (where balls do not respawn)
6 changes: 3 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ android {
applicationId "com.andrzejn.chainrelations"
minSdkVersion 19
targetSdkVersion 30
versionCode 2
versionName "1.1"
versionCode 3
versionName "1.2"
multiDexEnabled true
}
compileOptions {
sourceCompatibility "8.0"
targetCompatibility "8.0"
coreLibraryDesugaringEnabled true
}
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.jvmTarget = "1.8"
buildTypes {
release {
minifyEnabled true
Expand Down
10 changes: 10 additions & 0 deletions assets/Main.atlas
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,13 @@ colors7
xy: 260, 850
size: 120,120
index: -1
recycle
rotate: false
xy: 0, 420
size: 150,150
index: -1
death
rotate: false
xy: 160, 420
size: 150,150
index: -1
Binary file modified assets/atlas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ allprojects {
apply plugin: "eclipse"
apply plugin: 'idea'

version = '1.1'
version = '1.2'
ext {
appName = "ChainRelations"
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/com/andrzejn/chainrelations/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ class Context(
val largeradius: TextureRegion get() = texture("largeradius")
val colors6: TextureRegion get() = texture("colors6")
val colors7: TextureRegion get() = texture("colors7")
val recycle: TextureRegion get() = texture("recycle")
val death: TextureRegion get() = texture("death")

/**
* Create a bitmap font with given size, base color etc. from the provided TrueType font.
Expand Down
117 changes: 45 additions & 72 deletions core/src/com/andrzejn/chainrelations/GameScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ class GameScreen(
// Initialize WorldConstants before creating the World
ctx.wc = WorldConstants(ctx.gs.ballsCount).also {
it.setValues(
Gdx.graphics.width.toFloat(),
Gdx.graphics.height.toFloat()
Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()
)
}
}
Expand All @@ -71,15 +70,14 @@ class GameScreen(
ctx.wc.ballsCount = ctx.gs.ballsCount
world = World(ctx)
maxConnLen = ctx.gs.maxRadius
if (loadSavedGame)
try {
val s = ctx.sav.savedGame()
ctx.sav.loadSettingsAndScore(s)
world.deserialize(s)
} catch (ex: Exception) {
// Something wrong. Just recreate new World and start new game
world = World(ctx)
}
if (loadSavedGame) try {
val s = ctx.sav.savedGame()
ctx.sav.loadSettingsAndScore(s)
world.deserialize(s)
} catch (ex: Exception) {
// Something wrong. Just recreate new World and start new game
world = World(ctx)
}
resize(ctx.wc.width.toInt(), ctx.wc.height.toInt())
}

Expand Down Expand Up @@ -151,11 +149,9 @@ class GameScreen(
* Autosaves the game every 5 seconds
*/
private fun autoSaveGame() {
if (!thereWasAMove)
return
if (!thereWasAMove) return
val t = Calendar.getInstance().timeInMillis
if (t - lastGameSave < 5000)
return
if (t - lastGameSave < 5000) return
ctx.sav.saveGame(world)
thereWasAMove = false
lastGameSave = t
Expand All @@ -166,8 +162,7 @@ class GameScreen(
*/
private fun ballBlinked() {
suitableTargets = calcSuitableTargets(pointedBall, dragFrom)
if (suitableTargets?.isEmpty() == true)
cleanDragState(false)
if (suitableTargets?.isEmpty() == true) cleanDragState(false)
}

/**
Expand All @@ -178,8 +173,7 @@ class GameScreen(
try {
world.moveBalls(delta)
autoSaveGame()
if (!inShowAMove)
world.blinkRandomBall { ballBlinked() }
if (!inShowAMove) world.blinkRandomBall { ballBlinked() }
ctx.tweenManager.update(delta)
world.balls.filter { it.inBlink || it.inDeath }.forEach { it.updateEyeCoords() }
} catch (ex: Exception) {
Expand All @@ -194,10 +188,7 @@ class GameScreen(
ctx.sd.filledRectangle(0f, 0f, ctx.wc.buttonSize + 5f, ctx.wc.height)
ctx.sd.filledRectangle(ctx.wc.width - ctx.wc.buttonSize - 5f, 0f, ctx.wc.buttonSize + 5f, ctx.wc.height)
ctx.sd.filledRectangle(
ctx.wc.buttonSize + 5f,
0f,
ctx.wc.width - 2 * (ctx.wc.buttonSize + 5f),
ctx.wc.fontHeight.toFloat() + 5f
ctx.wc.buttonSize + 5f, 0f, ctx.wc.width - 2 * (ctx.wc.buttonSize + 5f), ctx.wc.fontHeight.toFloat() + 5f
)

val dF = dragFrom
Expand All @@ -207,9 +198,8 @@ class GameScreen(
world.drawConnectors()
world.balls.filter { dF != null || it != pB }.forEach {
setBallSpriteBounds(it.drawCoord, 1f)
ball.color =
if (dF != null && suitableTargets?.contains(it) == true) ctx.theme.dark[dF.color]
else ctx.theme.ballColor
ball.color = if (dF != null && suitableTargets?.contains(it) == true) ctx.theme.dark[dF.color]
else ctx.theme.ballColor
ball.draw(ctx.batch, it.alpha)
it.drawDetails()
}
Expand All @@ -233,14 +223,20 @@ class GameScreen(
)
}
// Draw buttons, scores and hand sprite on show-a-move
if (world.balls.size <= 6) ctx.sd.filledRectangle(
play.x - 5,
play.y - 5,
play.width + 10,
play.height + 10,
ctx.theme.scorePoints
)
play.draw(ctx.batch)
help.draw(ctx.batch)
hit.draw(ctx.batch)
exit.draw(ctx.batch)
home.draw(ctx.batch)
ctx.score.draw(ctx.batch)
if (inShowAMove)
hand.draw(ctx.batch)
if (inShowAMove) hand.draw(ctx.batch)
if (ctx.batch.isDrawing) ctx.batch.end()
}

Expand Down Expand Up @@ -283,8 +279,7 @@ class GameScreen(
*/
private fun pointTheBall(b: Ball?) {
pointedBall = b
if (b == null)
return
if (b == null) return
pointedBallCenter.set(b.drawCoord)
world.clampCoord(pointedBallCenter, ctx.wc.radius * 2)
}
Expand All @@ -301,8 +296,7 @@ class GameScreen(
*/
private fun showAMove() {
cleanDragState(true)
if (inShowAMove)
return
if (inShowAMove) return
inShowAMove = true
val dF = world.balls.filter { !it.inBlink && !it.inDeath }.flatMap { it.sockets }.filter { it.conn == null }
.shuffled().firstOrNull { calcSuitableTargets(it.ball, it)?.isNotEmpty() == true }
Expand All @@ -311,16 +305,13 @@ class GameScreen(
return
}

Timeline.createSequence()
.push(Tween.call { _, _ ->
Timeline.createSequence().push(Tween.call { _, _ ->
hand.setPosition(
help.x + help.width / 2 - hand.width / 2,
help.y + help.height / 2 - hand.height
help.x + help.width / 2 - hand.width / 2, help.y + help.height / 2 - hand.height
)
})
.push(Tween.to(hand, TW_POS_XY, 1f).target(dF.ball.coord.x - hand.width / 2, dF.ball.coord.y - hand.height))
.push(Tween.call { _, _ -> pointTheBall(dF.ball) })
.setCallback { _, _ -> showAMoveMiddle(dF) }
.push(Tween.call { _, _ -> pointTheBall(dF.ball) }).setCallback { _, _ -> showAMoveMiddle(dF) }
.start(ctx.tweenManager)
}

Expand All @@ -329,17 +320,13 @@ class GameScreen(
*/
private fun showAMoveMiddle(dF: BaseSocket) {
val dFCoord = Vector2(dF.coord).scl(2f).add(pointedBallCenter)
Timeline.createSequence()
.pushPause(0.2f)
Timeline.createSequence().pushPause(0.2f)
.push(Tween.to(hand, TW_POS_XY, 0.5f).target(dFCoord.x - hand.width / 2, dFCoord.y - hand.height))
.pushPause(0.3f)
.push(Tween.call { _, _ ->
.pushPause(0.3f).push(Tween.call { _, _ ->
dragFrom = dF
suitableTargets = calcSuitableTargets(dF.ball, dF)
dTforShow = suitableTargets?.firstOrNull { !it.inDeath && !it.inBlink }
})
.setCallback { _, _ -> showAMoveFinalize() }
.start(ctx.tweenManager)
}).setCallback { _, _ -> showAMoveFinalize() }.start(ctx.tweenManager)
}

/**
Expand All @@ -352,30 +339,24 @@ class GameScreen(
inShowAMove = false
return
}
Timeline.createSequence()
.push(
Tween.to(hand, TW_POS_XY, 1f)
.target(dT.coord.x - hand.width / 2, dT.coord.y - hand.height)
)
.pushPause(0.2f)
.push(Tween.call { _, _ ->
Timeline.createSequence().push(
Tween.to(hand, TW_POS_XY, 1f).target(dT.coord.x - hand.width / 2, dT.coord.y - hand.height)
).pushPause(0.2f).push(Tween.call { _, _ ->
val dF = dragFrom
if (dF != null) {
world.addConnector(dF, dT)
thereWasAMove = true
}
cleanDragState(true)
inShowAMove = false
})
.start(ctx.tweenManager)
}).start(ctx.tweenManager)
}

/**
* Clean up after drag end.
*/
private fun cleanDragState(cleanPointedBall: Boolean) {
if (cleanPointedBall)
pointedBall = null
if (cleanPointedBall) pointedBall = null
dragFrom = null
suitableTargets = null
dragTo.set(-1f, -1f)
Expand All @@ -386,24 +367,20 @@ class GameScreen(
* and maximum drag radius from the game settings
*/
private fun calcSuitableTargets(
pB: Ball?,
dF: BaseSocket?
pB: Ball?, dF: BaseSocket?
): Set<Ball>? {
if (pB == null || dF == null)
return null
if (pB == null || dF == null) return null
val dragFromCoord = dF.absDrawCoord()
return world.balls.filter { b ->
b != pB && b.coord.dst(dragFromCoord) < (maxConnLen + 1) * ctx.wc.radius // other balls in range
&& b.sockets.mapNotNull { s -> s.conn }
.none { c -> c.inSocket.ball == pB || c.outSocket.ball == pB }
// not connected to the pointed ball yet
&& (if (dF is InSocket) b.outSock else b.inSock)
.any { s ->
&& (if (dF is InSocket) b.outSock else b.inSock).any { s ->
s.conn == null && s.color == dF.color && s.absDrawCoord()
.dst(dragFromCoord) < maxConnLen * ctx.wc.radius
} // and matching free connector in range
}
.toSet()
}.toSet()
}

/**
Expand All @@ -427,15 +404,12 @@ class GameScreen(
override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
if (inShowAMove) return super.touchDragged(screenX, screenY, pointer)
val v = ctx.pointerPosition(input.x, input.y)
if (pointedBall == null)
pointTheBall(world.ballPointedBy(v))
if (pointedBall == null) pointTheBall(world.ballPointedBy(v))
if (pointedBall != null && dragFrom == null) {
setDragFrom(v)
if (dragFrom == null && pointedBallCenter.dst(v) > ctx.wc.radius * 2)
pointedBall = null
if (dragFrom == null && pointedBallCenter.dst(v) > ctx.wc.radius * 2) pointedBall = null
}
if (dragFrom != null)
setDragTo(v)
if (dragFrom != null) setDragTo(v)
return super.touchDragged(screenX, screenY, pointer)
}

Expand Down Expand Up @@ -478,8 +452,7 @@ class GameScreen(
val v1 = v.minus(pointedBallCenter).scl(0.5f)
val dF = pB.sockets.firstOrNull { it.conn == null && it.coord.epsilonEquals(v1, ctx.wc.radius * 0.3f) }
dragFrom = dF
if (dF == null)
return
if (dF == null) return
suitableTargets = calcSuitableTargets(pB, dF)
if (suitableTargets?.isEmpty() == true) // No suitable targets, do not start drag from this socket
dragFrom = null
Expand Down
Loading

0 comments on commit 21f5735

Please sign in to comment.