diff --git a/sharedUI/src/commonTest/kotlin/org/shahondin1624/DamageMonitorTest.kt b/sharedUI/src/commonTest/kotlin/org/shahondin1624/DamageMonitorTest.kt new file mode 100644 index 0000000..15ffd05 --- /dev/null +++ b/sharedUI/src/commonTest/kotlin/org/shahondin1624/DamageMonitorTest.kt @@ -0,0 +1,245 @@ +package org.shahondin1624 + +import org.shahondin1624.model.attributes.Attribute +import org.shahondin1624.model.attributes.AttributeType +import org.shahondin1624.model.attributes.Attributes +import org.shahondin1624.model.charactermodel.DamageMonitor +import org.shahondin1624.model.charactermodel.DamageTrackType +import org.shahondin1624.model.charactermodel.createDamageMonitor +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests for DamageMonitor damage tracking, wound modifiers, + * toggle behavior, and overflow transitions. + */ +class DamageMonitorTest { + + // Body=6, Willpower=4 -> physicalMax = 7 + (6/2) = 10, stunMax = 7 + (4/2) = 9, overflowMax = (6/2) = 3 + private val testAttributes = Attributes( + body = Attribute(AttributeType.Body, 6), + agility = Attribute(AttributeType.Agility, 3), + reaction = Attribute(AttributeType.Reaction, 3), + strength = Attribute(AttributeType.Strength, 3), + willpower = Attribute(AttributeType.Willpower, 4), + logic = Attribute(AttributeType.Logic, 3), + intuition = Attribute(AttributeType.Intuition, 3), + charisma = Attribute(AttributeType.Charisma, 3), + edge = 3 + ) + + private fun freshMonitor(): DamageMonitor = DamageMonitor(testAttributes) + + // ---- Max calculations ---- + + @Test + fun stunMaxIsBasePlusHalfWillpower() { + val monitor = freshMonitor() + // 7 + (4 / 2.0).toInt() = 7 + 2 = 9 + assertEquals(9, monitor.stunMax()) + } + + @Test + fun physicalMaxIsBasePlusHalfBody() { + val monitor = freshMonitor() + // 7 + (6 / 2.0).toInt() = 7 + 3 = 10 + assertEquals(10, monitor.physicalMax()) + } + + @Test + fun overflowMaxIsHalfBody() { + val monitor = freshMonitor() + // (6 / 2.0).toInt() = 3 + assertEquals(3, monitor.overflowMax()) + } + + // ---- Initial state ---- + + @Test + fun freshMonitorHasZeroDamage() { + val monitor = freshMonitor() + assertEquals(0, monitor.stunCurrent()) + assertEquals(0, monitor.physicalCurrent()) + assertEquals(0, monitor.overflowCurrent()) + } + + // ---- Setting damage values ---- + + @Test + fun withStunDamageSetsCorrectValue() { + val monitor = freshMonitor().withStunDamage(5) + assertEquals(5, monitor.stunCurrent()) + } + + @Test + fun withPhysicalDamageSetsCorrectValue() { + val monitor = freshMonitor().withPhysicalDamage(7) + assertEquals(7, monitor.physicalCurrent()) + } + + @Test + fun withOverflowDamageSetsCorrectValue() { + val monitor = freshMonitor().withOverflowDamage(2) + assertEquals(2, monitor.overflowCurrent()) + } + + @Test + fun damageIsClamtedToMax() { + val monitor = freshMonitor() + val overStun = monitor.withStunDamage(100) + assertEquals(9, overStun.stunCurrent()) // clamped to stunMax=9 + + val overPhys = monitor.withPhysicalDamage(100) + assertEquals(10, overPhys.physicalCurrent()) // clamped to physicalMax=10 + + val overFlow = monitor.withOverflowDamage(100) + assertEquals(3, overFlow.overflowCurrent()) // clamped to overflowMax=3 + } + + @Test + fun damageIsClampedToZero() { + val monitor = freshMonitor() + val negStun = monitor.withStunDamage(-5) + assertEquals(0, negStun.stunCurrent()) + } + + // ---- Wound modifier ---- + + @Test + fun woundModifierIsZeroWithNoDamage() { + assertEquals(0, freshMonitor().woundModifier()) + } + + @Test + fun woundModifierIsZeroUnderThreshold() { + // 1 and 2 physical damage -> no wound modifier yet + assertEquals(0, freshMonitor().withPhysicalDamage(1).woundModifier()) + assertEquals(0, freshMonitor().withPhysicalDamage(2).woundModifier()) + } + + @Test + fun woundModifierNegOneAtThreePhysical() { + val monitor = freshMonitor().withPhysicalDamage(3) + assertEquals(-1, monitor.woundModifier()) + } + + @Test + fun woundModifierNegTwoAtSixPhysical() { + val monitor = freshMonitor().withPhysicalDamage(6) + assertEquals(-2, monitor.woundModifier()) + } + + @Test + fun woundModifierScalesWithDamage() { + val monitor = freshMonitor().withPhysicalDamage(9) + assertEquals(-3, monitor.woundModifier()) + } + + // ---- Toggle damage box ---- + + @Test + fun toggleEmptyBoxMarksDamage() { + val monitor = freshMonitor() + // Toggle position 3 when current=0 -> should set to 3 + val toggled = monitor.toggleDamageBox(DamageTrackType.Physical, 3) + assertEquals(3, toggled.physicalCurrent()) + } + + @Test + fun toggleFilledBoxHealsToPreviousPosition() { + val monitor = freshMonitor().withPhysicalDamage(5) + // Toggle position 3 when current=5 -> position 3 is filled, heal to 2 + val toggled = monitor.toggleDamageBox(DamageTrackType.Physical, 3) + assertEquals(2, toggled.physicalCurrent()) + } + + @Test + fun toggleLastFilledBoxClearsDamage() { + val monitor = freshMonitor().withPhysicalDamage(3) + // Toggle position 1 -> heal to 0 + val toggled = monitor.toggleDamageBox(DamageTrackType.Physical, 1) + assertEquals(0, toggled.physicalCurrent()) + } + + @Test + fun toggleStunTrackWorks() { + val monitor = freshMonitor() + val damaged = monitor.toggleDamageBox(DamageTrackType.Stun, 5) + assertEquals(5, damaged.stunCurrent()) + + // Toggle lower position to heal + val healed = damaged.toggleDamageBox(DamageTrackType.Stun, 3) + assertEquals(2, healed.stunCurrent()) + } + + @Test + fun toggleOverflowTrackWorks() { + val monitor = freshMonitor() + val damaged = monitor.toggleDamageBox(DamageTrackType.Overflow, 2) + assertEquals(2, damaged.overflowCurrent()) + + val healed = damaged.toggleDamageBox(DamageTrackType.Overflow, 1) + assertEquals(0, healed.overflowCurrent()) + } + + // ---- Physical-to-overflow transition ---- + + @Test + fun physicalCanBeFilled() { + val monitor = freshMonitor().withPhysicalDamage(10) // physicalMax=10 + assertEquals(10, monitor.physicalCurrent()) + assertEquals(0, monitor.overflowCurrent()) + } + + @Test + fun overflowCanBeSetWhenPhysicalIsFull() { + val monitor = freshMonitor() + .withPhysicalDamage(10) // fill physical track + .withOverflowDamage(2) // then add overflow + assertEquals(10, monitor.physicalCurrent()) + assertEquals(2, monitor.overflowCurrent()) + } + + @Test + fun fullPhysicalAndFullOverflow() { + val monitor = freshMonitor() + .withPhysicalDamage(10) + .withOverflowDamage(3) // overflowMax=3 + assertEquals(10, monitor.physicalCurrent()) + assertEquals(3, monitor.overflowCurrent()) + } + + // ---- createDamageMonitor ---- + + @Test + fun createDamageMonitorStartsAtMaxValues() { + val monitor = createDamageMonitor(testAttributes) + assertEquals(monitor.stunMax(), monitor.stunCurrent()) + assertEquals(monitor.physicalMax(), monitor.physicalCurrent()) + assertEquals(monitor.overflowMax(), monitor.overflowCurrent()) + } + + // ---- Wound modifier updates past thresholds ---- + + @Test + fun woundModifierUpdatesCorrectlyAsDamageIncreases() { + var monitor = freshMonitor() + assertEquals(0, monitor.woundModifier()) + + monitor = monitor.withPhysicalDamage(1) + assertEquals(0, monitor.woundModifier()) + + monitor = monitor.withPhysicalDamage(3) + assertEquals(-1, monitor.woundModifier()) + + monitor = monitor.withPhysicalDamage(6) + assertEquals(-2, monitor.woundModifier()) + + monitor = monitor.withPhysicalDamage(9) + assertEquals(-3, monitor.woundModifier()) + + // Heal back + monitor = monitor.withPhysicalDamage(2) + assertEquals(0, monitor.woundModifier()) + } +}