Huge overhaul of targeting and team system
This commit is contained in:
		
							
								
								
									
										119
									
								
								core/src/me/msoucy/ptures/controller/Engine.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								core/src/me/msoucy/ptures/controller/Engine.kt
									
									
									
									
									
										Normal 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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,14 +22,14 @@ enum class Attribute {
 | 
			
		||||
    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 accuracy = 100
 | 
			
		||||
 | 
			
		||||
    var status : () -> Status? = {null}
 | 
			
		||||
    var status: () -> Status? = { null }
 | 
			
		||||
    var statusChance = 0
 | 
			
		||||
 | 
			
		||||
    fun applyStatus(creature : Creature) {
 | 
			
		||||
    fun applyStatus(creature: Creature) {
 | 
			
		||||
        val appStatus = status()
 | 
			
		||||
        if (appStatus != null && random(100) < statusChance) {
 | 
			
		||||
            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
 | 
			
		||||
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 postSteps = mutableListOf<StatusApplier>()
 | 
			
		||||
 | 
			
		||||
    fun damage(power : Int, block : Damage.() -> Unit = {}) {
 | 
			
		||||
    fun damage(power: Int, block: Damage.() -> Unit = {}) {
 | 
			
		||||
        val d = Damage(power, attribute)
 | 
			
		||||
        d.block()
 | 
			
		||||
        damageSteps.add(d)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun addStatus(status : Status, target : Target = Target.Selected) {
 | 
			
		||||
        postSteps.add (StatusApplier(target) { c -> c.addStatus(status) })
 | 
			
		||||
    fun addStatus(status: Status, target: Target = Target.Selected) {
 | 
			
		||||
        postSteps.add(StatusApplier(target) { c -> c.addStatus(status) })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun removeStatus(status : Status, target : Target = Target.Selected) {
 | 
			
		||||
        postSteps.add (StatusApplier(target) { c -> c.removeStatus(status) })
 | 
			
		||||
    fun removeStatus(status: Status, target: Target = Target.Selected) {
 | 
			
		||||
        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) {
 | 
			
		||||
    override val name : String get() = super.name + "+"
 | 
			
		||||
    val baseName : String get() = super.name
 | 
			
		||||
class AdvancedSkill(name: String, attribute: Attribute) : Skill(name, attribute) {
 | 
			
		||||
    override val name: 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)
 | 
			
		||||
    ret.block()
 | 
			
		||||
    return ret
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
package me.msoucy.ptures.model
 | 
			
		||||
 | 
			
		||||
import me.msoucy.ptures.controller.Engine
 | 
			
		||||
 | 
			
		||||
sealed class Status {
 | 
			
		||||
 | 
			
		||||
    open fun onTurnStart(engine : Engine, creature : Creature) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								core/src/me/msoucy/ptures/model/Team.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								core/src/me/msoucy/ptures/model/Team.kt
									
									
									
									
									
										Normal 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]) }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,21 +4,21 @@ import me.msoucy.ptures.model.Creature
 | 
			
		||||
import me.msoucy.ptures.model.Skill
 | 
			
		||||
import me.msoucy.ptures.model.VisibleStatus
 | 
			
		||||
 | 
			
		||||
class SkillViewText(val skill : Skill) : SkillView() {
 | 
			
		||||
class SkillViewText(skill : Skill) : SkillView(skill) {
 | 
			
		||||
    override fun display() {
 | 
			
		||||
        println(skill.name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) }
 | 
			
		||||
 | 
			
		||||
    override val skillChoice : Skill get() {
 | 
			
		||||
    private fun chooseSkillName() : Skill {
 | 
			
		||||
        println("Skills:")
 | 
			
		||||
        println("=======")
 | 
			
		||||
        skillViews.forEachIndexed { index, skillView ->
 | 
			
		||||
@@ -33,10 +33,21 @@ class CreatureViewText(val creature : Creature) : CreatureView() {
 | 
			
		||||
                idx = tmpIdx
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
        println(creature.name)
 | 
			
		||||
    }
 | 
			
		||||
@@ -52,4 +63,8 @@ class CreatureViewText(val creature : Creature) : CreatureView() {
 | 
			
		||||
            println(it.label)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PlayerViewText(playerId : Int) : PlayerView(playerId) {
 | 
			
		||||
    override fun creatureViewFor(creature: Creature) = CreatureViewText(playerId, creature)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +1,27 @@
 | 
			
		||||
package me.msoucy.ptures.view
 | 
			
		||||
 | 
			
		||||
import me.msoucy.ptures.model.Creature
 | 
			
		||||
import me.msoucy.ptures.model.Skill
 | 
			
		||||
 | 
			
		||||
interface SkillView {
 | 
			
		||||
    fun display()
 | 
			
		||||
    fun displayEnumerated(idx : Int)
 | 
			
		||||
data class SkillChoice (val skill : Skill, val target : Creature)
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
    fun displaySkills()
 | 
			
		||||
    fun displayStatuses()
 | 
			
		||||
    abstract fun displayName()
 | 
			
		||||
    abstract fun displaySkills()
 | 
			
		||||
    abstract fun displayStatuses()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface BattleView {
 | 
			
		||||
    //
 | 
			
		||||
abstract class BattleView {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abstract class PlayerView(val playerId : Int) {
 | 
			
		||||
    abstract fun creatureViewFor(creature: Creature) : CreatureView
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user