Skip to content

Commit f81691c

Browse files
committed
feat: add activation range to player close by activity
1 parent 214b28f commit f81691c

File tree

1 file changed

+39
-1
lines changed

1 file changed

+39
-1
lines changed

extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/activity/PlayerCloseByActivity.kt

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.typewritermc.core.utils.point.distanceSqrt
1010
import com.typewritermc.engine.paper.entry.entity.*
1111
import com.typewritermc.engine.paper.entry.entries.EntityActivityEntry
1212
import com.typewritermc.engine.paper.entry.entries.GenericEntityActivityEntry
13+
import com.typewritermc.engine.paper.logger
1314
import com.typewritermc.engine.paper.utils.isLookable
1415
import java.time.Duration
1516
import java.util.*
@@ -24,13 +25,16 @@ import java.util.*
2425
* The `PlayerCloseByActivityEntry` is an activity that activates child activities when a viewer is close by.
2526
*
2627
* The activity will only activate when the viewer is within the defined range.
28+
* If the activationRange is set, the activity will only activate when the viewer enters this smaller range after it has been deactivated.
2729
*
2830
* When the maximum idle duration is reached, the activity will deactivate.
2931
* If the maximum idle duration is set to 0, then it won't use the timer.
3032
*
3133
* ## How could this be used?
3234
* When the player has to follow the NPC and walks away, let the NPC wander around (or stand still) around the point the player walked away. When the player returns, resume its path.
3335
*
36+
* If the activationRange is set, it can prevent the entity from repeatedly activating and deactivating when the player is at the edge of the range, or allow the player to get closer to the npc before it starts walking again.
37+
*
3438
* When the npc is walking, and a player comes in range Stand still.
3539
*/
3640
class PlayerCloseByActivityEntry(
@@ -39,6 +43,8 @@ class PlayerCloseByActivityEntry(
3943
@Help("The range in which the player has to be close by to activate the activity.")
4044
@Default("10.0")
4145
val range: Double = 10.0,
46+
@Help("Optional range in which the player has to enter to activate the activity. Must be smaller than the main range.")
47+
val activationRange: Optional<Double> = Optional.empty(),
4248
@Help("The maximum duration a player can be idle in the same range before the activity deactivates.")
4349
@Default("30000")
4450
val maxIdleDuration: Duration = Duration.ofSeconds(30),
@@ -51,8 +57,18 @@ class PlayerCloseByActivityEntry(
5157
context: ActivityContext,
5258
currentLocation: PositionProperty
5359
): EntityActivity<in ActivityContext> {
60+
val effectiveActivationRange = activationRange
61+
.filter { it < range }
62+
.orElseGet {
63+
if (activationRange.isPresent) {
64+
logger.warning("Activation range (${activationRange.get()}) must be smaller than the main range ($range)")
65+
}
66+
range
67+
}
68+
5469
return PlayerCloseByActivity(
5570
range,
71+
effectiveActivationRange,
5672
maxIdleDuration,
5773
closeByActivity,
5874
idleActivity,
@@ -63,12 +79,14 @@ class PlayerCloseByActivityEntry(
6379

6480
class PlayerCloseByActivity(
6581
private val range: Double,
82+
private val activationRange: Double,
6683
private val maxIdleDuration: Duration,
6784
private val closeByActivity: Ref<out EntityActivityEntry>,
6885
private val idleActivity: Ref<out EntityActivityEntry>,
6986
startLocation: PositionProperty,
7087
) : SingleChildActivity<ActivityContext>(startLocation) {
7188
private var trackers = mutableMapOf<UUID, PlayerLocationTracker>()
89+
private var isActive = false
7290

7391
override fun currentChild(context: ActivityContext): Ref<out EntityActivityEntry> {
7492
val closeByPlayers = context.viewers
@@ -86,7 +104,16 @@ class PlayerCloseByActivity(
86104
.update(player.location.toProperty())
87105
}
88106

89-
val isActive = trackers.any { (_, tracker) -> tracker.isActive(maxIdleDuration) }
107+
val trackerActive = trackers.any { (_, tracker) -> tracker.isActive(maxIdleDuration) }
108+
109+
isActive = when {
110+
isActive -> trackerActive
111+
trackerActive -> trackers.any { (_, tracker) ->
112+
tracker.isInActivationRange(currentPosition, activationRange, maxIdleDuration)
113+
}
114+
else -> false
115+
}
116+
90117
return if (isActive) {
91118
closeByActivity
92119
} else {
@@ -108,5 +135,16 @@ class PlayerCloseByActivity(
108135
if (maxIdleDuration.isZero) return true
109136
return System.currentTimeMillis() - lastSeen < maxIdleDuration.toMillis()
110137
}
138+
139+
fun isInActivationRange(
140+
entityPosition: PositionProperty,
141+
activationRange: Double,
142+
maxIdleDuration: Duration
143+
): Boolean {
144+
if (!isActive(maxIdleDuration)) return false
145+
146+
val distanceSq = location.distanceSqrt(entityPosition) ?: Double.MAX_VALUE
147+
return distanceSq < activationRange * activationRange
148+
}
111149
}
112150
}

0 commit comments

Comments
 (0)