Compare commits

...

6 Commits

10 changed files with 4636 additions and 140 deletions

View File

@@ -1,16 +1,16 @@
package com.badlogic.drop.android
import android.os.Bundle
import com.badlogic.drop.Drop
import com.badlogic.gdx.backends.android.AndroidApplication
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration
import com.badlogic.drop.Main
/** Launches the Android application. */
class AndroidLauncher : AndroidApplication() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initialize(Main(), AndroidApplicationConfiguration().apply {
initialize(Drop(), AndroidApplicationConfiguration().apply {
// Configure your application here.
useImmersiveMode = true // Recommended, but not required.
})

View File

@@ -0,0 +1,34 @@
package com.badlogic.drop
import com.badlogic.gdx.Game
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.viewport.FitViewport
class Drop : Game() {
lateinit var batch: SpriteBatch
lateinit var font: BitmapFont
//lateinit var viewport: FitViewport // Use camera instead of viewport
override fun create(): Unit {
batch = SpriteBatch()
//viewport = FitViewport(8f, 5f)
/*font = BitmapFont().apply {
setUseIntegerPositions(false)
data.scale(viewport.worldHeight / Gdx.graphics.height)
}*/
font = BitmapFont()
this.screen = MainMenuScreen(this)
}
override fun render() {
super.render()
}
override fun dispose() {
batch.dispose()
font.dispose()
}
}

View File

@@ -0,0 +1,146 @@
package com.badlogic.drop
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Screen
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.Gdx.audio
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.Sprite
import com.badlogic.gdx.math.MathUtils
import com.badlogic.gdx.math.Rectangle
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.math.Vector3
import com.badlogic.gdx.utils.ScreenUtils
import ktx.assets.toInternalFile
class GameScreen(val game: Drop) : Screen {
val camera = OrthographicCamera().apply { setToOrtho(false, 800f, 480f) }
val backgroundTexture = Texture("background.png")
val bucketTexture = Texture("bucket.png")
val dropTexture = Texture("drop.png")
val dropSound = audio.newSound("drop.mp3".toInternalFile())
val music = audio.newMusic("music.mp3".toInternalFile()).apply {
isLooping = true
volume = 0.5f
}
val bucketSprite = Sprite(bucketTexture).apply { setSize(64f, 64f) }
val touchPos = Vector3()
val dropSprites = mutableListOf<Sprite>()
val dropSpeed = -128f
var dropTimer = 0f
val bucketRectangle = Rectangle()
val dropRectangle = Rectangle()
var dropsGathered = 0
override fun show() {
music.play()
}
override fun render(delta: Float) {
input()
logic()
draw()
}
private fun input() {
val speed = 4f
val delta: Float = Gdx.graphics.deltaTime
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
bucketSprite.translateX(speed * delta)
}
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
bucketSprite.translateX(-speed * delta)
}
if (Gdx.input.isTouched) {
touchPos.set(Gdx.input.x.toFloat(), Gdx.input.y.toFloat(), 0f)
camera.unproject(touchPos)
bucketSprite.setCenterX(touchPos.x)
}
}
private fun logic() {
val worldWidth: Float = camera.viewportWidth
val worldHeight: Float = camera.viewportHeight
val bucketWidth: Float = bucketSprite.width
val bucketHeight: Float = bucketSprite.height
val delta: Float = Gdx.graphics.deltaTime
bucketSprite.x = MathUtils.clamp(bucketSprite.x, 0f, worldWidth - bucketWidth)
bucketRectangle.set(bucketSprite.x, bucketSprite.y, bucketWidth, bucketHeight)
for (dropSprite in dropSprites.reversed()) {
val dropWidth: Float = dropSprite.width
val dropHeight: Float = dropSprite.height
dropSprite.translateY(dropSpeed * delta * (MathUtils.clamp((dropsGathered.toFloat() + 1f) / 5f, 1f, Float.MAX_VALUE)))
dropRectangle.set(dropSprite.x, dropSprite.y, dropWidth, dropHeight)
if (dropSprite.y < -dropHeight) dropSprites.remove(dropSprite)
else if (bucketRectangle.overlaps(dropRectangle)) {
dropsGathered++
dropSprites.remove(dropSprite)
dropSound.play()
}
}
dropTimer += delta
if (dropTimer > 1f) {
dropTimer = 0f
createDroplet()
}
}
private fun draw() {
ScreenUtils.clear(Color.BLACK)
camera.update()
game.batch.projectionMatrix = camera.combined
game.batch.begin()
val worldWidth: Float = camera.viewportWidth
val worldHeight: Float = camera.viewportHeight
game.batch.draw(backgroundTexture, 0f, 0f, worldWidth, worldHeight)
bucketSprite.draw(game.batch)
game.font.draw(game.batch, "Drops collected: $dropsGathered", 0f, worldHeight)
for (dropSprite in dropSprites) dropSprite.draw(game.batch)
game.batch.end()
}
private fun createDroplet() {
val dropWidth = 64f
val dropHeight = 64f
val worldWidth: Float = camera.viewportWidth
val worldHeight: Float = camera.viewportHeight
val dropSprite = Sprite(dropTexture).apply {
setSize(dropWidth, dropHeight)
x = MathUtils.random(0f, worldWidth - dropWidth)
y = worldHeight
}
dropSprites.add(dropSprite)
}
override fun resize(width: Int, height: Int) = Unit
override fun pause() = Unit
override fun resume() = Unit
override fun hide() = Unit
override fun dispose() {
backgroundTexture.dispose()
dropSound.dispose()
music.dispose()
dropTexture.dispose()
bucketTexture.dispose()
}
}

View File

@@ -1,138 +0,0 @@
package com.badlogic.drop
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Gdx.audio
import com.badlogic.gdx.audio.Music
import com.badlogic.gdx.audio.Sound
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.Texture.TextureFilter.Linear
import com.badlogic.gdx.graphics.g2d.Sprite
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.math.MathUtils
import com.badlogic.gdx.math.Rectangle
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.utils.ScreenUtils
import com.badlogic.gdx.utils.viewport.FitViewport
import ktx.app.KtxGame
import ktx.app.KtxScreen
import ktx.assets.disposeSafely
import ktx.assets.toInternalFile
import ktx.async.KtxAsync
import ktx.graphics.use
class Main : KtxGame<KtxScreen>() {
override fun create() {
KtxAsync.initiate()
addScreen(FirstScreen())
setScreen<FirstScreen>()
}
class FirstScreen : KtxScreen {
private val backgroundTexture: Texture = Texture("background.png".toInternalFile(), true).apply { setFilter(Linear, Linear) }
private val bucketTexture: Texture = Texture("bucket.png".toInternalFile(), true).apply { setFilter(Linear, Linear) }
private val dropTexture: Texture = Texture("drop.png".toInternalFile(), true).apply { setFilter(Linear, Linear) }
private val dropSound: Sound = audio.newSound("drop.mp3".toInternalFile())
private val music: Music = audio.newMusic("music.mp3".toInternalFile()).apply {
isLooping = true
volume = 0.5f
play()
}
private val bucketSprite: Sprite = Sprite(bucketTexture).apply { setSize(1f, 1f) }
private val image = Texture("logo.png".toInternalFile(), true).apply { setFilter(Linear, Linear) }
private val batch = SpriteBatch()
private val viewport = FitViewport(8f, 5f)
private var touchPos: Vector2 = Vector2()
private var dropletSpriteList: MutableList<Sprite> = mutableListOf()
private var dropTimer: Float = 0f
private val bucketRectangle: Rectangle = Rectangle()
private val dropletRectangle: Rectangle = Rectangle()
override fun resize(width: Int, height: Int) {
viewport.update(width, height, true)
}
override fun render(delta: Float) {
input()
logic()
draw()
}
override fun dispose() {
image.disposeSafely()
batch.disposeSafely()
}
private fun input() {
if (Gdx.input.isTouched) {
touchPos.set(Gdx.input.x.toFloat(), Gdx.input.y.toFloat())
viewport.unproject(touchPos)
bucketSprite.setCenterX(touchPos.x)
}
}
private fun logic() {
val worldWidth: Float = viewport.worldWidth
bucketSprite.x = MathUtils.clamp(bucketSprite.x, 0f, worldWidth - bucketSprite.width)
val delta: Float = Gdx.graphics.deltaTime
bucketRectangle.set(bucketSprite.x, bucketSprite.y, bucketSprite.width, bucketSprite.height)
for (sprite in dropletSpriteList.reversed()) {
sprite.translateY(-2f * delta)
dropletRectangle.set(sprite.x, sprite.y, sprite.width, sprite.height)
if (sprite.y < -sprite.height) dropletSpriteList.remove(sprite)
else if (bucketRectangle.overlaps(dropletRectangle)) {
dropletSpriteList.remove(sprite)
dropSound.play()
}
}
dropTimer += delta
if (dropTimer >= 1f)
{
dropTimer = 0f
dropletSpriteList.add(createDroplet())
}
//Gdx.app.log("Droplets", dropletSpriteList.size.toString() + " in List")
}
private fun draw() {
ScreenUtils.clear(Color.BLACK)
viewport.apply()
batch.setProjectionMatrix(viewport.camera.combined)
batch.use {
it.draw(backgroundTexture, 0f, 0f, viewport.worldWidth, viewport.worldHeight)
for (s in dropletSpriteList) {
s.draw(it)
}
bucketSprite.draw(it)
}
}
private fun createDroplet(): Sprite {
val dropWidth: Float = 1f
val dropHeight: Float = 1f
val worldWidth: Float = viewport.worldWidth
val worldHeight: Float = viewport.worldHeight
val dropSprite: Sprite = Sprite(dropTexture).apply {
setSize(dropWidth, dropHeight)
x = MathUtils.random(0f, worldWidth - dropWidth)
y = worldHeight //- dropHeight // Spawn droplet above world for cleaner transition in?
} // We don't add the object to the set here because apply is really meant for doing stuff with members
return dropSprite
}
}
}

View File

@@ -0,0 +1,48 @@
package com.badlogic.drop
import com.badlogic.gdx.Screen
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.utils.ScreenUtils
class MainMenuScreen(val game: Drop) : Screen {
private val camera = OrthographicCamera()
init {
camera.setToOrtho(false, 800f, 480f)
}
override fun show() = Unit
override fun render(delta: Float) {
ScreenUtils.clear(Color.BLACK)
//game.viewport.apply()
//game.batch.projectionMatrix = game.viewport.camera.combined
camera.update()
game.batch.projectionMatrix = camera.combined
game.batch.begin()
game.font.draw(game.batch, "Welcome to Drop!!!", 100f, 150f)
game.font.draw(game.batch, "Tap anywhere to begin!", 100f, 100f)
game.batch.end()
if (Gdx.input.isTouched) {
game.screen = GameScreen(game)
}
}
override fun resize(width: Int, height: Int) = Unit
override fun pause() = Unit
override fun resume() = Unit
override fun hide() = Unit
override fun dispose() = Unit
}

View File

@@ -0,0 +1,180 @@
import com.sun.org.apache.xpath.internal.operations.Bool
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
fun String.isPalindrome(): Boolean {
var treatedString: String = String()
this.forEach { char ->
if (char.isLetterOrDigit()) treatedString += char.uppercase()
}
return treatedString == treatedString.reversed()
}
fun calculateVolumeOfSphere(radius: Double): Double {
return (4.0 / 3.0) * Math.PI * (radius * radius * radius)
}
fun ageMessage(age: Int) {
when (age) {
in Int.MIN_VALUE..<0 -> println("Unborn")
in 0..11 -> println("Young Kid")
in 12..17 -> println("Teenager")
18 -> println("18 Year Old")
in 19..21 -> println("Young Adult")
in 22..64 -> println("Adult")
in 65..Int.MAX_VALUE -> println("Elderly")
}
}
fun powerCalculator(base: Int, exponent: Int): Float {
var result: Float = base.toFloat()
if (base == 0) return 0f
else if (exponent == 0) return 1f
if (exponent < 0) {
for (loop in exponent..<-1) {
result *= base
}
result = 1 / result
} else {
for (loop in 1..<exponent) {
result *= base
}
}
return result
}
fun Int.isPrime(): Boolean {
if (this == 0) return false
var tmpVar: Int = this
if (this < 0) tmpVar *= -1
for (loop in 2..tmpVar / 2) {
if (tmpVar % loop == 0) return false
}
return true
}
fun splitList(list: List<Int>): Array<List<Int>> {
val newList1: MutableList<Int> = mutableListOf<Int>()
val newList2: MutableList<Int> = mutableListOf<Int>()
for (loop in 0..<list.size) {
if (loop % 2 == 0) newList1.add(list[loop])
else newList2.add(list[loop])
}
return arrayOf(newList1, newList2)
}
class Rectangle(var width: Double, var height: Double) {
init {
println("Created new Rectangle with a width of $width and a height of $height")
}
fun area(): Double {
return width * height
}
fun perimeter(): Double {
return width * 2 + height * 2
}
fun isSquare(): Boolean {
return width == height
}
}
fun main() {
// 1
//println("Hello, world!!!")
// 2
//var myFloat: Float = 10f
//myFloat *= 5
//println(myFloat)
// 3
//val myInt1: Int = (1..10).random()
//val myInt2: Int = (1..10).random()
//val myInt3: Int = myInt1 / myInt2
//println(myInt1.toString() + " / " + myInt2.toString() + " = " + myInt3.toString())
// 4
//val myString: String = "My String"
//println(myString.uppercase().reversed())
// 5
//val myInt1: Int = 2
//val myInt2: Int = 2
//val myInt3: Int = 4
//val myInt4: Int = 4
//if ((myInt1 == myInt2 || myInt3 == myInt4) && myInt3 > myInt2) println("Either the first two integers or the last two integers are equal, and the 3rd integer is larger that the 2nd.")
// 6
//val myPalindrome: String = "Satan, oscillate my metallic sonatas."
//println(myPalindrome.isPalindrome())
// 7
//println(calculateVolumeOfSphere(2.0))
// 8
//val myIntArray: Array<Int> = arrayOf(1, 2, 3, 4, 5)
//val myStringArray: Array<String> = arrayOf("One", "Two", "Three", "Four", "Five")
//myIntArray.forEach { println(it) }
//myStringArray.forEach { println(it) }
// 9
//val myString: String = "Hello"
//val loopAmount: Int = 5
//var currentLoop: Int = 0
//while (currentLoop < loopAmount) {
// println(myString)
// currentLoop++
//}
// 10
//for (number in 1..42) println(number)
// 11
//(65..90).forEach { println(it.toChar()) }
// 12
//val myArray: Array<Int> = arrayOf(5, 10, 5)
//var myResult: Int = 0
//myArray.forEach { myResult += it }
//println(myResult)
// 13
//val myArray: Array<Int> = arrayOf(5, 10, 2)
//val myMutableList: MutableList<Int> = mutableListOf()
//for (entry in myArray.reversed()) myMutableList += entry
//myMutableList.forEach { println(it) }
// 14
//ageMessage(14)
// 15
//println(powerCalculator(-2, 3))
// 16
//println(887.isPrime())
// 17
//val myRectangle: Rectangle = Rectangle(5.0, 10.0)
//println(myRectangle.area())
//println(myRectangle.perimeter())
//println(myRectangle.isSquare())
// 18
//val myList: List<Int> = listOf<Int>(1, 4, 6, 7, 8)
//val myArray: Array<List<Int>> = splitList(myList)
//for (item in myArray) {
// for (subItem in item) println(subItem)
//}
}

View File

@@ -0,0 +1,34 @@
### Outline
1. Gameplay
1. Co-Op
2. Competitive
3. Combat
4. The Witch
### Gameplay
The game will be played with the phone held vertically. The game will be a roguelite with a both a cooperative and a competitive game mode.
Players must enter a dungeon with another player in order to kill The Witch. They may enter with some prepared items in Co-op, but will be going in without in the competitive game mode.
Upon entering a room, the player will be given some options. If there are enemies, they will be given the option to fight them. They will be given the option to search clutter if it exists within the room. Any chests can be looted with additional chests maybe reviled behind clutter. All of these activities can reward equipment that can be used in future fights/games.
After the player determines they have sufficiently cleared the room they can pick adjacent rooms. Players can end up meeting each other if they end up picking the same room. Players may group up and take rooms together. In co-op this will result in the loot being split. In competitive this will result in the person who initiated the action, if it was searching clutter or looting a chest, or dealt the final blow (maybe dice roll instead) in a combat encounter.
##### Co-Op
Players will be allowed to bring prepared items, such as potions, and must work with their partner to scour the dungeon in order to get powerful enough to kill The Witch. Some collected loot can be taken back and used in future runs if the players were not able to finish a run completely.
Players may be resurrected at certain interactable that may spawn randomly in rooms.
##### Competitive
Players must be quick but careful when doing a competitive run. The first person to kill The Witch wins.
Once a player is dead they are removed from the game. The final player can make it to the end and kill The Witch for a greater reward.
##### Combat
Upon entering a combat encounter the options available to the Player will change to reflect the actions they can take. They will be given the option to flee. This will take them to an adjacent room with all unclaimed loot in the previous room now locked to them (not chance based). Players will also have the option to attack with a certain action. If the enemy is shielded, the Player can use a shield-break attack. If the enemy is weak to fire they may use a fire attack if they have one etc. Attack options will depend on the players equipment.
##### The Witch
The Witch is the ultimate fight in a run. They will test the Player's breadth of held equipment. If the Player(s) do not have the equipment capable of taking down The Witch they will find the fight very difficult.
Players will be allowed to run and keep all the collected equipment should they manage to escape (Chance based?).
See [[Idea Whiteboard]] for more details. A [PNG version](Idea%20Whiteboard.png) and an [SVG Version](Idea%20Whiteboard.svg) are also available.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.7 MiB