Skip to content

Commit

Permalink
feat: 在线匹配与联机对战
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim-shop committed May 31, 2023
1 parent 9bcaaf2 commit 1e21061
Show file tree
Hide file tree
Showing 16 changed files with 191 additions and 24 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ dependencies {
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
implementation("com.google.android.material:material:1.9.0")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-scalars:2.1.0")
}
19 changes: 19 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,25 @@
android:stateNotNeeded="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="stateUnspecified|adjustUnspecified" />
<activity
android:name=".gui.PairingActivity"
android:allowEmbedded="true"
android:autoRemoveFromRecents="true"
android:colorMode="wideColorGamut"
android:documentLaunchMode="none"
android:finishOnTaskLaunch="false"
android:immersive="true"
android:label="@string/pairing_label"
android:launchMode="standard"
android:lockTaskMode="normal"
android:noHistory="false"
android:parentActivityName=".gui.MainActivity"
android:permission=""
android:screenOrientation="unspecified"
android:showForAllUsers="false"
android:stateNotNeeded="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="stateUnspecified|adjustUnspecified" />
<activity
android:name=".gui.ManageSpaceActivity"
android:allowEmbedded="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import java.security.MessageDigest
import java.time.Duration
import java.time.LocalDateTime

class LoginManager(context: Context) {
class LoginManager(private val context: Context) {

companion object Conf {
private const val SALT = "HITsz-Crazy-COMP2028"
Expand Down Expand Up @@ -49,6 +49,9 @@ class LoginManager(context: Context) {
val isLogin: Boolean
get() = accountInfo != null

val token: String?
get() = accountInfo?.token

suspend fun login(account: String, password: String): Boolean {
return try {
val result = LoginApi.api.login(account, encodePassword(password))
Expand Down Expand Up @@ -79,7 +82,7 @@ class LoginManager(context: Context) {
}
}

suspend fun requireLogin(context: Context): Boolean {
suspend fun requireLogin(): Boolean {
if (!isLogin && context is AppCompatActivity) {
// 唤起登录
val semaphore = Semaphore(1, 1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.imshit.aircraftwar.data.pairing

class PairingInfo(
val userId: Int,
val userAccount: String,
var isWilling: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ class AccountActivity : AppCompatActivity() {
companion object Api {
fun actionStart(context: Context) {
CoroutineScope(Dispatchers.Default).launch {
if (LoginManager(context).requireLogin(context)) {
if (LoginManager(context).requireLogin()) {
context.startActivity(Intent(context, AccountActivity::class.java).apply {

})
}
}
Expand Down
22 changes: 19 additions & 3 deletions app/src/main/java/net/imshit/aircraftwar/gui/GameActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,18 @@ import java.time.format.DateTimeFormatter

class GameActivity : AppCompatActivity() {
companion object Api {
fun actionStart(context: Context, gameMode: Difficulty, soundMode: Boolean) {
fun actionStart(
context: Context,
gameMode: Difficulty,
soundMode: Boolean,
onlineMode: Boolean,
roomId: Int = 0
) {
context.startActivity(Intent(context, GameActivity::class.java).apply {
putExtra("gameMode", gameMode)
putExtra("soundMode", soundMode)
putExtra("onlineMode", onlineMode)
putExtra("roomId", roomId)
})
}
}
Expand All @@ -49,12 +57,20 @@ class GameActivity : AppCompatActivity() {
// 获取配置
val soundMode: Boolean
val gameMode: Difficulty
val onlineMode: Boolean
val roomId: Int
intent.apply {
gameMode = getSerializableExtra("gameMode", Difficulty::class.java) ?: Difficulty.EASY
soundMode = getBooleanExtra("soundMode", true)
onlineMode = getBooleanExtra("onlineMode", true)
roomId = getIntExtra("roomId", 0)
}
val game: Games =
Games.getGames(this, gameMode, soundMode, object : Handler(Looper.getMainLooper()) {
val game: Games = Games.getGames(this,
gameMode,
soundMode,
onlineMode,
roomId,
object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
onGameOver(gameMode, msg.what)
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/net/imshit/aircraftwar/gui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ class MainActivity : AppCompatActivity() {
}

amBtnOffline.setOnClickListener {
GameActivity.actionStart(this@MainActivity, gameMode, amSwSound.isChecked)
GameActivity.actionStart(this@MainActivity, gameMode, amSwSound.isChecked, false)
}

amBtnOnline.setOnClickListener {
PairingActivity.actionStart(this@MainActivity, gameMode, amSwSound.isChecked)
}
}
}
Expand Down
16 changes: 14 additions & 2 deletions app/src/main/java/net/imshit/aircraftwar/logic/game/EasyGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ import android.os.Handler
import android.util.AttributeSet
import net.imshit.aircraftwar.element.generate.enemy.EasyEnemyGenerateStrategy

class EasyGame(context: Context, attrs: AttributeSet?, soundMode: Boolean, handler: Handler) :
class EasyGame(
context: Context,
attrs: AttributeSet?,
soundMode: Boolean,
onlineMode: Boolean,
roomId: Int,
handler: Handler
) :
Games(
context = context, attrs = attrs, soundMode = soundMode, handler = handler
context = context,
attrs = attrs,
soundMode = soundMode,
onlineMode = onlineMode,
roomId = roomId,
handler = handler,
) {

override val generateStrategy = EasyEnemyGenerateStrategy(this)
Expand Down
74 changes: 69 additions & 5 deletions app/src/main/java/net/imshit/aircraftwar/logic/game/Games.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import net.imshit.aircraftwar.R
import net.imshit.aircraftwar.data.fighting.CommunicateInfo
import net.imshit.aircraftwar.data.fighting.FightingClient
import net.imshit.aircraftwar.data.resource.ImageManager
import net.imshit.aircraftwar.element.AbstractFlyingObject
import net.imshit.aircraftwar.element.aircraft.enemy.BossEnemy
Expand All @@ -39,18 +41,31 @@ import net.imshit.aircraftwar.logic.music.MusicStrategies
import net.imshit.aircraftwar.logic.music.MuteMusicStrategy

sealed class Games(
context: Context, attrs: AttributeSet?, soundMode: Boolean, private val handler: Handler?
context: Context,
attrs: AttributeSet?,
soundMode: Boolean,
private val onlineMode: Boolean,
roomId: Int,
private val handler: Handler?
) : SurfaceView(context, attrs), SurfaceHolder.Callback, CoroutineScope by CoroutineScope(
Dispatchers.Default
) {
companion object {
fun getGames(
context: Context, gameMode: Difficulty, soundMode: Boolean, handler: Handler
context: Context,
gameMode: Difficulty,
soundMode: Boolean,
onlineMode: Boolean,
roomId: Int,
handler: Handler
): Games {
return when (gameMode) {
Difficulty.EASY -> EasyGame(context, null, soundMode, handler)
Difficulty.MEDIUM -> MediumGame(context, null, soundMode, handler)
Difficulty.HARD -> HardGame(context, null, soundMode, handler)
Difficulty.EASY -> EasyGame(context, null, soundMode, onlineMode, roomId, handler)
Difficulty.MEDIUM -> MediumGame(
context, null, soundMode, onlineMode, roomId, handler
)

Difficulty.HARD -> HardGame(context, null, soundMode, onlineMode, roomId, handler)
}
}

Expand All @@ -64,12 +79,17 @@ sealed class Games(
private const val SCORE_SIZE = 128f
private const val SCORE_X = 10f
private const val SCORE_Y = SCORE_SIZE + 10f

private const val REMOTE_SIZE = 64f
private const val REMOTE_X = SCORE_X
private const val REMOTE_Y = SCORE_Y + REMOTE_SIZE + 10f
}

// utils
private val heroController = HeroController(context)
lateinit var images: ImageManager
private var logicJob: Job? = null
private var client = if (onlineMode) FightingClient(context, roomId) else null

// configs
lateinit var background: Bitmap
Expand All @@ -84,6 +104,7 @@ sealed class Games(
private var time = 0
private var isGameOver = false
private var isStopped = true
private var remoteInfo: CommunicateInfo? = CommunicateInfo(0, 0, 0)

// variables
lateinit var heroAircraft: HeroAircraft
Expand All @@ -104,6 +125,13 @@ sealed class Games(
private val lifeBarElementLists = listOf(heroList, enemyAircrafts)

init {
client?.run()
client?.onData = {
this.remoteInfo = it
}
client?.onQuit = {
this.remoteInfo = null
}
// 等布局完成就开始获取图片等初始化工作
viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
Expand Down Expand Up @@ -188,6 +216,8 @@ sealed class Games(
this.crashCheck()
// 清理已损毁的飞行物
this.cleanInvalid()
// 发送得分
this.sendRemote()
// 检查游戏结束
if (this.heroAircraft.hp <= 0) {
this.gameOver()
Expand Down Expand Up @@ -270,6 +300,10 @@ sealed class Games(
}
}

private fun sendRemote() {
client?.send(CommunicateInfo(0, score, heroAircraft.hp))
}

private fun gameOver() {
stop()
this.isGameOver = true
Expand Down Expand Up @@ -336,12 +370,42 @@ sealed class Games(
)
}

private fun paintRemote(canvas: Canvas) {
if (onlineMode) {
this.paint.color = Color.WHITE
this.paint.textSize = REMOTE_SIZE
this.paint.typeface = Typeface.DEFAULT
if (remoteInfo != null) {
canvas.drawText(
context.getString(
R.string.game_canvas_text_other_info,
remoteInfo?.score,
remoteInfo?.life
),
REMOTE_X,
REMOTE_Y,
this.paint
)
} else {
canvas.drawText(
context.getString(R.string.game_canvas_text_other_offline),
REMOTE_X,
REMOTE_Y,
this.paint
)
}

}
}

private fun draw() {
val canvas = this.holder.lockCanvas() ?: return
// 循环绘制背景
paintBackground(canvas)
// 绘制得分
paintScore(canvas)
// 绘制远程得分
paintRemote(canvas)
// 绘制物件
paintObject(canvas)
// 绘制血条
Expand Down
16 changes: 14 additions & 2 deletions app/src/main/java/net/imshit/aircraftwar/logic/game/HardGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ import android.os.Handler
import android.util.AttributeSet
import net.imshit.aircraftwar.element.generate.enemy.HardEnemyGenerateStrategy

class HardGame(context: Context, attrs: AttributeSet?, soundMode: Boolean, handler: Handler) :
class HardGame(
context: Context,
attrs: AttributeSet?,
soundMode: Boolean,
onlineMode: Boolean,
roomId: Int,
handler: Handler
) :
Games(
context = context, attrs = attrs, soundMode = soundMode, handler = handler
context = context,
attrs = attrs,
soundMode = soundMode,
onlineMode = onlineMode,
roomId = roomId,
handler = handler
) {

override val generateStrategy = HardEnemyGenerateStrategy(this)
Expand Down
16 changes: 14 additions & 2 deletions app/src/main/java/net/imshit/aircraftwar/logic/game/MediumGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ import android.os.Handler
import android.util.AttributeSet
import net.imshit.aircraftwar.element.generate.enemy.MediumEnemyGenerateStrategy

class MediumGame(context: Context, attrs: AttributeSet?, soundMode: Boolean, handler: Handler) :
class MediumGame(
context: Context,
attrs: AttributeSet?,
soundMode: Boolean,
onlineMode: Boolean,
roomId: Int,
handler: Handler
) :
Games(
context = context, attrs = attrs, soundMode = soundMode, handler = handler
context = context,
attrs = attrs,
soundMode = soundMode,
onlineMode = onlineMode,
roomId = roomId,
handler = handler
) {

override val generateStrategy = MediumEnemyGenerateStrategy(this)
Expand Down
8 changes: 2 additions & 6 deletions app/src/main/res/layout/activity_account.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,12 @@
android:layout_width="240dp"
android:layout_height="240dp"
android:contentDescription="@string/avatar"
android:src="@drawable/avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:src="@drawable/avatar" />

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_constraintTop_toBottomOf="@id/aa_iv_avatar">
android:padding="8dp">

<TextView
android:id="@+id/aa_tv_name"
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/values-en/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<string name="app_description">HITSZ COMP2028 experiment.</string>
<string name="game_label">Gaming</string>
<string name="game_canvas_text_score">Score: %d</string>
<string name="game_canvas_text_other_info">Other score: %d Other life: %d</string>
<string name="game_canvas_text_other_offline">The other has quit.</string>
<string name="button_easy">Easy Mode</string>
<string name="button_medium">Medium Mode</string>
<string name="button_hard">Hard Mode</string>
Expand Down Expand Up @@ -37,4 +39,7 @@
<string name="dialog_login_input_password_error">Password may be wrong.</string>
<string name="button_logout">Logout</string>
<string name="avatar">Avatar</string>
<string name="pairing_label">Pairing</string>
<string name="button_pairing">Ask pairing</string>
<string name="button_pairing_sent">Request has been sent.</string>
</resources>
Loading

0 comments on commit 1e21061

Please sign in to comment.