Huge overhaul of targeting and team system

This commit is contained in:
Matt Soucy 2019-12-06 21:55:48 -05:00
parent 3e19060084
commit f496abee18
8 changed files with 191 additions and 121 deletions

View File

@ -10,7 +10,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.2' classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.7' classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.7'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"

View File

@ -0,0 +1,119 @@
package me.msoucy.ptures.controller
import me.msoucy.ptures.model.Creature
import me.msoucy.ptures.model.KnockedOut
import me.msoucy.ptures.model.Skill
import me.msoucy.ptures.model.Target
import me.msoucy.ptures.model.Team
import me.msoucy.ptures.view.BattleView
import me.msoucy.ptures.view.SkillChoice
sealed class BattleType(vararg val teams: Team) {
init {
assert(teams.isNotEmpty())
}
abstract val numGroups: Int
}
sealed class IndividualBattle(vararg teams: Team) : BattleType(*teams) {
override val numGroups: Int
get() = teams.size
}
class SingleBattle(vararg teams: Team) : IndividualBattle(*teams)
class DoubleBattle(vararg teams: Team) : IndividualBattle(*teams)
class TripleBattle(vararg teams: Team) : IndividualBattle(*teams)
class TeamBattle(override val numGroups: Int, vararg teams: Team) : BattleType(*teams)
class RaidBattle(val boss: Creature, vararg teams: Team) : BattleType(*teams) {
override val numGroups: Int
get() = 2
}
class Engine(private val battle: BattleType) {
private var currentCreature = 0
private val creatures get() = battle.teams.flatMap { it.activeCreatures }
val attacker: Creature
get() {
return creatures[currentCreature].creature
}
private val forcedSkills = mutableMapOf<Creature, Skill>()
private val activeCreatures
get() = creatures
.filter { !it.creature.hasStatus<KnockedOut>() }
.sortedBy { it.creature.spd }
fun resolveTurn(view: BattleView) {
forcedSkills.clear()
// All preconditions
for (i in activeCreatures.indices) {
val creature = creatures[i]
for (status in creature.creature.statuses) {
status.onTurnStart(this, creature.creature)
}
}
// Get the moves each creature will use this turn
val moves = activeCreatures.indices.map { i ->
val forcedSkill = forcedSkills[creatures[i].creature]
if (forcedSkill != null) {
SkillChoice(forcedSkill, creatures[nextOpponent(i)].creature)
} else {
creatures[i].chooseSkill(activeCreatures.map { it.creature })
}
}
// Resolve each move
for (i in activeCreatures.indices) {
// Resolve move
val (skill, target) = moves[i]
currentCreature = i
for (step in skill.damageSteps) {
for (targetCreature in getTargetList(step.target, target)) {
if (attacker.hits(targetCreature, step)) {
targetCreature.apply(step, attacker)
step.applyStatus(targetCreature)
}
}
}
for (step in skill.postSteps) {
for (targetCreature in getTargetList(step.target, target)) {
step.apply(targetCreature)
}
}
}
// All post conditions
for (creatureView in activeCreatures) {
val creature = creatureView.creature
for (status in creature.statuses) {
status.onTurnEnd(this)
}
}
}
private fun getTargetList(target: Target, selected: Creature): List<Creature> {
return when (target) {
Target.Self -> listOf(attacker)
Target.Selected -> listOf(selected)
Target.Others -> creatures.filter { it.creature != attacker }.map { it.creature }
Target.Opponents -> creatures.filter { it.playerId != creatures[currentCreature].playerId }.map { it.creature }
Target.All -> creatures.map { it.creature }
}
}
private fun nextOpponent(idx: Int): Int {
var nextIdx = idx + 1
while (nextIdx != idx) {
if (creatures[nextIdx].playerId != creatures[idx].playerId) {
return nextIdx
}
nextIdx = (nextIdx + 1) % creatures.size
}
// There are no opponents... so use the "none" index
return -1
}
}

View File

@ -1,91 +0,0 @@
package me.msoucy.ptures.model
import me.msoucy.ptures.view.BattleView
class Engine(private vararg val creatures : Pair<Creature, Int>) {
init {
assert(creatures.isNotEmpty())
}
private var currentCreature = 0
val attacker : Creature get() {
return creatures[currentCreature].first
}
val forcedSkills = mutableMapOf<Creature, Skill>()
val activeCreatures get() = creatures.withIndex()
.filter { (_, c) -> !c.first.hasStatus<KnockedOut>() }
.sortedBy { (_, c) -> c.first.spd }
.map { (i, _) -> i}
fun resolveTurn(view : BattleView) {
forcedSkills.clear()
// All preconditions
for (i in activeCreatures) {
val creature = creatures[i]
for (status in creature.first.statuses) {
status.onTurnStart(this, creature.first)
}
}
// Get the moves each creature will use this turn
val moves = activeCreatures.map { i ->
if (creatures[i].first in forcedSkills) {
Pair(forcedSkills[creatures[i].first], nextOpponent(i))
} else {
TODO("Fill in view here")
}
}
// Resolve each move
for (i in activeCreatures) {
// Resolve move
val (skill, target) = moves[i]
currentCreature = i
for (step in skill.damageSteps) {
for (t in getTargetList(step.target, target)) {
val (targetCreature, _) = creatures[t]
if (attacker.hits(targetCreature, step)) {
targetCreature.apply(step, attacker)
step.applyStatus(targetCreature)
}
}
}
for(step in skill.postSteps) {
for (t in getTargetList(step.target, target)) {
val (targetCreature, _) = creatures[t]
step.apply(targetCreature)
}
}
}
// All post conditions
for (i in activeCreatures) {
val creature = creatures[i]
for (status in creature.first.statuses) {
status.onTurnEnd(this)
}
}
}
private fun getTargetList(target : Target, selected : Int) : List<Int> {
return when(target) {
Target.Self -> listOf(currentCreature)
Target.Selected -> if (selected == -1) { listOf() } else { listOf(selected) }
Target.Others -> creatures.indices.filter { it != currentCreature }
Target.Opponents -> creatures.indices.filter { creatures[it].second != creatures[currentCreature].second }
Target.All -> creatures.indices.toList()
}
}
private fun nextOpponent(idx : Int) : Int {
var nextIdx = idx + 1
while (nextIdx != idx) {
if (creatures[nextIdx].second != creatures[idx].second) {
return nextIdx
}
nextIdx = (nextIdx + 1) % creatures.size
}
// There are no opponents... so use the "none" index
return -1
}
}

View File

@ -22,14 +22,14 @@ enum class Attribute {
Wood Wood
} }
class Damage(val power : Int, val attribute : Attribute = Attribute.Neutral) { class Damage(val power: Int, val attribute: Attribute = Attribute.Neutral) {
var target = Target.Selected var target = Target.Selected
var accuracy = 100 var accuracy = 100
var status : () -> Status? = {null} var status: () -> Status? = { null }
var statusChance = 0 var statusChance = 0
fun applyStatus(creature : Creature) { fun applyStatus(creature: Creature) {
val appStatus = status() val appStatus = status()
if (appStatus != null && random(100) < statusChance) { if (appStatus != null && random(100) < statusChance) {
creature.addStatus(appStatus) creature.addStatus(appStatus)
@ -37,34 +37,42 @@ class Damage(val power : Int, val attribute : Attribute = Attribute.Neutral) {
} }
} }
class StatusApplier(val target : Target, val apply : (Creature) -> Unit) class StatusApplier(val target: Target, val apply: (Creature) -> Unit)
@SkillMarker @SkillMarker
sealed class Skill(open val name : String, val attribute : Attribute) { open class Skill(open val name: String, val attribute: Attribute) {
val damageSteps = mutableListOf<Damage>() val damageSteps = mutableListOf<Damage>()
val postSteps = mutableListOf<StatusApplier>() val postSteps = mutableListOf<StatusApplier>()
fun damage(power : Int, block : Damage.() -> Unit = {}) { fun damage(power: Int, block: Damage.() -> Unit = {}) {
val d = Damage(power, attribute) val d = Damage(power, attribute)
d.block() d.block()
damageSteps.add(d) damageSteps.add(d)
} }
fun addStatus(status : Status, target : Target = Target.Selected) { fun addStatus(status: Status, target: Target = Target.Selected) {
postSteps.add (StatusApplier(target) { c -> c.addStatus(status) }) postSteps.add(StatusApplier(target) { c -> c.addStatus(status) })
} }
fun removeStatus(status : Status, target : Target = Target.Selected) { fun removeStatus(status: Status, target: Target = Target.Selected) {
postSteps.add (StatusApplier(target) { c -> c.removeStatus(status) }) postSteps.add(StatusApplier(target) { c -> c.removeStatus(status) })
} }
// Assume that the first damage step is the one that provides the target
val target: Target
get() = if (damageSteps.isNotEmpty()) {
damageSteps[0].target
} else {
Target.Self
}
} }
class AdvancedSkill(name : String, attribute : Attribute) : Skill(name, attribute) { class AdvancedSkill(name: String, attribute: Attribute) : Skill(name, attribute) {
override val name : String get() = super.name + "+" override val name: String get() = super.name + "+"
val baseName : String get() = super.name val baseName: String get() = super.name
} }
fun skill(name : String, attribute : Attribute = Attribute.Neutral, block : Skill.() -> Unit) : Skill { fun skill(name: String, attribute: Attribute = Attribute.Neutral, block: Skill.() -> Unit): Skill {
val ret = Skill(name, attribute) val ret = Skill(name, attribute)
ret.block() ret.block()
return ret return ret

View File

@ -1,5 +1,7 @@
package me.msoucy.ptures.model package me.msoucy.ptures.model
import me.msoucy.ptures.controller.Engine
sealed class Status { sealed class Status {
open fun onTurnStart(engine : Engine, creature : Creature) { open fun onTurnStart(engine : Engine, creature : Creature) {

View File

@ -0,0 +1,11 @@
package me.msoucy.ptures.model
import me.msoucy.ptures.view.PlayerView
class Team(var view : PlayerView) {
var creatures = mutableListOf<Creature>()
var selectedCreatures = mutableListOf(0)
val activeCreatures get() = selectedCreatures.map { view.creatureViewFor(creatures[it]) }
}

View File

@ -4,21 +4,21 @@ import me.msoucy.ptures.model.Creature
import me.msoucy.ptures.model.Skill import me.msoucy.ptures.model.Skill
import me.msoucy.ptures.model.VisibleStatus import me.msoucy.ptures.model.VisibleStatus
class SkillViewText(val skill : Skill) : SkillView() { class SkillViewText(skill : Skill) : SkillView(skill) {
override fun display() { override fun display() {
println(skill.name) println(skill.name)
} }
override fun displayEnumerated(idx: Int) { override fun displayEnumerated(idx: Int) {
println("${idx}: ${skill.name}") println("${idx+1}: ${skill.name}")
} }
} }
class CreatureViewText(val creature : Creature) : CreatureView() { class CreatureViewText(playerId : Int, creature : Creature) : CreatureView(playerId, creature) {
private val skillViews = creature.skills.map { SkillViewText(it) } private val skillViews = creature.skills.map { SkillViewText(it) }
override val skillChoice : Skill get() { private fun chooseSkillName() : Skill {
println("Skills:") println("Skills:")
println("=======") println("=======")
skillViews.forEachIndexed { index, skillView -> skillViews.forEachIndexed { index, skillView ->
@ -33,10 +33,21 @@ class CreatureViewText(val creature : Creature) : CreatureView() {
idx = tmpIdx idx = tmpIdx
} }
} }
return creature.skills[idx] return creature.skills[idx]
} }
private fun chooseTarget(skill : Skill, possibleTargets : List<Creature>) : Creature {
return creature
}
override fun chooseSkill(possibleTargets : List<Creature>) : SkillChoice {
val skill = chooseSkillName()
val target = chooseTarget(skill, possibleTargets)
return SkillChoice(skill, target)
}
override fun displayName() { override fun displayName() {
println(creature.name) println(creature.name)
} }
@ -52,4 +63,8 @@ class CreatureViewText(val creature : Creature) : CreatureView() {
println(it.label) println(it.label)
} }
} }
}
class PlayerViewText(playerId : Int) : PlayerView(playerId) {
override fun creatureViewFor(creature: Creature) = CreatureViewText(playerId, creature)
} }

View File

@ -1,21 +1,27 @@
package me.msoucy.ptures.view package me.msoucy.ptures.view
import me.msoucy.ptures.model.Creature
import me.msoucy.ptures.model.Skill import me.msoucy.ptures.model.Skill
interface SkillView { data class SkillChoice (val skill : Skill, val target : Creature)
fun display()
fun displayEnumerated(idx : Int) abstract class SkillView(val skill : Skill) {
abstract fun display()
abstract fun displayEnumerated(idx : Int)
} }
interface CreatureView { abstract class CreatureView(val playerId : Int, val creature: Creature) {
val skillChoice : Skill abstract fun chooseSkill(possibleTargets : List<Creature>) : SkillChoice
fun displayName() abstract fun displayName()
fun displaySkills() abstract fun displaySkills()
fun displayStatuses() abstract fun displayStatuses()
} }
interface BattleView { abstract class BattleView {
// }
abstract class PlayerView(val playerId : Int) {
abstract fun creatureViewFor(creature: Creature) : CreatureView
} }