Final commit

This commit is contained in:
psyopcutie 2026-05-09 11:30:20 +02:00
parent a7c7dea3c7
commit d29dd6f1b7
10 changed files with 1070 additions and 100 deletions

View file

@ -20,6 +20,10 @@ import static rt4.Player.plane;
* @author ceikry
*/
public class API {
public static final int TILE_FLAG_UNDER_ROOF = 0x4;
private static final int DEFAULT_ROOF_VISIBILITY_GROUP_LIMIT = 2;
private static RoofVisibilityHandler roofVisibilityHandler = null;
public static Runnable[] miniMenuCustomActions = new Runnable[10];
public static int customMiniMenuIndex = 0;
public static ArrayList<KeyAdapter> registeredKeyListeners = new ArrayList<>();
@ -246,6 +250,104 @@ public class API {
return client.gameState == 30;
}
public static int[] GetMapFlagTile() {
if (LoginManager.mapFlagX == 0) {
return null;
}
return new int[]{LoginManager.mapFlagX, LoginManager.mapFlagZ};
}
public static void SetRoofVisibilityHandler(RoofVisibilityHandler handler) {
roofVisibilityHandler = handler;
}
public static void ClearRoofVisibilityHandler(RoofVisibilityHandler handler) {
if (roofVisibilityHandler == handler) {
roofVisibilityHandler = null;
}
}
public static int GetRoofVisibilityGroupLimit() {
if (roofVisibilityHandler == null) {
return DEFAULT_ROOF_VISIBILITY_GROUP_LIMIT;
}
return Math.max(DEFAULT_ROOF_VISIBILITY_GROUP_LIMIT, roofVisibilityHandler.getGroupLimit());
}
public static boolean IsRoofVisibilityActive() {
return roofVisibilityHandler != null && roofVisibilityHandler.isActive();
}
public static void DisableRoofVisibilityIfExpired() {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.disableIfExpired();
}
}
public static void EnsureRoofVisibilityBuffers() {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.ensureBuffers();
}
}
public static void ApplyRoofVisibilityRequests() {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.applyRequests();
}
}
public static boolean IsRoofVisibilityPicking() {
return roofVisibilityHandler != null && roofVisibilityHandler.isPicking();
}
public static int GetRoofVisibilityPickScreenX() {
return roofVisibilityHandler == null ? 0 : roofVisibilityHandler.getPickScreenX();
}
public static int GetRoofVisibilityPickScreenY() {
return roofVisibilityHandler == null ? 0 : roofVisibilityHandler.getPickScreenY();
}
public static void ReportRoofVisibilityTile(int sceneX, int sceneZ, int plane) {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.reportTile(sceneX, sceneZ, plane);
}
}
public static boolean IsRoofVisibilityLocPickable(long key) {
return roofVisibilityHandler != null && roofVisibilityHandler.isLocPickable(key);
}
public static void ReportRoofVisibilityLoc(long key, int plane) {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.reportLoc(key, plane);
}
}
public static void BeginRoofVisibilityGroup(int group) {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.beginGroup(group);
}
}
public static void AddRoofVisibilityGroupTile(int group, int plane, int sceneX, int sceneZ) {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.addGroupTile(group, plane, sceneX, sceneZ);
}
}
public static void SetDestinationRoofTarget(int sceneX, int sceneZ) {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.setDestinationTarget(sceneX, sceneZ);
}
}
public static void ClearDestinationRoofTarget() {
if (roofVisibilityHandler != null) {
roofVisibilityHandler.clearDestinationTarget();
}
}
public static void StoreData(String key, Object value) {
PluginRepository.pluginStorage.put(key, value);
PluginRepository.pluginStorage.put("_keystoreDirty", true);

View file

@ -5,6 +5,7 @@ import org.openrs2.deob.annotation.OriginalArg;
import org.openrs2.deob.annotation.OriginalClass;
import org.openrs2.deob.annotation.OriginalMember;
import org.openrs2.deob.annotation.Pc;
import plugin.api.API;
import java.nio.ByteBuffer;
@ -972,7 +973,9 @@ public final class GlModel extends Model {
local161 = MathUtils.sin[arg0];
local163 = MathUtils.cos[arg0];
}
if (arg8 > 0L && RawModel.allowInput && local70 > 0) {
@Pc(165) boolean roofVisibilityLocPick = API.IsRoofVisibilityLocPickable(arg8);
@Pc(168) boolean miniMenuPick = arg8 > 0L;
if ((miniMenuPick || roofVisibilityLocPick) && RawModel.allowInput && local70 > 0) {
@Pc(187) int local187;
@Pc(191) int local191;
if (local84 > 0) {
@ -1046,63 +1049,79 @@ public final class GlModel extends Model {
}
if (anInt3582 >= local187 && anInt3582 <= local191 && RawModel.anInt1053 >= local206 && RawModel.anInt1053 <= local210) {
if (this.pickable) {
Model.aLongArray11[MiniMenu.anInt7++] = arg8;
} else {
if (anIntArray468.length < this.anInt5296) {
anIntArray468 = new int[this.anInt5296];
anIntArray467 = new int[this.anInt5296];
if (miniMenuPick) {
Model.aLongArray11[MiniMenu.anInt7++] = arg8;
}
local362 = 0;
label118:
while (true) {
if (local362 >= this.vertexCount) {
local362 = 0;
while (true) {
if (local362 >= this.triangleCount) {
break label118;
if (roofVisibilityLocPick) {
API.ReportRoofVisibilityLoc(arg8, arg9);
}
} else {
if (this.vertexX == null || this.vertexY == null || this.vertexZ == null || this.anIntArray462 == null || this.aShortArray81 == null) {
if (roofVisibilityLocPick) {
API.ReportRoofVisibilityLoc(arg8, arg9);
}
} else {
if (anIntArray468.length < this.anInt5296) {
anIntArray468 = new int[this.anInt5296];
anIntArray467 = new int[this.anInt5296];
}
local362 = 0;
label118:
while (true) {
if (local362 >= this.vertexCount) {
local362 = 0;
while (true) {
if (local362 >= this.triangleCount) {
break label118;
}
@Pc(698) short local698 = this.aShortArray77[local362];
@Pc(703) short local703 = this.aShortArray82[local362];
@Pc(708) short local708 = this.aShortArray83[local362];
if (this.method4118(anInt3582, RawModel.anInt1053, anIntArray467[local698], anIntArray467[local703], anIntArray467[local708], anIntArray468[local698], anIntArray468[local703], anIntArray468[local708])) {
if (miniMenuPick) {
Model.aLongArray11[MiniMenu.anInt7++] = arg8;
}
if (roofVisibilityLocPick) {
API.ReportRoofVisibilityLoc(arg8, arg9);
}
break label118;
}
local362++;
}
@Pc(698) short local698 = this.aShortArray77[local362];
@Pc(703) short local703 = this.aShortArray82[local362];
@Pc(708) short local708 = this.aShortArray83[local362];
if (this.method4118(anInt3582, RawModel.anInt1053, anIntArray467[local698], anIntArray467[local703], anIntArray467[local708], anIntArray468[local698], anIntArray468[local703], anIntArray468[local708])) {
Model.aLongArray11[MiniMenu.anInt7++] = arg8;
break label118;
}
local362++;
}
}
local369 = this.vertexX[local362];
local373 = this.vertexY[local362];
local377 = this.vertexZ[local362];
if (arg0 != 0) {
local389 = local377 * local161 + local369 * local163 >> 16;
local377 = local377 * local163 - local369 * local161 >> 16;
local369 = this.vertexX[local362];
local373 = this.vertexY[local362];
local377 = this.vertexZ[local362];
if (arg0 != 0) {
local389 = local377 * local161 + local369 * local163 >> 16;
local377 = local377 * local163 - local369 * local161 >> 16;
local369 = local389;
}
local369 += arg5;
local373 += arg6;
local377 += arg7;
local389 = local377 * arg3 + local369 * arg4 >> 16;
local377 = local377 * arg4 - local369 * arg3 >> 16;
local369 = local389;
}
local369 += arg5;
local373 += arg6;
local377 += arg7;
local389 = local377 * arg3 + local369 * arg4 >> 16;
local377 = local377 * arg4 - local369 * arg3 >> 16;
local369 = local389;
local389 = local373 * arg2 - local377 * arg1 >> 16;
local377 = local373 * arg1 + local377 * arg2 >> 16;
if (local377 < 50) {
break;
}
local465 = (local369 << 9) / local377;
local471 = (local389 << 9) / local377;
@Pc(652) int local652 = this.anIntArray462[local362];
@Pc(659) int local659 = this.anIntArray462[local362 + 1];
for (@Pc(661) int local661 = local652; local661 < local659; local661++) {
@Pc(671) int local671 = this.aShortArray81[local661] - 1;
if (local671 == -1) {
local389 = local373 * arg2 - local377 * arg1 >> 16;
local377 = local373 * arg1 + local377 * arg2 >> 16;
if (local377 < 50) {
break;
}
anIntArray468[local671] = local465;
anIntArray467[local671] = local471;
local465 = (local369 << 9) / local377;
local471 = (local389 << 9) / local377;
@Pc(652) int local652 = this.anIntArray462[local362];
@Pc(659) int local659 = this.anIntArray462[local362 + 1];
for (@Pc(661) int local661 = local652; local661 < local659; local661++) {
@Pc(671) int local671 = this.aShortArray81[local661] - 1;
if (local671 == -1) {
break;
}
anIntArray468[local671] = local465;
anIntArray467[local671] = local471;
}
local362++;
}
local362++;
}
}
}

View file

@ -606,11 +606,17 @@ public class MiniMenu {
}
if (actionCode == WALK_HERE) {
if (local36 == 0) {
if (API.IsRoofVisibilityActive()) {
API.ClearDestinationRoofTarget();
}
method3556(Player.plane, local15, local19);
} else if (local36 == 1) {
if (LoginManager.staffModLevel > 0 && Keyboard.pressedKeys[Keyboard.KEY_CTRL] && Keyboard.pressedKeys[Keyboard.KEY_SHIFT]) {
Cheat.teleport(Camera.originX + local15, Camera.originZ + local19, Player.plane);
} else if (PathFinder.findPath(PlayerList.self.movementQueueZ[0], 0, 0, true, 0, local15, 0, 0, 1, local19, PlayerList.self.movementQueueX[0])) {
if (API.IsRoofVisibilityActive()) {
API.SetDestinationRoofTarget(local15, local19);
}
Protocol.outboundBuffer.p1(InterfaceList.anInt5);
Protocol.outboundBuffer.p1(anInt2878);
Protocol.outboundBuffer.p2((int) Camera.yawTarget);

View file

@ -4,6 +4,7 @@ import com.jogamp.opengl.GL2;
import org.openrs2.deob.annotation.OriginalArg;
import org.openrs2.deob.annotation.OriginalMember;
import org.openrs2.deob.annotation.Pc;
import plugin.api.API;
public class SceneGraph {
@ -1744,7 +1745,7 @@ public class SceneGraph {
}
if (local153.plainTile == null) {
if (local153.shapedTile != null) {
method2762(local153.shapedTile, anInt2886, anInt3038, anInt5205, anInt2222, local18, local21, method187(0, local18, local21));
method2762(local153.shapedTile, anInt2886, anInt3038, anInt5205, anInt2222, local18, local21, 0, method187(0, local18, local21));
}
} else
method2610(local153.plainTile, 0, anInt2886, anInt3038, anInt5205, anInt2222, local18, local21, method187(0, local18, local21));
@ -1776,10 +1777,10 @@ public class SceneGraph {
if (local8.plainTile == null) {
if (local8.shapedTile != null) {
if (method187(local27, local18, local21)) {
method2762(local8.shapedTile, anInt2886, anInt3038, anInt5205, anInt2222, local18, local21, true);
method2762(local8.shapedTile, anInt2886, anInt3038, anInt5205, anInt2222, local18, local21, local27, true);
} else {
var24 = true;
method2762(local8.shapedTile, anInt2886, anInt3038, anInt5205, anInt2222, local18, local21, false);
method2762(local8.shapedTile, anInt2886, anInt3038, anInt5205, anInt2222, local18, local21, local27, false);
}
}
} else if (method187(local27, local18, local21)) {
@ -3915,6 +3916,9 @@ public class SceneGraph {
MiniMenu.anInt1742 = arg6;
MiniMenu.anInt2954 = arg7;
}
if (API.IsRoofVisibilityPicking() && method583(API.GetRoofVisibilityPickScreenX() + Rasteriser.centerX, API.GetRoofVisibilityPickScreenY() + Rasteriser.centerY, local315, local331, local299, local307, local323, local291)) {
API.ReportRoofVisibilityTile(arg6, arg7, arg1);
}
if (!GlRenderer.enabled && !arg8) {
Rasteriser.testX = local307 < 0 || local323 < 0 || local291 < 0 || local307 > Rasteriser.width || local323 > Rasteriser.width || local291 > Rasteriser.width;
if (arg0.anInt4869 == -1) {
@ -3938,6 +3942,9 @@ public class SceneGraph {
MiniMenu.anInt1742 = arg6;
MiniMenu.anInt2954 = arg7;
}
if (API.IsRoofVisibilityPicking() && method583(API.GetRoofVisibilityPickScreenX() + Rasteriser.centerX, API.GetRoofVisibilityPickScreenY() + Rasteriser.centerY, local283, local299, local331, local275, local291, local323)) {
API.ReportRoofVisibilityTile(arg6, arg7, arg1);
}
if (GlRenderer.enabled || arg8) {
return;
}
@ -4225,7 +4232,7 @@ public class SceneGraph {
}
@OriginalMember(owner = "client!lh", name = "a", descriptor = "(Lclient!fg;IIIIIIZ)V")
public static void method2762(@OriginalArg(0) ShapedTile arg0, @OriginalArg(1) int arg1, @OriginalArg(2) int arg2, @OriginalArg(3) int arg3, @OriginalArg(4) int arg4, @OriginalArg(5) int arg5, @OriginalArg(6) int arg6, @OriginalArg(7) boolean arg7) {
public static void method2762(@OriginalArg(0) ShapedTile arg0, @OriginalArg(1) int arg1, @OriginalArg(2) int arg2, @OriginalArg(3) int arg3, @OriginalArg(4) int arg4, @OriginalArg(5) int arg5, @OriginalArg(6) int arg6, int arg7, @OriginalArg(7) boolean arg8) {
@Pc(3) int local3 = arg0.anIntArray168.length;
@Pc(5) int local5;
@Pc(15) int local15;
@ -4268,7 +4275,10 @@ public class SceneGraph {
MiniMenu.anInt1742 = arg5;
MiniMenu.anInt2954 = arg6;
}
if (!GlRenderer.enabled && !arg7) {
if (API.IsRoofVisibilityPicking() && method583(API.GetRoofVisibilityPickScreenX() + Rasteriser.centerX, API.GetRoofVisibilityPickScreenY() + Rasteriser.centerY, local156, local160, local164, local39, local148, local152)) {
API.ReportRoofVisibilityTile(arg5, arg6, arg7);
}
if (!GlRenderer.enabled && !arg8) {
Rasteriser.testX = local39 < 0 || local148 < 0 || local152 < 0 || local39 > Rasteriser.width || local148 > Rasteriser.width || local152 > Rasteriser.width;
if (arg0.anIntArray161 == null || arg0.anIntArray161[local5] == -1) {
if (arg0.anIntArray167[local5] != 12345678) {

View file

@ -4,6 +4,7 @@ import org.openrs2.deob.annotation.OriginalArg;
import org.openrs2.deob.annotation.OriginalMember;
import org.openrs2.deob.annotation.Pc;
import plugin.PluginRepository;
import plugin.api.API;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
@ -116,15 +117,15 @@ public final class ScriptRunner {
@OriginalMember(owner = "client!vg", name = "c", descriptor = "Z")
public static boolean aBoolean299 = false;
@OriginalMember(owner = "client!lj", name = "z", descriptor = "[I")
public static int[] anIntArray338 = new int[2];
public static int[] anIntArray338 = new int[8];
@OriginalMember(owner = "client!vl", name = "i", descriptor = "[I")
public static int[] anIntArray518 = new int[2];
public static int[] anIntArray518 = new int[8];
@OriginalMember(owner = "client!tk", name = "K", descriptor = "[I")
public static int[] anIntArray476 = new int[2];
public static int[] anIntArray476 = new int[8];
@OriginalMember(owner = "client!e", name = "xc", descriptor = "[I")
public static int[] anIntArray134 = new int[2];
public static int[] anIntArray134 = new int[8];
@OriginalMember(owner = "client!ge", name = "k", descriptor = "[I")
public static int[] anIntArray205 = new int[2];
public static int[] anIntArray205 = new int[8];
@OriginalMember(owner = "client!nm", name = "W", descriptor = "Lclient!na;")
public static JagString url;
@OriginalMember(owner = "client!bf", name = "B", descriptor = "I")
@ -976,12 +977,16 @@ public final class ScriptRunner {
method2608();
} else {
method960((byte) (anInt3325 - 4 & 0xFF));
method3993(2);
method3993(API.GetRoofVisibilityGroupLimit());
}
}
@OriginalMember(owner = "client!tc", name = "a", descriptor = "(B)I")
public static int method4047() {
return getBaseRoofMode();
}
private static int getBaseRoofMode() {
if (neverRemoveRoofs) {
return 0;
} else if (SceneGraph.allLevelsAreVisible()) {
@ -991,6 +996,11 @@ public final class ScriptRunner {
}
}
public static boolean canUseSelectiveRoofHiding() {
return getBaseRoofMode() == 2;
}
@OriginalMember(owner = "client!ok", name = "a", descriptor = "(IIB)Lclient!ce;")
public static SecondaryLinkedList method3333(@OriginalArg(0) int arg0, @OriginalArg(1) int arg1) {
@Pc(9) SecondaryLinkedList local9 = new SecondaryLinkedList();
@ -1042,18 +1052,31 @@ public final class ScriptRunner {
@OriginalMember(owner = "client!uj", name = "a", descriptor = "(BZII[[[Lclient!bj;I)Z")
public static boolean method4348(@OriginalArg(1) boolean arg0, @OriginalArg(2) int arg1, @OriginalArg(3) int arg2, @OriginalArg(4) Tile[][][] arg3, @OriginalArg(5) int arg4) {
@Pc(14) byte local14 = arg0 ? 1 : (byte) (anInt3325 & 0xFF);
if (local14 == aByteArrayArrayArray15[Player.plane][arg1][arg2]) {
return method4348(arg0, arg1, arg2, arg3, arg4, Player.plane);
}
public static boolean hideRoofAt(int sceneX, int sceneZ, int group, int plane) {
return method4348(false, sceneX, sceneZ, SceneGraph.tiles, group, plane);
}
private static boolean method4348(boolean arg0, int arg1, int arg2, Tile[][][] arg3, int arg4, int plane) {
if (plane < 0 || plane >= 3) {
return false;
} else if ((SceneGraph.renderFlags[Player.plane][arg1][arg2] & 0x4) == 0) {
}
@Pc(14) byte local14 = arg0 ? 1 : (byte) (anInt3325 & 0xFF);
if (local14 == aByteArrayArrayArray15[plane][arg1][arg2]) {
return false;
} else if ((SceneGraph.renderFlags[plane][arg1][arg2] & API.TILE_FLAG_UNDER_ROOF) == 0) {
return false;
} else {
API.BeginRoofVisibilityGroup(arg4);
@Pc(47) int local47 = 0;
@Pc(49) byte local49 = 0;
PathFinder.queueX[0] = arg1;
@Pc(69) int local69 = local49 + 1;
PathFinder.queueZ[0] = arg2;
aByteArrayArrayArray15[Player.plane][arg1][arg2] = local14;
aByteArrayArrayArray15[plane][arg1][arg2] = local14;
while (local47 != local69) {
@Pc(94) int local94 = PathFinder.queueX[local47] >> 16 & 0xFF;
@Pc(102) int local102 = PathFinder.queueX[local47] >> 24 & 0xFF;
@ -1063,13 +1086,13 @@ public final class ScriptRunner {
local47 = local47 + 1 & 0xFFF;
@Pc(130) boolean local130 = false;
@Pc(132) boolean local132 = false;
if ((SceneGraph.renderFlags[Player.plane][local108][local122] & 0x4) == 0) {
if ((SceneGraph.renderFlags[plane][local108][local122] & API.TILE_FLAG_UNDER_ROOF) == 0) {
local130 = true;
}
@Pc(150) int local150;
@Pc(191) int local191;
label238:
for (local150 = Player.plane + 1; local150 <= 3; local150++) {
for (local150 = plane + 1; local150 <= 3; local150++) {
if ((SceneGraph.renderFlags[local150][local108][local122] & 0x8) == 0) {
@Pc(227) int local227;
@Pc(358) int local358;
@ -1124,8 +1147,8 @@ public final class ScriptRunner {
}
}
if (local132) {
if (SceneGraph.tileHeights[Player.plane + 1][local108][local122] > anIntArray205[arg4]) {
anIntArray205[arg4] = SceneGraph.tileHeights[Player.plane + 1][local108][local122];
if (SceneGraph.tileHeights[plane + 1][local108][local122] > anIntArray205[arg4]) {
anIntArray205[arg4] = SceneGraph.tileHeights[plane + 1][local108][local122];
}
local150 = local108 << 7;
if (local150 < anIntArray338[arg4]) {
@ -1139,60 +1162,61 @@ public final class ScriptRunner {
} else if (anIntArray134[arg4] < local191) {
anIntArray134[arg4] = local191;
}
API.AddRoofVisibilityGroupTile(arg4, plane, local108, local122);
}
if (!local130) {
if (local108 >= 1 && aByteArrayArrayArray15[Player.plane][local108 - 1][local122] != local14) {
if (local108 >= 1 && aByteArrayArrayArray15[plane][local108 - 1][local122] != local14) {
PathFinder.queueX[local69] = local108 - 1 | 0x120000 | 0xD3000000;
PathFinder.queueZ[local69] = local122 | 0x130000;
local69 = local69 + 1 & 0xFFF;
aByteArrayArrayArray15[Player.plane][local108 - 1][local122] = local14;
aByteArrayArrayArray15[plane][local108 - 1][local122] = local14;
}
local122++;
if (local122 < 104) {
if (local108 - 1 >= 0 && local14 != aByteArrayArrayArray15[Player.plane][local108 - 1][local122] && (SceneGraph.renderFlags[Player.plane][local108][local122] & 0x4) == 0 && (SceneGraph.renderFlags[Player.plane][local108 - 1][local122 - 1] & 0x4) == 0) {
if (local108 - 1 >= 0 && local14 != aByteArrayArrayArray15[plane][local108 - 1][local122] && (SceneGraph.renderFlags[plane][local108][local122] & API.TILE_FLAG_UNDER_ROOF) == 0 && (SceneGraph.renderFlags[plane][local108 - 1][local122 - 1] & API.TILE_FLAG_UNDER_ROOF) == 0) {
PathFinder.queueX[local69] = 0x52000000 | 0x120000 | local108 - 1;
PathFinder.queueZ[local69] = local122 | 0x130000;
aByteArrayArrayArray15[Player.plane][local108 - 1][local122] = local14;
aByteArrayArrayArray15[plane][local108 - 1][local122] = local14;
local69 = local69 + 1 & 0xFFF;
}
if (local14 != aByteArrayArrayArray15[Player.plane][local108][local122]) {
if (local14 != aByteArrayArrayArray15[plane][local108][local122]) {
PathFinder.queueX[local69] = local108 | 0x13000000 | 0x520000;
PathFinder.queueZ[local69] = local122 | 0x530000;
local69 = local69 + 1 & 0xFFF;
aByteArrayArrayArray15[Player.plane][local108][local122] = local14;
aByteArrayArrayArray15[plane][local108][local122] = local14;
}
if (local108 + 1 < 104 && aByteArrayArrayArray15[Player.plane][local108 + 1][local122] != local14 && (SceneGraph.renderFlags[Player.plane][local108][local122] & 0x4) == 0 && (SceneGraph.renderFlags[Player.plane][local108 + 1][local122 - 1] & 0x4) == 0) {
if (local108 + 1 < 104 && aByteArrayArrayArray15[plane][local108 + 1][local122] != local14 && (SceneGraph.renderFlags[plane][local108][local122] & API.TILE_FLAG_UNDER_ROOF) == 0 && (SceneGraph.renderFlags[plane][local108 + 1][local122 - 1] & API.TILE_FLAG_UNDER_ROOF) == 0) {
PathFinder.queueX[local69] = 0x92000000 | 0x520000 | local108 + 1;
PathFinder.queueZ[local69] = local122 | 0x530000;
aByteArrayArrayArray15[Player.plane][local108 + 1][local122] = local14;
aByteArrayArrayArray15[plane][local108 + 1][local122] = local14;
local69 = local69 + 1 & 0xFFF;
}
}
local122--;
if (local108 + 1 < 104 && local14 != aByteArrayArrayArray15[Player.plane][local108 + 1][local122]) {
if (local108 + 1 < 104 && local14 != aByteArrayArrayArray15[plane][local108 + 1][local122]) {
PathFinder.queueX[local69] = local108 + 1 | 0x920000 | 0x53000000;
PathFinder.queueZ[local69] = local122 | 0x930000;
aByteArrayArrayArray15[Player.plane][local108 + 1][local122] = local14;
aByteArrayArrayArray15[plane][local108 + 1][local122] = local14;
local69 = local69 + 1 & 0xFFF;
}
local122--;
if (local122 >= 0) {
if (local108 - 1 >= 0 && aByteArrayArrayArray15[Player.plane][local108 - 1][local122] != local14 && (SceneGraph.renderFlags[Player.plane][local108][local122] & 0x4) == 0 && (SceneGraph.renderFlags[Player.plane][local108 - 1][local122 + 1] & 0x4) == 0) {
if (local108 - 1 >= 0 && aByteArrayArrayArray15[plane][local108 - 1][local122] != local14 && (SceneGraph.renderFlags[plane][local108][local122] & API.TILE_FLAG_UNDER_ROOF) == 0 && (SceneGraph.renderFlags[plane][local108 - 1][local122 + 1] & API.TILE_FLAG_UNDER_ROOF) == 0) {
PathFinder.queueX[local69] = local108 - 1 | 0xD20000 | 0x12000000;
PathFinder.queueZ[local69] = local122 | 0xD30000;
aByteArrayArrayArray15[Player.plane][local108 - 1][local122] = local14;
aByteArrayArrayArray15[plane][local108 - 1][local122] = local14;
local69 = local69 + 1 & 0xFFF;
}
if (local14 != aByteArrayArrayArray15[Player.plane][local108][local122]) {
if (local14 != aByteArrayArrayArray15[plane][local108][local122]) {
PathFinder.queueX[local69] = local108 | 0xD20000 | 0x93000000;
PathFinder.queueZ[local69] = local122 | 0xD30000;
local69 = local69 + 1 & 0xFFF;
aByteArrayArrayArray15[Player.plane][local108][local122] = local14;
aByteArrayArrayArray15[plane][local108][local122] = local14;
}
if (local108 + 1 < 104 && aByteArrayArrayArray15[Player.plane][local108 + 1][local122] != local14 && (SceneGraph.renderFlags[Player.plane][local108][local122] & 0x4) == 0 && (SceneGraph.renderFlags[Player.plane][local108 + 1][local122 + 1] & 0x4) == 0) {
if (local108 + 1 < 104 && aByteArrayArrayArray15[plane][local108 + 1][local122] != local14 && (SceneGraph.renderFlags[plane][local108][local122] & API.TILE_FLAG_UNDER_ROOF) == 0 && (SceneGraph.renderFlags[plane][local108 + 1][local122 + 1] & API.TILE_FLAG_UNDER_ROOF) == 0) {
PathFinder.queueX[local69] = local108 + 1 | 0xD2000000 | 0x920000;
PathFinder.queueZ[local69] = local122 | 0x930000;
aByteArrayArrayArray15[Player.plane][local108 + 1][local122] = local14;
aByteArrayArrayArray15[plane][local108 + 1][local122] = local14;
local69 = local69 + 1 & 0xFFF;
}
}
@ -1316,9 +1340,12 @@ public final class ScriptRunner {
@OriginalMember(owner = "client!uh", name = "f", descriptor = "(I)V")
public static void method4302() {
API.DisableRoofVisibilityIfExpired();
if (method4047() != 2) {
return;
}
API.EnsureRoofVisibilityBuffers();
boolean roofVisibilityActive = API.IsRoofVisibilityActive();
@Pc(27) byte local27 = (byte) (anInt3325 - 4 & 0xFF);
@Pc(31) int local31 = anInt3325 % 104;
@Pc(33) int local33;
@ -1331,7 +1358,7 @@ public final class ScriptRunner {
if (Player.plane == 3) {
return;
}
for (local33 = 0; local33 < 2; local33++) {
for (local33 = 0; local33 < anIntArray205.length; local33++) {
anIntArray205[local33] = -1000000;
anIntArray338[local33] = 1000000;
anIntArray518[local33] = 0;
@ -1340,14 +1367,17 @@ public final class ScriptRunner {
}
if (Camera.cameraType != 1) {
local33 = SceneGraph.getTileHeight(Player.plane, Camera.renderX, Camera.renderZ);
if (local33 - Camera.anInt40 < 800 && (SceneGraph.renderFlags[Player.plane][Camera.renderX >> 7][Camera.renderZ >> 7] & 0x4) != 0) {
if (local33 - Camera.anInt40 < 800 && (SceneGraph.renderFlags[Player.plane][Camera.renderX >> 7][Camera.renderZ >> 7] & API.TILE_FLAG_UNDER_ROOF) != 0) {
method4348(false, Camera.renderX >> 7, Camera.renderZ >> 7, SceneGraph.tiles, 1);
}
return;
}
if ((SceneGraph.renderFlags[Player.plane][PlayerList.self.xFine >> 7][PlayerList.self.zFine >> 7] & 0x4) != 0) {
if ((SceneGraph.renderFlags[Player.plane][PlayerList.self.xFine >> 7][PlayerList.self.zFine >> 7] & API.TILE_FLAG_UNDER_ROOF) != 0) {
method4348(false, PlayerList.self.xFine >> 7, PlayerList.self.zFine >> 7, SceneGraph.tiles, 0);
}
if (roofVisibilityActive) {
API.ApplyRoofVisibilityRequests();
}
if (Camera.cameraPitch >= 310) {
return;
}
@ -1378,7 +1408,7 @@ public final class ScriptRunner {
} else if (local40 > local135) {
local40--;
}
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & 0x4) != 0) {
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & API.TILE_FLAG_UNDER_ROOF) != 0) {
method4348(false, local33, local40, SceneGraph.tiles, 1);
break;
}
@ -1390,7 +1420,7 @@ public final class ScriptRunner {
local33--;
}
local186 -= 65536;
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & 0x4) != 0) {
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & API.TILE_FLAG_UNDER_ROOF) != 0) {
method4348(false, local33, local40, SceneGraph.tiles, 1);
break;
}
@ -1406,7 +1436,7 @@ public final class ScriptRunner {
} else if (local33 > local162) {
local33--;
}
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & 0x4) != 0) {
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & API.TILE_FLAG_UNDER_ROOF) != 0) {
method4348(false, local33, local40, SceneGraph.tiles, 1);
break;
}
@ -1418,7 +1448,7 @@ public final class ScriptRunner {
local40--;
}
local186 -= 65536;
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & 0x4) != 0) {
if ((SceneGraph.renderFlags[Player.plane][local33][local40] & API.TILE_FLAG_UNDER_ROOF) != 0) {
method4348(false, local33, local40, SceneGraph.tiles, 1);
break;
}

View file

@ -4,6 +4,7 @@ import org.openrs2.deob.annotation.OriginalArg;
import org.openrs2.deob.annotation.OriginalClass;
import org.openrs2.deob.annotation.OriginalMember;
import org.openrs2.deob.annotation.Pc;
import plugin.api.API;
@OriginalClass("client!w")
public final class SoftwareModel extends Model {
@ -833,7 +834,9 @@ public final class SoftwareModel extends Model {
@Pc(223) int x;
@Pc(208) int y;
@Pc(227) int z;
if (key > 0L && RawModel.allowInput && d > 0) {
@Pc(229) boolean roofVisibilityLocPick = API.IsRoofVisibilityLocPickable(key);
@Pc(232) boolean miniMenuPick = key > 0L;
if ((miniMenuPick || roofVisibilityLocPick) && RawModel.allowInput && d > 0) {
if (e > 0) {
v = maxScreenX / c;
y = minScreenX / d;
@ -893,7 +896,12 @@ public final class SoftwareModel extends Model {
}
if (GlModel.anInt3582 >= v && GlModel.anInt3582 <= y && RawModel.anInt1053 >= x && RawModel.anInt1053 <= z) {
if (this.pickable) {
Model.aLongArray11[MiniMenu.anInt7++] = key;
if (miniMenuPick) {
Model.aLongArray11[MiniMenu.anInt7++] = key;
}
if (roofVisibilityLocPick) {
API.ReportRoofVisibilityLoc(key, arg9);
}
} else {
local190 = true;
}
@ -933,7 +941,7 @@ public final class SoftwareModel extends Model {
}
}
try {
this.draw(project1, local190, key, b - d, c - d + 2, arg10);
this.draw(project1, local190, key, b - d, c - d + 2, arg9, arg10);
} catch (@Pc(713) Exception ex) {
ex.printStackTrace();
}
@ -1148,7 +1156,7 @@ public final class SoftwareModel extends Model {
projectSceneZ[local52] = local71;
}
}
this.draw(false, arg6 >= 0L, arg6, this.aShort35, this.aShort35 << 1, null);
this.draw(false, arg6 >= 0L || API.IsRoofVisibilityLocPickable(arg6), arg6, this.aShort35, this.aShort35 << 1, Player.plane, null);
} catch (@Pc(240) RuntimeException local240) {
}
}
@ -1583,7 +1591,7 @@ public final class SoftwareModel extends Model {
}
@OriginalMember(owner = "client!w", name = "a", descriptor = "(ZZJIILclient!ga;)V")
private void draw(@OriginalArg(0) boolean arg0, @OriginalArg(1) boolean arg1, @OriginalArg(2) long arg2, @OriginalArg(3) int arg3, @OriginalArg(4) int arg4, @OriginalArg(5) ParticleSystem arg5) {
private void draw(@OriginalArg(0) boolean arg0, @OriginalArg(1) boolean arg1, @OriginalArg(2) long arg2, @OriginalArg(3) int arg3, @OriginalArg(4) int arg4, int roofHidePlane, @OriginalArg(5) ParticleSystem arg5) {
if (arg4 >= 1600) {
return;
}
@ -1659,7 +1667,12 @@ public final class SoftwareModel extends Model {
}
} else {
if (arg1 && this.pointWithinTriangle(GlModel.anInt3582 + Rasteriser.centerX, RawModel.anInt1053 + Rasteriser.centerY, vertexScreenY[local51], vertexScreenY[local56], vertexScreenY[pri], local65, local69, local73)) {
Model.aLongArray11[MiniMenu.anInt7++] = arg2;
if (arg2 >= 0L) {
Model.aLongArray11[MiniMenu.anInt7++] = arg2;
}
if (API.IsRoofVisibilityLocPickable(arg2)) {
API.ReportRoofVisibilityLoc(arg2, roofHidePlane);
}
arg1 = false;
}
if ((local65 - local69) * (vertexScreenY[pri] - vertexScreenY[local56]) - (vertexScreenY[local51] - vertexScreenY[local56]) * (local73 - local69) > 0) {
@ -1946,7 +1959,7 @@ public final class SoftwareModel extends Model {
projectSceneZ[local52] = local71;
}
}
this.draw(false, false, 0L, this.aShort35, this.aShort35 << 1, null);
this.draw(false, false, 0L, this.aShort35, this.aShort35 << 1, Player.plane, null);
} catch (@Pc(234) RuntimeException local234) {
}
}

View file

@ -0,0 +1,33 @@
package plugin.api
interface RoofVisibilityHandler {
fun getGroupLimit(): Int
fun isActive(): Boolean
fun disableIfExpired()
fun ensureBuffers()
fun applyRequests()
fun isPicking(): Boolean
fun getPickScreenX(): Int
fun getPickScreenY(): Int
fun reportTile(sceneX: Int, sceneZ: Int, plane: Int)
fun isLocPickable(key: Long): Boolean
fun reportLoc(key: Long, plane: Int)
fun beginGroup(group: Int)
fun addGroupTile(group: Int, plane: Int, sceneX: Int, sceneZ: Int)
fun setDestinationTarget(sceneX: Int, sceneZ: Int)
fun clearDestinationTarget()
}

View file

@ -0,0 +1,703 @@
package SmartRoofs
import plugin.api.API
import plugin.api.MiniMenuAction
import plugin.api.MiniMenuEntry
import plugin.api.RoofVisibilityHandler
import rt4.Camera
import rt4.Cs1ScriptRunner
import rt4.LocType
import rt4.MathUtils
import rt4.Player
import rt4.PlayerList
import rt4.SceneGraph
import rt4.ScriptRunner
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
object RoofHider {
private const val TILE_FLAG_UNDER_ROOF = API.TILE_FLAG_UNDER_ROOF
private const val SMART_ROOFS_ACTIVE_GRACE_MS = 750L
private const val MAP_FLAG_ROOF_GROUP = 2
private const val HOVER_GROUP_START = 3
private const val HOVER_TARGET_LIMIT = 8
private const val HOVER_HOLD_MS = 125L
private const val HOVER_PRIMARY_HIT_DISTANCE = 64
private const val HOVER_BUILDING_FOOTPRINT_PRIMARY_HIT_DISTANCE = 12
private const val HOVER_UPPER_PLANE_TILE_SEARCH_RADIUS = 2
private const val HOVER_BUILDING_FOOTPRINT_X_PADDING = 2
private const val HOVER_BUILDING_FOOTPRINT_TOP_PADDING = 64
private const val HOVER_BUILDING_FOOTPRINT_BOTTOM_PADDING = 4
private const val HOVER_OUTSIDE_GROUND_HIT_MS = 50L
private const val DESTINATION_TARGET_GROUP = HOVER_GROUP_START + HOVER_TARGET_LIMIT
const val SELECTIVE_ROOF_GROUP_COUNT = DESTINATION_TARGET_GROUP + 1
private const val DESTINATION_HOLD_MS = 20000L
private const val DESTINATION_REACHED_DISTANCE = 1
@JvmField
val HANDLER: RoofVisibilityHandler = object : RoofVisibilityHandler {
override fun getGroupLimit(): Int = SELECTIVE_ROOF_GROUP_COUNT
override fun isActive(): Boolean = isSmartRoofsActive()
override fun disableIfExpired() = disableSmartRoofsIfExpired()
override fun ensureBuffers() = ensureSelectiveRoofBuffers()
override fun applyRequests() {
hideMapFlagRoof()
hideHoverTargets()
hideDestinationTarget()
}
override fun isPicking(): Boolean = isHoverPicking()
override fun getPickScreenX(): Int = getHoverPickScreenX()
override fun getPickScreenY(): Int = getHoverPickScreenY()
override fun reportTile(sceneX: Int, sceneZ: Int, plane: Int) {
setHoverTile(sceneX, sceneZ, plane)
}
override fun isLocPickable(key: Long): Boolean = isHoverLocPickable(key)
override fun reportLoc(key: Long, plane: Int) {
setHoverLoc(key, plane)
}
override fun beginGroup(group: Int) = beginBuildingFootprint(group)
override fun addGroupTile(group: Int, plane: Int, sceneX: Int, sceneZ: Int) {
addBuildingFootprintTile(group, plane, sceneX, sceneZ)
}
override fun setDestinationTarget(sceneX: Int, sceneZ: Int) {
this@RoofHider.setDestinationTarget(sceneX, sceneZ)
}
override fun clearDestinationTarget() = this@RoofHider.clearDestinationTarget()
}
private var smartRoofsEnabled = false
private var smartRoofsActiveUntil = 0L
private var smartRoofsWasActive = false
private var mapFlagRoofX = -1
private var mapFlagRoofZ = -1
private var mapFlagRoofRadius = 0
private var hoverPickActive = false
private var hoverPickScreenX = 0
private var hoverPickScreenY = 0
private val hoverTargetX = IntArray(HOVER_TARGET_LIMIT) { -1 }
private val hoverTargetZ = IntArray(HOVER_TARGET_LIMIT) { -1 }
private val hoverTargetPlane = IntArray(HOVER_TARGET_LIMIT) { -1 }
private val hoverPrimaryHitX = IntArray(HOVER_TARGET_LIMIT) { Int.MIN_VALUE }
private val hoverPrimaryHitY = IntArray(HOVER_TARGET_LIMIT) { Int.MIN_VALUE }
private val hoverTargetUntil = LongArray(HOVER_TARGET_LIMIT)
private val buildingFootprintMinX = IntArray(HOVER_TARGET_LIMIT) { Int.MAX_VALUE }
private val buildingFootprintMaxX = IntArray(HOVER_TARGET_LIMIT) { Int.MIN_VALUE }
private val buildingFootprintMinY = IntArray(HOVER_TARGET_LIMIT) { Int.MAX_VALUE }
private val buildingFootprintMaxY = IntArray(HOVER_TARGET_LIMIT) { Int.MIN_VALUE }
private val buildingFootprintTiles = Array(HOVER_TARGET_LIMIT) { Array(104) { BooleanArray(104) } }
private var lastHoverTileX = -1
private var lastHoverTileZ = -1
private var lastHoverTilePlane = -1
private var lastHoverTileValid = false
private var lastHoverTileAt = 0L
private var lastHoverHitAt = 0L
private var destinationTargetX = -1
private var destinationTargetZ = -1
private var destinationTargetUntil = 0L
fun isSmartRoofsActive(): Boolean {
return smartRoofsEnabled && isSmartRoofsActive(System.currentTimeMillis()) && ScriptRunner.canUseSelectiveRoofHiding()
}
private fun isSmartRoofsActive(now: Long): Boolean {
return now <= smartRoofsActiveUntil
}
private fun updateSmartRoofsPulse(now: Long) {
smartRoofsActiveUntil = now + SMART_ROOFS_ACTIVE_GRACE_MS
smartRoofsWasActive = true
}
private fun disableSmartRoofsIfExpired() {
val now = System.currentTimeMillis()
if (!smartRoofsWasActive || isSmartRoofsActive(now)) {
return
}
clearSmartRoofsState()
smartRoofsWasActive = false
ScriptRunner.method2218()
}
private fun clearSmartRoofsState() {
clearMapFlagRoof()
clearHoverRequest()
clearDestinationTarget()
smartRoofsEnabled = false
}
fun setSmartRoofsEnabled(enabled: Boolean) {
if (!enabled) {
clearSmartRoofsState()
return
}
smartRoofsEnabled = true
}
fun updateSmartRoofs() {
if (!smartRoofsEnabled) {
clearSmartRoofsState()
return
}
val now = System.currentTimeMillis()
val wasActive = isSmartRoofsActive(now)
updateSmartRoofsPulse(now)
if (!ScriptRunner.canUseSelectiveRoofHiding()) {
clearSmartRoofsState()
return
}
val rebuild = !wasActive ||
ScriptRunner.method4047() != 2 ||
ScriptRunner.aByteArrayArrayArray15 == null ||
ScriptRunner.anIntArray205.size < SELECTIVE_ROOF_GROUP_COUNT
if (rebuild) {
ScriptRunner.method2218()
} else {
ensureSelectiveRoofBuffers()
}
}
private fun ensureSelectiveRoofBuffers() {
if (ScriptRunner.aByteArrayArrayArray15 == null) {
ScriptRunner.method960(((ScriptRunner.anInt3325 - 4) and 0xFF).toByte())
}
if (ScriptRunner.anIntArray205.size < SELECTIVE_ROOF_GROUP_COUNT) {
ScriptRunner.method3993(SELECTIVE_ROOF_GROUP_COUNT)
}
}
fun setMapFlagRoof(sceneX: Int, sceneZ: Int, radius: Int) {
if (!isSmartRoofsActive()) {
clearMapFlagRoof()
return
}
if (!isInSceneBounds(sceneX, sceneZ)) {
clearMapFlagRoof()
return
}
mapFlagRoofX = sceneX
mapFlagRoofZ = sceneZ
mapFlagRoofRadius = radius.coerceIn(0, 8)
}
fun clearMapFlagRoof() {
mapFlagRoofX = -1
mapFlagRoofZ = -1
mapFlagRoofRadius = 0
}
private fun hideMapFlagRoof() {
hideRoofFromSeedTile(mapFlagRoofX, mapFlagRoofZ, mapFlagRoofRadius, MAP_FLAG_ROOF_GROUP, Player.plane)
}
private fun hideRoofFromSeedTile(sceneX: Int, sceneZ: Int, radius: Int, group: Int, plane: Int): Boolean {
if (sceneX == -1 || !isRoofHidePlane(plane) || ScriptRunner.anIntArray205.size <= group) {
return false
}
for (currentRadius in 0..radius) {
for (dx in -currentRadius..currentRadius) {
for (dz in -currentRadius..currentRadius) {
if (abs(dx) != currentRadius && abs(dz) != currentRadius) {
continue
}
if (tryHideRoofFromTile(sceneX + dx, sceneZ + dz, group, plane)) {
return true
}
}
}
}
return false
}
private fun tryHideRoofFromTile(sceneX: Int, sceneZ: Int, group: Int, plane: Int): Boolean {
if (!isHideableRoofTile(sceneX, sceneZ, plane)) {
return false
}
val masks = ScriptRunner.aByteArrayArrayArray15 ?: return false
val currentRoofMask = (ScriptRunner.anInt3325 and 0xFF).toByte()
if (masks[plane][sceneX][sceneZ] == currentRoofMask) {
return true
}
return ScriptRunner.hideRoofAt(sceneX, sceneZ, group, plane)
}
fun updateHoverPick(entries: Array<out MiniMenuEntry>?) {
if (!isSmartRoofsActive()) {
clearHoverRequest()
return
}
hoverPickActive = false
if (entries == null) {
clearExpiredHoverTargets(System.currentTimeMillis())
return
}
for (entry in entries) {
if (entry.action == MiniMenuAction.WALK_HERE && entry.subjectIndex == 0L) {
hoverPickActive = true
hoverPickScreenX = entry.intArg1
hoverPickScreenY = entry.intArg2
return
}
}
clearExpiredHoverTargets(System.currentTimeMillis())
}
private fun isHoverPicking(): Boolean {
return isSmartRoofsActive() && hoverPickActive
}
private fun getHoverPickScreenX(): Int = hoverPickScreenX
private fun getHoverPickScreenY(): Int = hoverPickScreenY
private fun setHoverTile(sceneX: Int, sceneZ: Int, plane: Int) {
if (!isSmartRoofsActive() || !hoverPickActive || !isInSceneBounds(sceneX, sceneZ)) {
return
}
val valid = updateHoverTarget(sceneX, sceneZ, Player.plane, plane)
rememberHoverTileHit(sceneX, sceneZ, plane, valid)
if (valid) {
markHoverHit()
return
}
clearExpiredHoverTargets(System.currentTimeMillis())
}
private fun isHoverLocPickable(key: Long): Boolean {
if (!isSmartRoofsActive() || !hoverPickActive || key == 0L) {
return false
}
val entityType = (key.toInt() shr 29) and 0x3
val locShape = ((key shr 14) and 0x3F).toInt()
return entityType == 2 && isBuildingLocShape(locShape)
}
private fun setHoverLoc(key: Long, plane: Int) {
if (!isHoverLocPickable(key)) {
return
}
val sceneX = (key and 0x7F).toInt()
val sceneZ = ((key shr 7) and 0x7F).toInt()
if (!isInSceneBounds(sceneX, sceneZ)) {
return
}
if (updateHoverTarget(sceneX, sceneZ, Player.plane, plane)) {
markHoverHit()
}
}
private fun updateHoverTarget(sceneX: Int, sceneZ: Int, plane: Int, hitPlane: Int): Boolean {
var targetX = sceneX
var targetZ = sceneZ
val now = System.currentTimeMillis()
if (!isHideableRoofTile(targetX, targetZ, plane)) {
val packed = findNearbyHideableRoofTile(sceneX, sceneZ, plane, hitPlane)
if (packed == -1) {
return false
}
targetX = packed shr 7
targetZ = packed and 0x7F
}
if (!isHideableRoofTile(targetX, targetZ, plane)) {
return false
}
val duplicateSlot = findHoverTargetByBuildingFootprint(targetX, targetZ, plane)
if (duplicateSlot != -1) {
updateHoverPrimaryHit(duplicateSlot)
extendHoverTarget(duplicateSlot, now)
return true
}
var oldestSlot = 0
var oldestUntil = Long.MAX_VALUE
for (i in 0 until HOVER_TARGET_LIMIT) {
if (hoverTargetX[i] == targetX && hoverTargetZ[i] == targetZ && hoverTargetPlane[i] == plane) {
updateHoverPrimaryHit(i)
extendHoverTarget(i, now)
return true
}
if (hoverTargetX[i] == -1 || now > hoverTargetUntil[i]) {
hoverTargetX[i] = targetX
hoverTargetZ[i] = targetZ
hoverTargetPlane[i] = plane
clearBuildingFootprint(i)
updateHoverPrimaryHit(i)
extendHoverTarget(i, now)
return true
}
if (hoverTargetUntil[i] < oldestUntil) {
oldestUntil = hoverTargetUntil[i]
oldestSlot = i
}
}
hoverTargetX[oldestSlot] = targetX
hoverTargetZ[oldestSlot] = targetZ
hoverTargetPlane[oldestSlot] = plane
clearBuildingFootprint(oldestSlot)
updateHoverPrimaryHit(oldestSlot)
extendHoverTarget(oldestSlot, now)
return true
}
private fun findNearbyHideableRoofTile(sceneX: Int, sceneZ: Int, plane: Int, hitPlane: Int): Int {
if (hitPlane <= plane) {
return -1
}
for (radius in 1..HOVER_UPPER_PLANE_TILE_SEARCH_RADIUS) {
for (dx in -radius..radius) {
for (dz in -radius..radius) {
if (abs(dx) != radius && abs(dz) != radius) {
continue
}
val targetX = sceneX + dx
val targetZ = sceneZ + dz
if (isHideableRoofTile(targetX, targetZ, plane)) {
return (targetX shl 7) or targetZ
}
}
}
}
return -1
}
private fun rememberHoverTileHit(sceneX: Int, sceneZ: Int, plane: Int, valid: Boolean) {
lastHoverTileX = sceneX
lastHoverTileZ = sceneZ
lastHoverTilePlane = plane
lastHoverTileValid = valid
lastHoverTileAt = System.currentTimeMillis()
}
private fun markHoverHit() {
lastHoverHitAt = System.currentTimeMillis()
}
private fun clearHoverRequest() {
hoverPickActive = false
clearHoverTargets()
}
private fun findHoverTargetByBuildingFootprint(sceneX: Int, sceneZ: Int, plane: Int): Int {
for (i in 0 until HOVER_TARGET_LIMIT) {
if (hoverTargetX[i] == -1 || hoverTargetPlane[i] != plane) {
continue
}
if (isBuildingFootprintTile(i, sceneX, sceneZ)) {
return i
}
}
return -1
}
private fun extendHoverTarget(index: Int, now: Long) {
hoverTargetUntil[index] = now + HOVER_HOLD_MS
}
private fun getHoverGroup(index: Int): Int = HOVER_GROUP_START + index
private fun getHoverIndex(group: Int): Int {
val index = group - HOVER_GROUP_START
return if (index >= 0 && index < HOVER_TARGET_LIMIT) index else -1
}
private fun clearExpiredHoverTargets(now: Long) {
for (i in 0 until HOVER_TARGET_LIMIT) {
if (hoverTargetX[i] != -1 && now > hoverTargetUntil[i]) {
if (isPickInsideBuildingFootprint(i)) {
extendHoverTarget(i, now)
continue
}
clearHoverTarget(i)
}
}
}
private fun clearHoverTargets() {
for (i in 0 until HOVER_TARGET_LIMIT) {
clearHoverTarget(i)
}
lastHoverTileX = -1
lastHoverTileZ = -1
lastHoverTilePlane = -1
lastHoverTileValid = false
lastHoverTileAt = 0L
lastHoverHitAt = 0L
}
private fun clearHoverTarget(index: Int) {
hoverTargetX[index] = -1
hoverTargetZ[index] = -1
hoverTargetPlane[index] = -1
hoverPrimaryHitX[index] = Int.MIN_VALUE
hoverPrimaryHitY[index] = Int.MIN_VALUE
hoverTargetUntil[index] = 0L
clearBuildingFootprint(index)
}
private fun hideHoverTargets() {
val now = System.currentTimeMillis()
val keepAlive = Cs1ScriptRunner.aBoolean108
for (i in 0 until HOVER_TARGET_LIMIT) {
if (hoverTargetX[i] == -1) {
continue
}
val hasBuildingArea = hasBuildingFootprint(i)
val buildingFootprintKeepAlive = isPickInsideBuildingFootprint(i)
val primaryHitKeepAlive = isPickNearPrimaryHit(i, if (hasBuildingArea) HOVER_BUILDING_FOOTPRINT_PRIMARY_HIT_DISTANCE else HOVER_PRIMARY_HIT_DISTANCE)
if (hasRecentOutsideGroundHit(i, now, buildingFootprintKeepAlive || hasBuildingArea && primaryHitKeepAlive)) {
clearHoverTarget(i)
continue
}
if (keepAlive || primaryHitKeepAlive || buildingFootprintKeepAlive) {
extendHoverTarget(i, now)
} else if (now > hoverTargetUntil[i]) {
clearHoverTarget(i)
continue
}
val group = getHoverGroup(i)
hideRoofFromSeedTile(hoverTargetX[i], hoverTargetZ[i], 0, group, hoverTargetPlane[i])
}
}
private fun updateHoverPrimaryHit(index: Int) {
hoverPrimaryHitX[index] = hoverPickScreenX
hoverPrimaryHitY[index] = hoverPickScreenY
}
private fun isPickNearPrimaryHit(index: Int, distance: Int): Boolean {
if (!hoverPickActive || hoverPrimaryHitX[index] == Int.MIN_VALUE) {
return false
}
val dx = hoverPickScreenX - hoverPrimaryHitX[index]
val dy = hoverPickScreenY - hoverPrimaryHitY[index]
return dx * dx + dy * dy <= distance * distance
}
private fun isPickInsideBuildingFootprint(index: Int): Boolean {
if (!hoverPickActive || !hasBuildingFootprint(index)) {
return false
}
for (x in 0 until 104) {
for (z in 0 until 104) {
if (buildingFootprintTiles[index][x][z] && isPickInsideBuildingFootprintTile(index, x, z)) {
return true
}
}
}
return false
}
private fun hasBuildingFootprint(index: Int): Boolean {
return buildingFootprintMinX[index] != Int.MAX_VALUE
}
private fun clearBuildingFootprint(index: Int) {
buildingFootprintMinX[index] = Int.MAX_VALUE
buildingFootprintMaxX[index] = Int.MIN_VALUE
buildingFootprintMinY[index] = Int.MAX_VALUE
buildingFootprintMaxY[index] = Int.MIN_VALUE
for (x in 0 until 104) {
buildingFootprintTiles[index][x].fill(false)
}
}
private fun beginBuildingFootprint(group: Int) {
val index = getHoverIndex(group)
if (index != -1) {
clearBuildingFootprint(index)
}
}
private fun addBuildingFootprintTile(group: Int, plane: Int, sceneX: Int, sceneZ: Int) {
val index = getHoverIndex(group)
if (index == -1 || plane + 1 >= SceneGraph.tileHeights.size) {
return
}
buildingFootprintTiles[index][sceneX][sceneZ] = true
addBuildingFootprintTileBounds(index, plane, sceneX, sceneZ)
}
private fun addBuildingFootprintTileBounds(index: Int, plane: Int, sceneX: Int, sceneZ: Int) {
addBuildingFootprintBoundPoint(index, projectBuildingFootprintPoint(sceneX shl 7, sceneZ shl 7, SceneGraph.tileHeights[plane + 1][sceneX][sceneZ]))
addBuildingFootprintBoundPoint(index, projectBuildingFootprintPoint((sceneX + 1) shl 7, sceneZ shl 7, SceneGraph.tileHeights[plane + 1][sceneX + 1][sceneZ]))
addBuildingFootprintBoundPoint(index, projectBuildingFootprintPoint((sceneX + 1) shl 7, (sceneZ + 1) shl 7, SceneGraph.tileHeights[plane + 1][sceneX + 1][sceneZ + 1]))
addBuildingFootprintBoundPoint(index, projectBuildingFootprintPoint(sceneX shl 7, (sceneZ + 1) shl 7, SceneGraph.tileHeights[plane + 1][sceneX][sceneZ + 1]))
}
private fun isPickInsideBuildingFootprintTile(index: Int, sceneX: Int, sceneZ: Int): Boolean {
val plane = hoverTargetPlane[index]
if (!isRoofHidePlane(plane) || plane + 1 >= SceneGraph.tileHeights.size) {
return false
}
val point0 = projectBuildingFootprintPoint(sceneX shl 7, sceneZ shl 7, SceneGraph.tileHeights[plane + 1][sceneX][sceneZ])
val point1 = projectBuildingFootprintPoint((sceneX + 1) shl 7, sceneZ shl 7, SceneGraph.tileHeights[plane + 1][sceneX + 1][sceneZ])
val point2 = projectBuildingFootprintPoint((sceneX + 1) shl 7, (sceneZ + 1) shl 7, SceneGraph.tileHeights[plane + 1][sceneX + 1][sceneZ + 1])
val point3 = projectBuildingFootprintPoint(sceneX shl 7, (sceneZ + 1) shl 7, SceneGraph.tileHeights[plane + 1][sceneX][sceneZ + 1])
if (point0 == Long.MIN_VALUE || point1 == Long.MIN_VALUE || point2 == Long.MIN_VALUE || point3 == Long.MIN_VALUE) {
return false
}
val minX = min(min((point0 shr 32).toInt(), (point1 shr 32).toInt()), min((point2 shr 32).toInt(), (point3 shr 32).toInt()))
val maxX = max(max((point0 shr 32).toInt(), (point1 shr 32).toInt()), max((point2 shr 32).toInt(), (point3 shr 32).toInt()))
val minY = min(min(point0.toInt(), point1.toInt()), min(point2.toInt(), point3.toInt()))
val maxY = max(max(point0.toInt(), point1.toInt()), max(point2.toInt(), point3.toInt()))
return hoverPickScreenX >= minX - HOVER_BUILDING_FOOTPRINT_X_PADDING &&
hoverPickScreenX <= maxX + HOVER_BUILDING_FOOTPRINT_X_PADDING &&
hoverPickScreenY >= minY - HOVER_BUILDING_FOOTPRINT_TOP_PADDING &&
hoverPickScreenY <= maxY + HOVER_BUILDING_FOOTPRINT_BOTTOM_PADDING
}
private fun hasRecentOutsideGroundHit(index: Int, now: Long, pickInsideBuildingFootprint: Boolean): Boolean {
if (pickInsideBuildingFootprint || lastHoverTileAt == 0L || now - lastHoverTileAt > HOVER_OUTSIDE_GROUND_HIT_MS) {
return false
}
if (lastHoverTileValid || lastHoverTileAt <= lastHoverHitAt || now - lastHoverHitAt <= HOVER_OUTSIDE_GROUND_HIT_MS) {
return false
}
if (lastHoverTilePlane != hoverTargetPlane[index]) {
return false
}
return !isBuildingFootprintTile(index, lastHoverTileX, lastHoverTileZ)
}
private fun isBuildingFootprintTile(index: Int, sceneX: Int, sceneZ: Int): Boolean {
return isInSceneBounds(sceneX, sceneZ) && buildingFootprintTiles[index][sceneX][sceneZ]
}
private fun addBuildingFootprintBoundPoint(index: Int, packedPoint: Long) {
if (packedPoint == Long.MIN_VALUE) {
return
}
val screenX = (packedPoint shr 32).toInt()
val screenY = packedPoint.toInt()
if (screenX < buildingFootprintMinX[index]) {
buildingFootprintMinX[index] = screenX
}
if (screenX > buildingFootprintMaxX[index]) {
buildingFootprintMaxX[index] = screenX
}
if (screenY < buildingFootprintMinY[index]) {
buildingFootprintMinY[index] = screenY
}
if (screenY > buildingFootprintMaxY[index]) {
buildingFootprintMaxY[index] = screenY
}
}
private fun projectBuildingFootprintPoint(xFine: Int, zFine: Int, yFine: Int): Long {
val localX = xFine - Camera.renderX
val localY = yFine - Camera.anInt40
val localZ = zFine - Camera.renderZ
val sinPitch = MathUtils.sin[Camera.cameraPitch]
val cosPitch = MathUtils.cos[Camera.cameraPitch]
val sinYaw = MathUtils.sin[Camera.cameraYaw]
val cosYaw = MathUtils.cos[Camera.cameraYaw]
val rotatedX = localX * cosYaw + localZ * sinYaw shr 16
val rotatedZ = localZ * cosYaw - localX * sinYaw shr 16
val screenYDepth = rotatedZ * cosPitch + localY * sinPitch shr 16
if (screenYDepth < 50) {
return Long.MIN_VALUE
}
val rotatedY = localY * cosPitch - rotatedZ * sinPitch shr 16
val screenX = (rotatedX shl 9) / screenYDepth
val screenY = (rotatedY shl 9) / screenYDepth
return (screenX.toLong() shl 32) or (screenY.toLong() and 0xFFFFFFFFL)
}
fun setDestinationTarget(sceneX: Int, sceneZ: Int) {
if (!isSmartRoofsActive()) {
clearDestinationTarget()
return
}
if (!isInSceneBounds(sceneX, sceneZ)) {
clearDestinationTarget()
return
}
destinationTargetX = sceneX
destinationTargetZ = sceneZ
destinationTargetUntil = System.currentTimeMillis() + DESTINATION_HOLD_MS
}
fun clearDestinationTarget() {
destinationTargetX = -1
destinationTargetZ = -1
destinationTargetUntil = 0L
}
private fun hasDestinationTarget(): Boolean {
if (destinationTargetX == -1) {
return false
}
if (System.currentTimeMillis() > destinationTargetUntil) {
clearDestinationTarget()
return false
}
val self = PlayerList.self
if (self != null &&
abs(self.movementQueueX[0] - destinationTargetX) <= DESTINATION_REACHED_DISTANCE &&
abs(self.movementQueueZ[0] - destinationTargetZ) <= DESTINATION_REACHED_DISTANCE
) {
clearDestinationTarget()
return false
}
return true
}
private fun hideDestinationTarget() {
if (hasDestinationTarget()) {
hideRoofFromSeedTile(destinationTargetX, destinationTargetZ, 0, DESTINATION_TARGET_GROUP, Player.plane)
}
}
private fun isInSceneBounds(sceneX: Int, sceneZ: Int): Boolean {
return sceneX in 0 until 104 && sceneZ in 0 until 104
}
private fun isHideableRoofTile(sceneX: Int, sceneZ: Int, plane: Int): Boolean {
if (ScriptRunner.aByteArrayArrayArray15 == null) {
return false
}
return isRoofHidePlane(plane) &&
isInSceneBounds(sceneX, sceneZ) &&
(SceneGraph.renderFlags[plane][sceneX][sceneZ].toInt() and TILE_FLAG_UNDER_ROOF) != 0
}
private fun isRoofHidePlane(plane: Int): Boolean {
return plane in 0 until 3
}
private fun isBuildingLocShape(shape: Int): Boolean {
return shape == LocType.WALL_STRAIGHT ||
shape == LocType.WALL_DIAGONALCORNER ||
shape == LocType.WALL_L ||
shape == LocType.WALL_SQUARECORNER ||
shape == LocType.WALL_DIAGONAL ||
shape == LocType.WALLDECOR_STRAIGHT_XOFFSET ||
shape == LocType.WALLDECOR_STRAIGHT_ZOFFSET ||
shape == LocType.WALLDECOR_DIAGONAL_XOFFSET ||
shape == LocType.WALLDECOR_DIAGONAL_ZOFFSET ||
shape == LocType.WALLDECOR_DIAGONAL_BOTH
}
}

View file

@ -0,0 +1,51 @@
package SmartRoofs
import plugin.Plugin
import plugin.api.API
import plugin.api.MiniMenuEntry
class plugin : Plugin() {
override fun Init() {
API.SetRoofVisibilityHandler(RoofHider.HANDLER)
}
override fun Draw(timeDelta: Long) {
if (!API.IsLoggedIn()) {
clearTargets()
return
}
API.SetRoofVisibilityHandler(RoofHider.HANDLER)
RoofHider.setSmartRoofsEnabled(true)
RoofHider.updateSmartRoofs()
val mapFlag = API.GetMapFlagTile()
if (mapFlag == null) {
RoofHider.clearMapFlagRoof()
} else {
RoofHider.setMapFlagRoof(mapFlag[0], mapFlag[1], MAP_FLAG_RADIUS)
}
}
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
RoofHider.updateHoverPick(currentEntries)
}
override fun OnLogout() {
clearTargets()
}
override fun OnPluginsReloaded(): Boolean {
clearTargets()
API.ClearRoofVisibilityHandler(RoofHider.HANDLER)
return false
}
private fun clearTargets() {
RoofHider.clearMapFlagRoof()
RoofHider.setSmartRoofsEnabled(false)
}
companion object {
private const val MAP_FLAG_RADIUS = 0
}
}

View file

@ -0,0 +1,3 @@
AUTHOR='Edith'
DESCRIPTION='Hides roofs on cursor hover and destination travel.'
VERSION=1.0