Open runt9 opened 3 years ago
Example of a way the hooking could work with moving to Ashley
interface UnitHook {
fun beforeAttack(attacker: BattleUnit, defender: BattleUnit, attackRoll: AttackRollResult, critResult: CritCheckResult, damageCalcRequest: DamageCalcRequest) {}
fun whenHit(attacker: BattleUnit, defender: BattleUnit, attackRoll: AttackRollResult, critResult: CritCheckResult, damage: DamageCalcResult) {}
fun onHit(attacker: BattleUnit, defender: BattleUnit, attackRoll: AttackRollResult, critResult: CritCheckResult, damage: DamageCalcResult) {}
}
class UnitHooksComponent : Component, UnitHook {
val hooks = mutableListOf<UnitHook>()
override fun beforeAttack(attacker: BattleUnit, defender: BattleUnit, attackRoll: AttackRollResult, critResult: CritCheckResult, damageCalcRequest: DamageCalcRequest) =
hooks.forEach { it.beforeAttack(attacker, defender, attackRoll, critResult, damageCalcRequest) }
override fun whenHit(attacker: BattleUnit, defender: BattleUnit, attackRoll: AttackRollResult, critResult: CritCheckResult, damage: DamageCalcResult) =
hooks.forEach { it.whenHit(attacker, defender, attackRoll, critResult, damage) }
override fun onHit(attacker: BattleUnit, defender: BattleUnit, attackRoll: AttackRollResult, critResult: CritCheckResult, damage: DamageCalcResult) =
hooks.forEach { it.onHit(attacker, defender, attackRoll, critResult, damage) }
}
Adding a hook:
entity[unitHooksMapper]?.hooks?.add(object : UnitHook {
override fun beforeAttack(attacker: BattleUnit, defender: BattleUnit, attackRoll: AttackRollResult, critResult: CritCheckResult, damageCalcRequest: DamageCalcRequest) {
// do stuff
}
})
System that is handling attacks:
class AttackSystem : IteratingSystem(oneOf(CanAttackComponent::class).get()) {
override fun processEntity(entity: Entity, deltaTime: Float) {
val hooks = entity[unitHooksMapper]
hooks?.beforeAttack(attacker, defender, attackRoll, critResult, damageCalcRequest)
// attack happens
// damage happens
hooks?.onHit(attacker, defender, attackRoll, critResult, damage)
hooks?.whenHit(attacker, defender, attackRoll, critResult, damage)
}
}
Here's an example of what can be done with an AutoUpdatingLabel + Observable + ViewModel pattern:
object AutoUpdatingLabelTest {
@JvmStatic
fun main(args: Array<String>) {
val vm = TopBarViewModel(0, 0, 3, 1, 1)
val label = AutoUpdatingLabel { "Gold: ${vm.gold()}" }
println(label.text)
vm.gold += 3
println(label.text)
}
}
private operator fun Observable<Int>.plusAssign(t: Int) {
set(get() + t)
}
interface Updateable {
fun update()
operator fun <T : Any> Observable<T>.invoke(): T {
bind(this@Updateable)
return get()
}
}
class Observable<T : Any>(initialValue: T) {
private var realValue = initialValue
private val binds = mutableSetOf<Updateable>()
operator fun invoke(value: T) = set(value)
fun set(value: T) {
if (value == realValue) return
realValue = value
binds.forEach(Updateable::update)
}
fun get() = realValue
fun bind(updateable: Updateable) = binds.add(updateable)
}
class TopBarViewModel(gold: Int, activeUnitCount: Int, unitCap: Int, floor: Int, room: Int) {
val gold = Observable(gold)
val activeUnitCount = Observable(activeUnitCount)
val unitCap = Observable(unitCap)
val floor = Observable(floor)
val room = Observable(room)
}
class AutoUpdatingLabel(val strGetter: AutoUpdatingLabel.() -> String) : Updateable {
var text: String
init {
text = strGetter()
}
override fun update() {
text = strGetter()
}
}
With the example above, the following is a concept for data flow for updating UI elements based off of events occurring:
Event bus added in 94d2adc74adf28376a4a23b70fb69bb723245fbc
Eventing and ViewModel auto-updating scaffolding finished in 57dbf55bc2b33a1aee58967cfca785b6471b70a5
longer description tbd
Need to differentiate between "I need to update/redraw when X happens", "I need to perform X action when Y happens", and "X thing is happening, allow Y and Z to modify X before processing it"