Skip to content

Commit 83f0065

Browse files
committed
feat: RechargeableComponents now maintain the value is not greater than max value relationship, added valuePercent, closes #833
1 parent 453f6f5 commit 83f0065

File tree

3 files changed

+214
-25
lines changed

3 files changed

+214
-25
lines changed

fxgl/src/main/kotlin/com/almasb/fxgl/dsl/components/RechargeableComponents.kt

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@ class ManaIntComponent(maxValue: Int) : RechargeableIntComponent(maxValue)
3737
/**
3838
* Any rechargeable component, such as HP, SP, ammo, etc.
3939
* The internal value is a double in the range [0..maxValue].
40+
* All methods assume that the arguments provided are >= 0.
4041
*
4142
* @author Almas Baimagambetov (AlmasB) (almaslvl@gmail.com)
4243
*/
4344
abstract class RechargeableDoubleComponent
4445
@JvmOverloads constructor(
45-
maxValue: Double,
46-
initialValue: Double = maxValue
46+
initialMaxValue: Double,
47+
initialValue: Double = initialMaxValue
4748
) : DoubleComponent(initialValue) {
4849

49-
private val maxValueProp = SimpleDoubleProperty(maxValue)
50+
private val maxValueProp = SimpleDoubleProperty(initialMaxValue)
5051
private val valuePercentBinding = valueProperty().divide(maxValueProp).multiply(100)
5152

5253
fun maxValueProperty() = maxValueProp
@@ -59,6 +60,18 @@ abstract class RechargeableDoubleComponent
5960
val valuePercent: Double
6061
get() = valuePercentBinding.value
6162

63+
init {
64+
maxValueProp.addListener { _, _, newValue ->
65+
if (value > newValue.toDouble())
66+
value = newValue.toDouble()
67+
}
68+
69+
valueProperty().addListener { _, _, newValue ->
70+
if (newValue.toDouble() > maxValueProp.value)
71+
value = maxValueProp.value
72+
}
73+
}
74+
6275
/**
6376
* Set component value to 0.
6477
*/
@@ -146,26 +159,47 @@ abstract class RechargeableDoubleComponent
146159
fun zeroProperty(): BooleanBinding = zeroProp
147160
}
148161

162+
// Code below is duplicated for Int for FXGL version upgrade compatibility reasons
163+
149164
/**
150165
* Any rechargeable component, such as HP, SP, ammo, etc.
151166
* The internal value is an int in the range [0..maxValue].
167+
* All methods assume that the arguments provided are >= 0.
152168
*
153169
* @author Almas Baimagambetov (AlmasB) (almaslvl@gmail.com)
154170
*/
155171
abstract class RechargeableIntComponent
156172
@JvmOverloads constructor(
157-
maxValue: Int,
158-
initialValue: Int = maxValue
173+
initialMaxValue: Int,
174+
initialValue: Int = initialMaxValue
159175
) : IntegerComponent(initialValue) {
160176

161-
private val maxValueProp = SimpleIntegerProperty(maxValue)
177+
private val maxValueProp = SimpleIntegerProperty(initialMaxValue)
178+
// 1.0 is needed to avoid integer division
179+
private val valuePercentBinding = valueProperty().multiply(1.0).divide(maxValueProp).multiply(100)
162180

163181
fun maxValueProperty() = maxValueProp
182+
fun valuePercentProperty() = valuePercentBinding
164183

165184
var maxValue: Int
166185
get() = maxValueProp.value
167186
set(value) { maxValueProp.value = value }
168187

188+
val valuePercent: Double
189+
get() = valuePercentBinding.doubleValue()
190+
191+
init {
192+
maxValueProp.addListener { _, _, newValue ->
193+
if (value > newValue.toInt())
194+
value = newValue.toInt()
195+
}
196+
197+
valueProperty().addListener { _, _, newValue ->
198+
if (newValue.toInt() > maxValueProp.value)
199+
value = maxValueProp.value
200+
}
201+
}
202+
169203
/**
170204
* Set component value to 0.
171205
*/
@@ -236,11 +270,10 @@ abstract class RechargeableIntComponent
236270
restore((percentage / 100 * maxValue).toInt())
237271
}
238272

239-
private val zeroProp = valueProperty().lessThanOrEqualTo(0.0)
273+
private val zeroProp = valueProperty().lessThanOrEqualTo(0)
240274

241275
/**
242-
* Check if value is 0. Note that because internal value is a double,
243-
* value of 0.xx will not return true.
276+
* Check if value is 0.
244277
*
245278
* @return true iff value is 0
246279
*/

fxgl/src/test/kotlin/com/almasb/fxgl/dsl/components/RechargeableDoubleComponentTest.kt

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,22 @@
66

77
package com.almasb.fxgl.dsl.components
88

9-
import javafx.beans.value.ChangeListener
109
import org.hamcrest.CoreMatchers.`is`
1110
import org.hamcrest.MatcherAssert.assertThat
1211
import org.junit.jupiter.api.Assertions.assertTrue
1312
import org.junit.jupiter.api.BeforeEach
1413
import org.junit.jupiter.api.Test
1514

1615
/**
17-
*
18-
*
1916
* @author Almas Baimagambetov (almaslvl@gmail.com)
2017
*/
2118
class RechargeableDoubleComponentTest {
2219

23-
private class HPComponent : RechargeableDoubleComponent(100.0)
24-
25-
private lateinit var hp: HPComponent
20+
private lateinit var hp: HealthDoubleComponent
2621

2722
@BeforeEach
2823
fun setUp() {
29-
hp = HPComponent()
24+
hp = HealthDoubleComponent(100.0)
3025
}
3126

3227
@Test
@@ -100,21 +95,18 @@ class RechargeableDoubleComponentTest {
10095
}
10196

10297
@Test
103-
fun `Negative damage`() {
98+
fun `Set max below current value`() {
10499
hp.restoreFully()
105-
hp.damage(-50.0)
100+
hp.maxValue = 50.0
106101
// value cannot be > maxValue
107-
// FAILS:
108-
// assertTrue(hp.value <= hp.maxValue)
102+
assertTrue(hp.value <= hp.maxValue)
109103
}
110104

111105
@Test
112-
fun `Change max`() {
113-
hp.restoreFully()
114-
hp.maxValue = 50.0
106+
fun `Set current value above max`() {
107+
hp.value = 200.0
115108
// value cannot be > maxValue
116-
// FAILS:
117-
// assertTrue(hp.value <= hp.maxValue)
109+
assertTrue(hp.value <= hp.maxValue)
118110
}
119111

120112
@Test
@@ -166,5 +158,6 @@ class RechargeableDoubleComponentTest {
166158
hp.maxValue = 50.0
167159

168160
assertThat(hp.valuePercent, `is`(20.0))
161+
assertThat(hp.valuePercentProperty().value, `is`(20.0))
169162
}
170163
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* FXGL - JavaFX Game Library. The MIT License (MIT).
3+
* Copyright (c) AlmasB (almaslvl@gmail.com).
4+
* See LICENSE for details.
5+
*/
6+
7+
package com.almasb.fxgl.dsl.components
8+
9+
import org.hamcrest.CoreMatchers.`is`
10+
import org.hamcrest.MatcherAssert.assertThat
11+
import org.junit.jupiter.api.Assertions.assertTrue
12+
import org.junit.jupiter.api.BeforeEach
13+
import org.junit.jupiter.api.Test
14+
15+
/**
16+
* @author Almas Baimagambetov (almaslvl@gmail.com)
17+
*/
18+
class RechargeableIntComponentTest {
19+
20+
private lateinit var hp: HealthIntComponent
21+
22+
@BeforeEach
23+
fun setUp() {
24+
hp = HealthIntComponent(100)
25+
}
26+
27+
@Test
28+
fun `Creation`() {
29+
assertThat(hp.maxValue, `is`(100))
30+
assertThat(hp.value, `is`(100))
31+
assertThat(hp.isZero, `is`(false))
32+
}
33+
34+
@Test
35+
fun `Modification`() {
36+
hp.value = 100
37+
assertThat(hp.value, `is`(100))
38+
39+
hp.damage(30)
40+
assertThat(hp.value, `is`(70))
41+
42+
hp.damagePercentageCurrent(10.0)
43+
assertThat(hp.value, `is`(63))
44+
45+
hp.damagePercentageMax(50.0)
46+
assertThat(hp.value, `is`(13))
47+
48+
hp.restore(37)
49+
assertThat(hp.value, `is`(50))
50+
51+
hp.restorePercentageCurrent(50.0)
52+
assertThat(hp.value, `is`(75))
53+
54+
hp.restorePercentageMax(15.0)
55+
assertThat(hp.value, `is`(90))
56+
assertThat(hp.isZero, `is`(false))
57+
58+
hp.damage(100)
59+
assertThat(hp.value, `is`(0))
60+
assertThat(hp.isZero, `is`(true))
61+
62+
hp.damageFully()
63+
assertThat(hp.value, `is`(0))
64+
assertThat(hp.isZero, `is`(true))
65+
66+
hp.restoreFully()
67+
assertThat(hp.value, `is`(100))
68+
assertThat(hp.isZero, `is`(false))
69+
70+
hp.value = 50
71+
assertThat(hp.value, `is`(50))
72+
73+
// From now on, maxValue is 200
74+
hp.maxValue = 200
75+
assertThat(hp.maxValue, `is`(200))
76+
// Value is still 50
77+
assertThat(hp.value, `is`(50))
78+
79+
hp.restoreFully()
80+
assertThat(hp.value, `is`(200))
81+
82+
hp.damagePercentageMax(75.0)
83+
assertThat(hp.value, `is`(50))
84+
85+
hp.restorePercentageMax(50.0)
86+
assertThat(hp.value, `is`(150))
87+
88+
hp.restorePercentageMax(50.0)
89+
// value cannot be > maxValue
90+
assertThat(hp.value, `is`(200))
91+
hp.damagePercentageMax(150.0)
92+
// value cannot be < 0
93+
assertThat(hp.value, `is`(0))
94+
assertThat(hp.isZero, `is`(true))
95+
}
96+
97+
@Test
98+
fun `Set max below current value`() {
99+
hp.restoreFully()
100+
hp.maxValue = 50
101+
// value cannot be > maxValue
102+
assertTrue(hp.value <= hp.maxValue)
103+
}
104+
105+
@Test
106+
fun `Set current value above max`() {
107+
hp.value = 200
108+
// value cannot be > maxValue
109+
assertTrue(hp.value <= hp.maxValue)
110+
}
111+
112+
@Test
113+
fun `Properties`() {
114+
var value = 0
115+
var maxValue = 0
116+
var zero = false
117+
118+
// Add listeners so we can monitor the properties
119+
hp.valueProperty().addListener { _, _, newValue -> value = newValue as Int }
120+
hp.maxValueProperty().addListener { _, _, newValue -> maxValue = newValue as Int }
121+
hp.zeroProperty().addListener { _, _, newValue -> zero = newValue as Boolean }
122+
123+
hp.damage(75)
124+
assertThat(value, `is`(25))
125+
assertThat(zero, `is`(false))
126+
127+
hp.damagePercentageMax(100.0)
128+
assertThat(value, `is`(0))
129+
assertThat(zero, `is`(true))
130+
131+
hp.restorePercentageCurrent(10.0)
132+
// Still zero
133+
assertThat(value, `is`(0))
134+
assertThat(zero, `is`(true))
135+
136+
hp.restorePercentageMax(10.0)
137+
assertThat(value, `is`(10))
138+
assertThat(zero, `is`(false))
139+
140+
// From now on, maxValue is 200
141+
hp.maxValue = 200
142+
assertThat(maxValue, `is`(200))
143+
assertThat(value, `is`(10))
144+
assertThat(zero, `is`(false))
145+
146+
hp.restorePercentageMax(10.0)
147+
assertThat(value, `is`(30))
148+
assertThat(zero, `is`(false))
149+
}
150+
151+
@Test
152+
fun `value in percent`() {
153+
hp.value = 10
154+
hp.maxValue = 100
155+
156+
assertThat(hp.valuePercent, `is`(10.0))
157+
158+
hp.maxValue = 50
159+
160+
assertThat(hp.valuePercent, `is`(20.0))
161+
assertThat(hp.valuePercentProperty().value, `is`(20.0))
162+
}
163+
}

0 commit comments

Comments
 (0)