Skip to content

Commit 3dbc2af

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents b3fc66f + 8488bdc commit 3dbc2af

File tree

5 files changed

+93
-19
lines changed

5 files changed

+93
-19
lines changed

src/main/java/io/luna/game/action/ActionQueue.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,9 @@ public void interruptAll() {
224224
}
225225

226226
/**
227-
* Returns the total number of actions currently registered in the processing set.
228-
*
229-
* @return The processing action count.
227+
* @return The active action count.
230228
*/
231229
public int size() {
232-
return processing.size();
230+
return executing.size() + processing.size();
233231
}
234232
}

src/main/java/io/luna/net/msg/GameMessageReader.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ private void handleInteractableEvent(Player player, E event) {
142142

143143
Iterator<InteractionAction> actions = player.getActions().getAll(InteractionAction.class).iterator();
144144
Entity target = ((InteractableEvent) event).target();
145-
// todo spam click fix: try removing and interrupting regardless instead of setting listeners and target to
146-
// avoid weird behaviour
145+
147146
if (!actions.hasNext()) {
148147
// We have no interaction action, submit a new one.
149148
player.submitAction(new InteractionAction(player, listeners, target, (InteractableEvent) event));
@@ -155,7 +154,7 @@ private void handleInteractableEvent(Player player, E event) {
155154
first.setListeners(listeners);
156155
first.setTarget(target);
157156

158-
// Remove any potential duplicate actions (just in case).
157+
// Remove any potential duplicate actions (shouldn't happen, but just in case).
159158
while (actions.hasNext()) {
160159
actions.next().interrupt();
161160
actions.remove();

src/main/java/io/luna/net/msg/in/WalkingMessageReader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public boolean validate(Player player, WalkingEvent event) {
8080

8181
@Override
8282
public void handle(Player player, WalkingEvent event) {
83-
player.getOverlays().closeWindows();
83+
// Only interrupt actions for non-interaction clicks.
84+
player.getOverlays().closeWindows(event.getOrigin() != WalkingOrigin.INTERACTION);
8485
}
8586
}

src/main/kotlin/api/api/event/Matcher.kt

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ import kotlin.reflect.KClass
2121

2222
/**
2323
* A matcher that routes events of type [E] by deriving a lookup key of type [K].
24-
*
24+
*
2525
* Matchers provide an optimized alternative to scanning every listener in an event pipeline. Each incoming event is
2626
* transformed into a key via [key], and that key is then used to find the matching listeners directly.
27-
*
27+
*
2828
* Matchers may also produce [InteractionActionListener] instances for interaction-driven events so listener execution
2929
* can be deferred until movement and reach requirements are satisfied.
3030
*
@@ -45,7 +45,6 @@ abstract class Matcher<E : Event, K : Any>(private val eventType: KClass<E>) {
4545
* @return The matcher for [E].
4646
* @throws NoSuchElementException If no matcher exists for [E].
4747
*/
48-
@Suppress("UNCHECKED_CAST")
4948
inline fun <reified E : Event, K : Any> get(): Matcher<E, K> {
5049
return get(E::class)
5150
}
@@ -59,8 +58,7 @@ abstract class Matcher<E : Event, K : Any>(private val eventType: KClass<E>) {
5958
*/
6059
@Suppress("UNCHECKED_CAST")
6160
fun <E : Event, K : Any> get(eventType: KClass<E>): Matcher<E, K> {
62-
val matcher = ALL[eventType]
63-
return when (matcher) {
61+
return when (val matcher = ALL[eventType]) {
6462
null -> throw NoSuchElementException("Matcher for event type $eventType was not found.")
6563
else -> matcher as Matcher<E, K>
6664
}
@@ -127,7 +125,7 @@ abstract class Matcher<E : Event, K : Any>(private val eventType: KClass<E>) {
127125

128126
/**
129127
* Registers an optimized listener for [key].
130-
*
128+
*
131129
* The listener will only be considered for events whose computed key matches [key].
132130
*
133131
* @param key The matcher key.
@@ -151,19 +149,19 @@ abstract class Matcher<E : Event, K : Any>(private val eventType: KClass<E>) {
151149

152150
/**
153151
* Registers this matcher with the backing event pipeline for [eventType].
154-
*
152+
*
155153
* The pipeline receives an [EventMatcher] that delegates matching, existence checks, and interaction listener
156154
* creation back to this matcher.
157155
*/
158156
private fun addListener() {
159157
val type = eventType.java
160158
val pipeline = pipelines.get(type)
161-
pipeline.matcher = EventMatcher(this::match, this::has, this::interactions)
159+
pipeline.setMatcher(EventMatcher(this::match, this::has, this::interactions))
162160
}
163161

164162
/**
165163
* Attempts to match [msg] against listeners registered under its computed key.
166-
*
164+
*
167165
* If one or more listeners are found, they are executed in registration order.
168166
*
169167
* @param msg The event to match.
@@ -182,8 +180,8 @@ abstract class Matcher<E : Event, K : Any>(private val eventType: KClass<E>) {
182180

183181
/**
184182
* Creates interaction listeners for [msg] using the listeners registered under its computed key.
185-
*
186-
* Each matched listener is wrapped as an [InteractionActionListener] so it can be executed later by the
183+
*
184+
* Each matched listener is wrapped as an [InteractionActionListener] so it can be executed later by the
187185
* interaction system once the player has satisfied the required reach policy.
188186
*
189187
* @param plr The player attempting the interaction.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package engine.obj
2+
3+
import api.predef.*
4+
import io.luna.game.action.impl.ClimbAction
5+
import io.luna.game.event.impl.ObjectClickEvent
6+
import io.luna.game.event.impl.ServerStateChangedEvent.ServerLaunchEvent
7+
import io.luna.game.model.Direction
8+
import io.luna.game.model.def.GameObjectDefinition
9+
import io.luna.game.model.mob.Player
10+
import io.luna.game.model.`object`.GameObject
11+
12+
/**
13+
* Supported ladder interaction action names.
14+
*/
15+
val LADDER_ACTIONS = listOf("Climb", "Climb-up", "Climb-down")
16+
17+
/**
18+
* Starts a ladder climb for [plr].
19+
*
20+
* @param plr The climbing player.
21+
* @param ladder The ladder being used.
22+
* @param offset The height offset to apply. Positive climbs up, negative climbs down.
23+
*/
24+
fun climb(plr: Player, ladder: GameObject, offset: Int) {
25+
val plrPos = plr.position
26+
val dir = Direction.between(plrPos, ladder.position)
27+
when (Integer.signum(offset)) {
28+
1 -> plr.submitAction(ClimbAction(plr, plrPos.translate(0, 0, 1), dir, "You climb up the ladder."))
29+
-1 -> plr.submitAction(ClimbAction(plr, plrPos.translate(0, 0, -1), dir, "You climb down the ladder."))
30+
}
31+
}
32+
33+
/**
34+
* Returns the object click handler for a ladder action name.
35+
*
36+
* @param name The ladder action name.
37+
* @return The matching object click handler.
38+
* @throws IllegalArgumentException If [name] is not a supported ladder action.
39+
*/
40+
fun handleAction(name: String): EventAction<ObjectClickEvent> =
41+
when (name) {
42+
"Climb-up" -> fun(event: ObjectClickEvent) { climb(event.plr, event.gameObject, 1) }
43+
"Climb-down" -> fun(event: ObjectClickEvent) { climb(event.plr, event.gameObject, -1) }
44+
"Climb" ->
45+
fun(event: ObjectClickEvent) {
46+
val plr = event.plr
47+
plr.newDialogue().options("Climb up", { climb(plr, event.gameObject, 1) },
48+
"Climb down", { climb(plr, event.gameObject, -1) },
49+
"Nevermind", { plr.overlays.closeWindows() }).open()
50+
}
51+
52+
else -> throw IllegalArgumentException("Argument '$name' must be either 'Climb', 'Climb-up', or 'Climb-down'.")
53+
}
54+
55+
/**
56+
* Registers a ladder interaction for the given object action slot.
57+
*
58+
* @param id The object id.
59+
* @param index The action slot index.
60+
* @param name The action name in that slot.
61+
*/
62+
fun handleIndex(id: Int, index: Int, name: String) {
63+
if (LADDER_ACTIONS.contains(name)) {
64+
when (index) {
65+
0 -> object1(id, handleAction(name))
66+
1 -> object2(id, handleAction(name))
67+
2 -> object3(id, handleAction(name))
68+
}
69+
}
70+
}
71+
72+
on(ServerLaunchEvent::class) {
73+
for (def in GameObjectDefinition.ALL) {
74+
if (def.name.equals("Ladder")) {
75+
def.actions.forEachIndexed { index, name -> handleIndex(def.id, index, name) }
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)