Compare commits

..

9 Commits

Author SHA1 Message Date
TONY_All ef8e2391d6
fix rule logic 2023-06-10 22:02:00 +08:00
TONY_All 20b1004e62
fix test data 2023-06-10 21:06:13 +08:00
TONY_All 3d35de3fe3
add toString for Area 2023-06-10 19:40:14 +08:00
tony_all 764ead46c2 fix rule match
add debug
2023-06-07 16:05:45 +08:00
tony_all 84baa309e3 optimize rule structure 2023-06-07 15:54:37 +08:00
tony_all ed6f40b462 fix rule initialize
add dynamic rule when the wall falls
2023-06-07 15:47:36 +08:00
tony_all 45882c8cb6 add break & place check
optimize code logic of Teleport Listener
2023-06-07 15:34:44 +08:00
TONY_All 97c5c1c9fb sync with remote 2023-06-07 14:47:30 +08:00
tony_all 362941fe58 fix location
format code
2023-06-06 22:50:16 +08:00
8 changed files with 127 additions and 30 deletions

View File

@ -22,7 +22,6 @@ object BlastingCrisis : Plugin() {
override fun onEnable() { override fun onEnable() {
info("§a| §7Loading BlastingCrisis") info("§a| §7Loading BlastingCrisis")
DebugCommand.debug("debugcmd") DebugCommand.debug("debugcmd")
info("§a| §7Loading BlastingCrisis")
createDefaultGame() createDefaultGame()
} }
@ -40,12 +39,13 @@ object BlastingCrisis : Plugin() {
location(0, 72, -44), location(0, 72, -44),
location(0, 72, -44), location(0, 72, -44),
location(-8, 73, -47), location(-8, 73, -47),
Area(location(14, 72, -48), location(-13, 78, -34)), Area(location(14, 72, -48), location(-14, 78, -34)),
Area(location(-13, 78, -33), location(13, 72, -33)), Area(location(-13, 78, -33), location(13, 72, -33)),
Area(location(-13, 78, -32), location(13, 71, -1)), Area(location(-13, 78, -32), location(13, 72, -1)),
Area(location(7, 72, -45), location(9, 74, -48)), Area(location(7, 72, -45), location(9, 74, -48)),
listOf( listOf(
Area(location(15, 72, -1), location(19, 78, -48)), Area(location(19, 78, -48), location(-15, 72, -1)) Area(location(15, 72, -1), location(19, 78, -48)),
Area(location(-19, 78, -48), location(-15, 72, -1))
), ),
) )
val teamGreen = MapTeam( val teamGreen = MapTeam(
@ -54,12 +54,13 @@ object BlastingCrisis : Plugin() {
location(0, 72, 44), location(0, 72, 44),
location(0, 72, 44), location(0, 72, 44),
location(8, 73, 47), location(8, 73, 47),
Area(location(-14, 72, 48), location(13, 78, 34)), Area(location(-14, 72, 48), location(14, 78, 34)),
Area(location(13, 78, 33), location(-13, 72, 33)), Area(location(13, 78, 33), location(-13, 72, 33)),
Area(location(13, 78, 32), location(-13, 71, 1)), Area(location(13, 78, 32), location(-13, 72, 1)),
Area(location(-7, 72, 45), location(-9, 74, 48)), Area(location(-7, 72, 45), location(-9, 74, 48)),
listOf( listOf(
Area(location(-15, 72, 1), location(-19, 78, 48)), Area(location(-19, 78, 48), location(15, 72, 1)) Area(location(-15, 72, 1), location(-19, 78, 48)),
Area(location(19, 78, 48), location(15, 72, 1))
), ),
) )
val map = GameMap( val map = GameMap(

View File

@ -15,6 +15,7 @@ class Game(
val scoreboard: GameScoreboard = GameScoreboard(this) val scoreboard: GameScoreboard = GameScoreboard(this)
val timer: GameTimer = GameTimer(this) val timer: GameTimer = GameTimer(this)
val players = ArrayList<Player>() val players = ArrayList<Player>()
val placeBreakRule = GamePlaceBreakRule(this)
var state: GameState = GameState.WAITING var state: GameState = GameState.WAITING
private fun autoJoinTeam() { private fun autoJoinTeam() {
@ -47,6 +48,8 @@ class Game(
fun start() { fun start() {
debug("game ${map.name} started.") debug("game ${map.name} started.")
placeBreakRule.defaultRule()
state = GameState.START state = GameState.START
timer.startTimer() timer.startTimer()
timer.submitEvent("wall_fall", Duration.ofMinutes(1)) { timer.submitEvent("wall_fall", Duration.ofMinutes(1)) {
@ -55,6 +58,9 @@ class Game(
map.wall.forBlocksInArea().forEach { map.wall.forBlocksInArea().forEach {
it.block.type = Material.AIR it.block.type = Material.AIR
} }
placeBreakRule.addRule("allow_center_wall") { _, _, loc ->
map.wall.contains(loc)
}
} }
} }
autoJoinTeam() autoJoinTeam()

View File

@ -0,0 +1,59 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.game.GamePlaceBreakRule.ActionType.BREAK
import cc.maxmc.blastingcrisis.misc.debug
import org.bukkit.Location
import org.bukkit.entity.Player
class GamePlaceBreakRule(val game: Game) {
enum class ActionType {
PLACE,
BREAK
}
private val rules = HashMap<String, (Player, ActionType, Location) -> Boolean>()
fun matchRule(player: Player, actionType: ActionType, location: Location): Boolean {
// match rules
rules.forEach { (name, func) ->
if (func(player, actionType, location)) {
debug("Hit rule $name")
return true
}
}
// fallback strategy
return false
}
fun addRule(name: String, rule: (player: Player, type: ActionType, loc: Location) -> Boolean) {
if (rules.containsKey(name)) {
throw IllegalArgumentException("Rule $name already exists.")
}
debug("rule $name added")
rules[name] = rule
}
fun defaultRule() {
// allow mine
addRule("allow_mine") { _, _, loc ->
game.map.teams.flatMap {
it.sides + it.mine
}.map {
it.containsBlock(loc)
}.reduce { a, b -> a || b }
}
// allow enemy wall
addRule("allow_wall_conditional") { player, type, loc ->
val team = player.team ?: return@addRule false
val isWall = game.teams
.map {
it.teamInfo.wall.containsBlock(loc).also { result -> debug("area ${it.teamInfo.wall} contains $loc is $result") }
}
.reduce { a, b -> a || b }
if (!isWall) return@addRule false
val isHome = team.teamInfo.wall.containsBlock(loc)
(type == BREAK) xor isHome
}
}
}

View File

@ -20,6 +20,12 @@ class GameTeam(val game: Game, val teamInfo: MapTeam) {
GameListener.interactSubscribed[teamInfo.upgrade] = { GameListener.interactSubscribed[teamInfo.upgrade] = {
debug("interact team ${teamInfo.name} upgrade.") debug("interact team ${teamInfo.name} upgrade.")
} }
GameListener.moveSubscribed.add(teamInfo.teleport to {
val player = it.player ?: throw IllegalStateException("Bukkit API LOL")
debug("teleporting $player to battle field")
val team = player.team ?: throw IllegalStateException("Player ${player.name} should have a team")
player.teleport(team.teamInfo.mine.randomLocation().toPlayerLocation())
})
} }
fun join(player: Player) { fun join(player: Player) {

View File

@ -1,26 +1,28 @@
package cc.maxmc.blastingcrisis.listener package cc.maxmc.blastingcrisis.listener
import cc.maxmc.blastingcrisis.game.team import cc.maxmc.blastingcrisis.game.GamePlaceBreakRule
import cc.maxmc.blastingcrisis.misc.Area
import cc.maxmc.blastingcrisis.misc.GameManager import cc.maxmc.blastingcrisis.misc.GameManager
import cc.maxmc.blastingcrisis.misc.debug import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.misc.toPlayerLocation
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.event.entity.EntityExplodeEvent import org.bukkit.event.entity.EntityExplodeEvent
import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerMoveEvent import org.bukkit.event.player.PlayerMoveEvent
import taboolib.common.platform.event.SubscribeEvent import taboolib.common.platform.event.SubscribeEvent
import taboolib.platform.util.sendLang
object GameListener { object GameListener {
val interactSubscribed = HashMap<Location, (PlayerInteractEvent) -> Unit>() val interactSubscribed = HashMap<Location, (PlayerInteractEvent) -> Unit>()
val moveSubscribed = ArrayList<Pair<Area, (PlayerMoveEvent) -> Unit>>()
@SubscribeEvent @SubscribeEvent
fun onMoveInTeleport(event: PlayerMoveEvent) { fun portalTeleport(event: PlayerMoveEvent) {
if (!GameManager.currentGame.players.contains(event.player)) return if (!GameManager.currentGame.players.contains(event.player)) return
val player = event.player moveSubscribed.forEach { (area, func) ->
val team = player.team ?: return if (area.contains(event.to)) func(event)
if (!team.teamInfo.teleport.isInArea(event.to)) return }
debug("teleporting ${event.player} to battle field")
player.teleport(team.teamInfo.mine.randomLocation().toPlayerLocation())
} }
@SubscribeEvent @SubscribeEvent
@ -35,10 +37,28 @@ object GameListener {
tntExplode.blockList().clear() tntExplode.blockList().clear()
if (!GameManager.currentGame.state.isStarted()) return if (!GameManager.currentGame.state.isStarted()) return
GameManager.currentGame.teams.findLast { GameManager.currentGame.teams.findLast {
it.teamSurvive && it.teamInfo.home.isInArea(tntExplode.location) it.teamSurvive && it.teamInfo.home.contains(tntExplode.location)
}?.apply { }?.apply {
villager.damage() villager.damage()
debug("team ${teamInfo.name}'s villager damaged") debug("team ${teamInfo.name}'s villager damaged")
} ?: throw IllegalStateException("TNT Exploded at no team, which shouldn't happen") } ?: throw IllegalStateException("TNT Exploded at no team, which shouldn't happen")
} }
@SubscribeEvent
fun onBreak(breakEvent: BlockBreakEvent) {
val loc = breakEvent.block.location ?: return
val player = breakEvent.player ?: return
if (GameManager.currentGame.placeBreakRule.matchRule(player, GamePlaceBreakRule.ActionType.BREAK, loc)) return
breakEvent.isCancelled = true
player.sendLang("game_cant_break_block")
}
@SubscribeEvent
fun onPlace(placeEvent: BlockPlaceEvent) {
val loc = placeEvent.block.location ?: return
val player = placeEvent.player ?: return
if (GameManager.currentGame.placeBreakRule.matchRule(player, GamePlaceBreakRule.ActionType.PLACE, loc)) return
placeEvent.isCancelled = true
player.sendLang("game_cant_place_block")
}
} }

View File

@ -2,7 +2,13 @@ package cc.maxmc.blastingcrisis.map
import cc.maxmc.blastingcrisis.misc.Area import cc.maxmc.blastingcrisis.misc.Area
class GameMap(val name: String, val area: Area, val wall: Area, val teams: List<MapTeam>, val maxPlayersPerTeam: Int) { class GameMap(
val name: String,
val area: Area,
val wall: Area,
val teams: List<MapTeam>,
val maxPlayersPerTeam: Int
) {
val maxPlayer: Int val maxPlayer: Int
get() = maxPlayersPerTeam * teams.size get() = maxPlayersPerTeam * teams.size
} }

View File

@ -9,5 +9,4 @@ class MapInfo(
var waitArea: Area, var waitArea: Area,
val teams: List<MapTeam>, val teams: List<MapTeam>,
val maxPlayersPerTeam: Int, val maxPlayersPerTeam: Int,
)
)

View File

@ -2,7 +2,6 @@ package cc.maxmc.blastingcrisis.misc
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.block.Block
import org.bukkit.configuration.serialization.ConfigurationSerializable import org.bukkit.configuration.serialization.ConfigurationSerializable
import org.bukkit.configuration.serialization.SerializableAs import org.bukkit.configuration.serialization.SerializableAs
import org.bukkit.util.Vector import org.bukkit.util.Vector
@ -15,6 +14,7 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
val locTop: Location val locTop: Location
val locMin: Location val locMin: Location
@Suppress("unused") // Bukkit Specification
constructor(map: Map<String, Any>) : this(map["locTop"] as Location, map["locMin"] as Location) constructor(map: Map<String, Any>) : this(map["locTop"] as Location, map["locMin"] as Location)
init { init {
@ -30,21 +30,18 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
* *
* @return true if the location is in this area * @return true if the location is in this area
*/ */
fun isInArea(location: Location): Boolean { fun contains(location: Location): Boolean {
if (location.world != locTop.world) return false if (location.world != locTop.world) return false
if (location.x !in (locMin.x - 1)..(locTop.x + 1)) return false if (location.x !in (locMin.x - 1)..(locTop.x + 1)) return false
if (location.y !in locMin.y..locTop.y + 1) return false if (location.y !in locMin.y..locTop.y + 1) return false
if (location.z !in (locMin.z - 1)..(locTop.z + 1)) return false return location.z in (locMin.z - 1)..(locTop.z + 1)
return true
} }
fun isBlockInArea(block: Block): Boolean { fun containsBlock(location: Location): Boolean {
val location = block.location
if (location.world != locTop.world) return false if (location.world != locTop.world) return false
if (location.x !in locMin.x..locTop.x) return false if (location.x !in locMin.x..locTop.x) return false
if (location.y !in locMin.y..locTop.y) return false if (location.y !in locMin.y..locTop.y) return false
if (location.z !in locMin.z..locTop.z) return false return location.z in locMin.z..locTop.z
return true
} }
/** /**
@ -101,11 +98,10 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
other as Area other as Area
if (locTop != other.locTop) return false if (locTop != other.locTop) return false
if (locMin != other.locMin) return false return locMin == other.locMin
return true
} }
override fun hashCode(): Int { override fun hashCode(): Int {
var result = locTop.hashCode() var result = locTop.hashCode()
result = 31 * result + locMin.hashCode() result = 31 * result + locMin.hashCode()
@ -114,4 +110,8 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
override fun serialize() = mutableMapOf<String, Any>("locTop" to locTop, "locMin" to locMin) override fun serialize() = mutableMapOf<String, Any>("locTop" to locTop, "locMin" to locMin)
override fun toString(): String {
return "Area(locTop=$locTop, locMin=$locMin)"
}
} }