diff --git a/src/main/java/io/blert/client/WebSocketEventHandler.java b/src/main/java/io/blert/client/WebSocketEventHandler.java index e51fb33..8d8299d 100644 --- a/src/main/java/io/blert/client/WebSocketEventHandler.java +++ b/src/main/java/io/blert/client/WebSocketEventHandler.java @@ -25,11 +25,7 @@ import io.blert.BlertPlugin; import io.blert.BlertPluginPanel; -import io.blert.core.Challenge; -import io.blert.core.ChallengeMode; -import io.blert.core.OverallTicks; -import io.blert.core.RecordableChallenge; -import io.blert.core.Stage; +import io.blert.core.*; import io.blert.events.*; import io.blert.events.Event; import io.blert.json.*; diff --git a/src/main/java/io/blert/core/ChallengeRecordingConfig.java b/src/main/java/io/blert/core/ChallengeRecordingConfig.java index 784821c..6c7b5f6 100644 --- a/src/main/java/io/blert/core/ChallengeRecordingConfig.java +++ b/src/main/java/io/blert/core/ChallengeRecordingConfig.java @@ -25,8 +25,10 @@ import java.util.EnumMap; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Optional; +import java.util.Set; import javax.annotation.Nullable; import lombok.Getter; @@ -42,8 +44,6 @@ public enum ObjectCapture { ON_CREATION, } - // TODO(frolv): Support absolute hitpoints varbits, with current HP in one varbit and the maximum in another - // (e.g. TFA's 6099 and 6100). @Getter public static class HitpointsVarbit { private final int id; @@ -92,6 +92,14 @@ public int getBaseHitpoints(int scale) { private final Map> objectCaptures = new EnumMap<>(ObjectType.class); private final Map npcDefinitions = new HashMap<>(); + /** Varbits whose values are sent every tick. */ + @Getter + private final Set varbits = new LinkedHashSet<>(); + + /** Varps whose values are sent every tick. */ + @Getter + private final Set varps = new LinkedHashSet<>(); + ChallengeRecordingConfig(io.blert.json.RecordingConfig json, Challenge challenge) { this.version = json.version; @@ -158,5 +166,12 @@ private void addScope(@Nullable io.blert.json.RecordingConfig.Scope scope) { } } } + + if (scope.varbits != null) { + varbits.addAll(scope.varbits); + } + if (scope.varps != null) { + varps.addAll(scope.varps); + } } } diff --git a/src/main/java/io/blert/core/DataTracker.java b/src/main/java/io/blert/core/DataTracker.java index b76db71..cd0b9fb 100644 --- a/src/main/java/io/blert/core/DataTracker.java +++ b/src/main/java/io/blert/core/DataTracker.java @@ -28,6 +28,7 @@ import io.blert.util.Tick; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import javax.annotation.Nullable; import lombok.*; import lombok.extern.slf4j.Slf4j; @@ -174,6 +175,11 @@ public void tick() { dispatchEvent(objectsEvent); } + VarsEvent varsEvent = readVars(); + if (varsEvent != null) { + dispatchEvent(varsEvent); + } + if (pendingCompletion != null) { completeStage(true); } @@ -873,6 +879,20 @@ private Optional createGenericNpc(@NonNull NPC npc) { .map(definition -> new GenericTrackedNpc(npc, definition, generateRoomId(npc), challenge.getScale())); } + private @Nullable VarsEvent readVars() { + if (recordingConfig.getVarbits().isEmpty() && recordingConfig.getVarps().isEmpty()) { + return null; + } + + List varbits = recordingConfig.getVarbits().stream() + .map(id -> new VarsEvent.Value(id, client.getServerVarbitValue(id))) + .collect(Collectors.toList()); + List varps = recordingConfig.getVarps().stream() + .map(id -> new VarsEvent.Value(id, client.getServerVarpValue(id))) + .collect(Collectors.toList()); + return new VarsEvent(getStage(), getTick(), varbits, varps); + } + public final void onGameStateChanged(GameStateChanged event) { if (terminating()) { return; diff --git a/src/main/java/io/blert/events/EventType.java b/src/main/java/io/blert/events/EventType.java index 575e0ca..80a046c 100644 --- a/src/main/java/io/blert/events/EventType.java +++ b/src/main/java/io/blert/events/EventType.java @@ -41,6 +41,7 @@ public enum EventType { NPC_ATTACK(10), PLAYER_SPELL(11), OBJECTS(12), + VARS(13), MAIDEN_CRAB_LEAK(100), MAIDEN_BLOOD_SPLATS(101), diff --git a/src/main/java/io/blert/events/VarsEvent.java b/src/main/java/io/blert/events/VarsEvent.java new file mode 100644 index 0000000..128830d --- /dev/null +++ b/src/main/java/io/blert/events/VarsEvent.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026 Alexei Frolov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the “Software”), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package io.blert.events; + +import io.blert.core.Stage; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +public class VarsEvent extends Event { + @AllArgsConstructor + @Getter + public static class Value { + private final int id; + private final int value; + + @Override + public String toString() { + return id + "=" + value; + } + } + + private final List varbits; + private final List varps; + + public VarsEvent(Stage stage, int tick, List varbits, List varps) { + super(EventType.VARS, stage, tick, null); + this.varbits = varbits; + this.varps = varps; + } + + @Override + protected String eventDataString() { + return "varbits=" + varbits + ", varps=" + varps; + } +} diff --git a/src/main/java/io/blert/json/Event.java b/src/main/java/io/blert/json/Event.java index 77fd69a..5d89c79 100644 --- a/src/main/java/io/blert/json/Event.java +++ b/src/main/java/io/blert/json/Event.java @@ -44,6 +44,7 @@ public class Event { public NpcAttack npcAttack; public Spell playerSpell; public Objects objects; + public Vars vars; // ToB event-specific fields public List maidenBloodSplats; @@ -170,6 +171,16 @@ public static class ObjectUpdate { public List removed; } + public static class Vars { + public static class Value { + public int id; + public int value; + } + + public List varbits; + public List varps; + } + public static class NpcAttack { public int attack; public String target; diff --git a/src/main/java/io/blert/json/JsonEventTranslator.java b/src/main/java/io/blert/json/JsonEventTranslator.java index 87d5551..240acea 100644 --- a/src/main/java/io/blert/json/JsonEventTranslator.java +++ b/src/main/java/io/blert/json/JsonEventTranslator.java @@ -221,6 +221,14 @@ public static Event toJson(io.blert.events.Event event, @Nullable String challen break; } + case VARS: { + VarsEvent varsEvent = (VarsEvent) event; + json.vars = new Event.Vars(); + json.vars.varbits = toVarValues(varsEvent.getVarbits()); + json.vars.varps = toVarValues(varsEvent.getVarps()); + break; + } + case MAIDEN_BLOOD_SPLATS: { MaidenBloodSplatsEvent bloodSplatsEvent = (MaidenBloodSplatsEvent) event; json.maidenBloodSplats = toCoordsList(bloodSplatsEvent.getBloodSplats()); @@ -512,6 +520,20 @@ private static Coords worldPointToCoords(WorldPoint point) { return new Coords(point.getX(), point.getY()); } + private static @Nullable List toVarValues(List values) { + if (values.isEmpty()) { + return null; + } + return values.stream() + .map(v -> { + Event.Vars.Value value = new Event.Vars.Value(); + value.id = v.getId(); + value.value = v.getValue(); + return value; + }) + .collect(Collectors.toList()); + } + /** * Adds tracked NPC-specific properties to the given {@link Event.Npc} based * on the {@link NpcEvent}. diff --git a/src/main/java/io/blert/json/RecordingConfig.java b/src/main/java/io/blert/json/RecordingConfig.java index c8f38b7..ada18b1 100644 --- a/src/main/java/io/blert/json/RecordingConfig.java +++ b/src/main/java/io/blert/json/RecordingConfig.java @@ -53,6 +53,8 @@ public static class NpcDefinition { public static class Scope { public List objects; public List npcDefinitions; + public List varbits; + public List varps; } public static class ChallengeScope {