tears of guthix and fix of resulting edge cases

This commit is contained in:
Player Name 2026-07-25 16:49:51 +02:00
parent c4fa99d6d3
commit 6d7818951b
10 changed files with 111 additions and 131 deletions

View file

@ -289,9 +289,11 @@ public final class BuildingUtils {
h.setDecorationIndex(decIndex);
int objectId = hotspot.getHotspot().getObjectId(style);
int index = chunk.getIndex(x, y, objectId, -1);
Scenery o = chunk.getObjects()[index][x][y];
if (o != null && objectId == o.getId()) {
SceneryBuilder.replace(o, o.transform(hotspot.getHotspot().getDecorations()[decIndex].getObjectId(style)));
if (index != -1) {
Scenery o = chunk.getObjects()[x][y][index];
if (objectId == o.getId()) {
SceneryBuilder.replace(o, o.transform(hotspot.getHotspot().getDecorations()[decIndex].getObjectId(style)));
}
}
}
}
@ -308,9 +310,11 @@ public final class BuildingUtils {
h.setDecorationIndex(decIndex);
int objectId = bh.getObjectId(style);
int index = chunk.getIndex(x, y, objectId, -1);
Scenery o = chunk.getObjects()[index][x][y];
if (o != null && objectId == o.getId()) {
SceneryBuilder.replace(o, o.transform(bh.getDecorations()[decIndex].getObjectId(style)));
if (index != -1) {
Scenery o = chunk.getObjects()[x][y][index];
if (objectId == o.getId()) {
SceneryBuilder.replace(o, o.transform(bh.getDecorations()[decIndex].getObjectId(style)));
}
}
}
}
@ -423,7 +427,7 @@ public final class BuildingUtils {
if (h != null) {
int objectId = hotspot.getHotspot().getDecorations()[h.getDecorationIndex()].getObjectId(style);
int index = chunk.getIndex(x, y, objectId, -1);
Scenery o = chunk.getObjects()[index][x][y];
Scenery o = chunk.getObjects()[x][y][index];
h.setDecorationIndex(-1);
if (o != null && objectId == o.getId()) {
SceneryBuilder.replace(o, o.transform(hotspot.getHotspot().getObjectId(style)));
@ -442,7 +446,7 @@ public final class BuildingUtils {
if (h != null) {
int objectId = bh.getDecorations()[h.getDecorationIndex()].getObjectId(style);
int index = chunk.getIndex(x, y, objectId, -1);
Scenery o = chunk.getObjects()[index][x][y];
Scenery o = chunk.getObjects()[x][y][index];
h.setDecorationIndex(-1);
if (o != null && objectId == o.getId()) {
SceneryBuilder.replace(o, o.transform(bh.getObjectId(style)));

View file

@ -131,21 +131,22 @@ public final class Room {
int y = spot.getChunkY();
int id = spot.getHotspot().getObjectId(house.getStyle());
int index = chunk.getIndex(x, y, id, -1);
Scenery[][] objects = chunk.getObjects()[index];
Scenery object = objects[x][y];
if (object != null && object.getId() == spot.getHotspot().getObjectId(house.getStyle())) {
if (spot.getDecorationIndex() > -1 && spot.getDecorationIndex() < spot.getHotspot().getDecorations().length) {
id = spot.getHotspot().getDecorations()[spot.getDecorationIndex()].getObjectId(house.getStyle());
if (spot.getHotspot().getType() == BuildHotspotType.CREST) {
id += house.getCrest().ordinal();
if (index != -1) {
Scenery object = chunk.getObjects()[x][y][index];
if (object.getId() == spot.getHotspot().getObjectId(house.getStyle())) {
if (spot.getDecorationIndex() > -1 && spot.getDecorationIndex() < spot.getHotspot().getDecorations().length) {
id = spot.getHotspot().getDecorations()[spot.getDecorationIndex()].getObjectId(house.getStyle());
if (spot.getHotspot().getType() == BuildHotspotType.CREST) {
id += house.getCrest().ordinal();
}
SceneryBuilder.replace(object, object.transform(id, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0)));
} else if (object.getId() == BuildHotspot.WINDOW.getObjectId(house.getStyle()) || (!house.isBuildingMode() && object.getId() == BuildHotspot.CHAPEL_WINDOW.getObjectId(house.getStyle()))) {
SceneryBuilder.replace(object, object.transform(house.getStyle().getWindowStyle().getObjectId(house.getStyle()), object.getRotation(), object.getType()));
}
SceneryBuilder.replace(object, object.transform(id, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0)));
} else if (object.getId() == BuildHotspot.WINDOW.getObjectId(house.getStyle()) || (!house.isBuildingMode() && object.getId() == BuildHotspot.CHAPEL_WINDOW.getObjectId(house.getStyle()))) {
SceneryBuilder.replace(object, object.transform(house.getStyle().getWindowStyle().getObjectId(house.getStyle()), object.getRotation(), object.getType()));
int[] pos = RegionChunk.getRotatedPosition(x, y, object.getSizeX(), object.getSizeY(), 0, rotation.toInteger());
spot.setCurrentX(pos[0]);
spot.setCurrentY(pos[1]);
}
int[] pos = RegionChunk.getRotatedPosition(x, y, object.getSizeX(), object.getSizeY(), 0, rotation.toInteger());
spot.setCurrentX(pos[0]);
spot.setCurrentY(pos[1]);
}
}
if (rotation != Direction.NORTH && chunk.getRotation() == 0) {
@ -167,7 +168,7 @@ public final class Room {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int i = 0; i < RegionChunk.ARRAY_SIZE; i++) {
Scenery object = chunk.getObjects()[i][x][y];
Scenery object = chunk.getObjects()[x][y][i];
if (object != null) {
int id = object.getId();
boolean isBuilt = !object.getDefinition().hasAction("build");
@ -197,7 +198,7 @@ public final class Room {
for (int i = 0; i < RegionChunk.ARRAY_SIZE; i++) {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
Scenery object = chunk.getObjects()[i][x][y];
Scenery object = chunk.getObjects()[x][y][i];
if (object != null && BuildingUtils.isDoorHotspot(object)) {
boolean edge = false;
Room otherRoom = null;

View file

@ -374,10 +374,10 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea {
fun resetChunkIfBarren(chunk: RegionChunk) {
val wipedHoles = ArrayList<Scenery>()
for (i in 0 until chunk.objects.size) {
for (x in 0 until 8) {
for (y in 0 until 8) {
val obj = chunk.objects[i][x][y] ?: continue
for (x in 0 until 8) {
for (y in 0 until 8) {
for (i in 0 until 4) {
val obj = chunk.objects[x][y][i] ?: continue
if (obj.id in HOLES) {
return //this chunk is not barren and hence should not be reset
}
@ -393,10 +393,10 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea {
}
fun countFieldState(chunk: RegionChunk, emptyHoleList: ArrayList<Scenery>, seedHoleList: ArrayList<Scenery>) {
for (i in 0 until chunk.objects.size) {
for (x in 0 until 8) {
for (y in 0 until 8) {
val obj = chunk.objects[i][x][y] ?: continue
for (x in 0 until 8) {
for (y in 0 until 8) {
for (i in 0 until 4) {
val obj = chunk.objects[x][y][i] ?: continue
if (obj.id in HOLES) {
if (obj.location in SEED_LOCS) {
seedHoleList.add(obj)

View file

@ -11,7 +11,9 @@ import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.node.scenery.SceneryBuilder
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
import core.game.world.update.flag.context.Animation
@ -254,14 +256,12 @@ class TearsOfGuthixMinigame : InteractionListener, EventHook<TickEvent>, MapArea
}
override fun defineListeners() {
on(Scenery.WEEPING_WALL_6660, SCENERY, "collect-from") { player, node ->
animate(player, 2043)
val index = TearsOfGuthixGlobalTick.allWalls.indexOf(node.location)
setAttribute(player, attributeIsCollecting, index)
return@on true
}
}
// Timer step per tick while you are in the minigame.
@ -330,7 +330,6 @@ class TearsOfGuthixMinigame : InteractionListener, EventHook<TickEvent>, MapArea
* Global Tick class to randomize the walls consistently for everyone.
*/
class TearsOfGuthixGlobalTick : TickListener {
companion object {
var ticks = 0
var globalWallState = intArrayOf(0, 0, 2, 1, 2, 1, 0, 0, 2, 1)
@ -360,43 +359,29 @@ class TearsOfGuthixGlobalTick : TickListener {
globalWallState = intArrayOf(0) + wallStates
/*
* Explanation: The walls are layered sceneries, which makes it rabidly fucked to change them.
* What I did was to add the tears scenery first (essentially overriding the tears scenery),
* then add the WEEPING_WALL_6660 right after it so that the interactions are still there.
* this is how a layer is like:
* 1 - WEEPING_WALL_6660 - No model, but holds the option "collect-from"
* 2 - BLUE/GREEN/ABSENT - Model of the blue/green/absent waterfall.
* 3 - WEEPING_WALL_6664 - The actual model, but not interactive.
* 6661 - 6664 is left side, 6665 to 6668 is right side
* All walls have two objects:
* 1 - WEEPING_WALL_6660 (type 0) - No model, but holds the option "collect-from"
* 2 - BLUE/GREEN/ABSENT (type 4) - Model of the blue/green/absent waterfall.
* 6665, 6661, 6665 is blue on the north side
* 6662, 6666, 6662 is green on the south side
*/
wallStates.forEachIndexed { index, state ->
val scenery = getScenery(allWalls[index + 1])!!
val newSceneryId = if (state == 2) {
if (index + 1 <= 5) {
Scenery.GREEN_TEARS_6662
} else {
Scenery.GREEN_TEARS_6666
val wallLocation = allWalls[index + 1]
val currentWaterfall = RegionManager.getObject(wallLocation, 4)!!
val newWaterfallId = when (state) {
2 -> {
if (index + 1 <= 5) Scenery.GREEN_TEARS_6662 else Scenery.GREEN_TEARS_6666
}
} else if (state == 1) {
if (index + 1 <= 5) {
Scenery.BLUE_TEARS_6661
} else {
Scenery.BLUE_TEARS_6665
1 -> {
if (index + 1 <= 5) Scenery.BLUE_TEARS_6661 else Scenery.BLUE_TEARS_6665
}
} else {
if (index + 1 <= 5) {
Scenery.ABSENCE_OF_TEARS_6663
} else {
Scenery.ABSENCE_OF_TEARS_6667
else -> {
if (index + 1 <= 5) Scenery.ABSENCE_OF_TEARS_6663 else Scenery.ABSENCE_OF_TEARS_6667
}
}
addScenery(core.game.node.scenery.Scenery(
newSceneryId,
scenery.location,
4,
scenery.rotation
))
addScenery(core.game.node.scenery.Scenery(Scenery.WEEPING_WALL_6660, scenery.location, 0, scenery.rotation))
if (currentWaterfall.id != newWaterfallId) {
SceneryBuilder.replace(currentWaterfall, currentWaterfall.transform(newWaterfallId))
}
}
}
}

View file

@ -1078,9 +1078,10 @@ fun findNPC(id: Int): NPC? {
* @param x the X coordinate to use
* @param y the Y coordinate to use
* @param z the Z coordinate to use
* @param type the scenery type, -1 if any
*/
fun getScenery(x: Int, y: Int, z: Int): Scenery? {
return RegionManager.getObject(z, x, y)
fun getScenery(x: Int, y: Int, z: Int, type: Int = -1): Scenery? {
return RegionManager.getObject(z, x, y, type)
}
/**

View file

@ -185,8 +185,8 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){
}
}
val stat = ArrayList<Scenery>(10)
for (i in 0 until chunk.statObjects.size) {
val obj = chunk.statObjects[i][player.location.chunkOffsetX][player.location.chunkOffsetY]
for (i in 0 until 4) {
val obj = chunk.statObjects[player.location.chunkOffsetX][player.location.chunkOffsetY][i]
if (obj != null) {
stat.add(obj)
}

View file

@ -558,7 +558,7 @@ public class Region {
for (int offsetX = 0; offsetX < 8; offsetX++) {
for (int offsetY = 0; offsetY < 8; offsetY++) {
for (int i = 0; i < 4; i++) {
Scenery object = chunk.getObjects()[i][offsetX][offsetY];
Scenery object = chunk.getObjects()[offsetX][offsetY][i];
if (object != null) {
list.add(object);
}

View file

@ -118,8 +118,8 @@ public class RegionChunk {
this.base = base;
this.currentBase = base;
this.rotation = rotation;
this.statObjects = new Scenery[ARRAY_SIZE][SIZE][SIZE];
this.objects = new Scenery[ARRAY_SIZE][SIZE][SIZE];
this.statObjects = new Scenery[SIZE][SIZE][ARRAY_SIZE];
this.objects = new Scenery[SIZE][SIZE][ARRAY_SIZE];
this.flags = new ChunkFlags(base.getX(), base.getY(), base.getZ());
this.projectileFlags = new ChunkFlags(base.getX(), base.getY(), base.getZ(), true);
}
@ -136,10 +136,10 @@ public class RegionChunk {
* Corrects objects' Locations when copying them from a template region into an instance - private version.
*/
private void rebaseObjects(Scenery[][][] objects) {
for (int i = 0; i < objects.length; i++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
Scenery o = objects[i][x][y];
Scenery o = objects[x][y][i];
if (o == null) {
continue;
}
@ -153,21 +153,21 @@ public class RegionChunk {
* Makes a deep copy of an object list - private version.
*/
private void copyObjects(Scenery[][][] src, Scenery[][][] dest) {
for (int i = 0; i < src.length; i++) {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
Scenery o = src[i][x][y];
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery o = src[x][y][i];
if (o == null) {
continue;
}
Scenery copy = o.transform(o.getId());
if (o instanceof Constructed) {
dest[i][x][y] = copy.asConstructed();
dest[x][y][i] = copy.asConstructed();
} else {
dest[i][x][y] = copy;
dest[x][y][i] = copy;
}
dest[i][x][y].setActive(o.isActive());
dest[i][x][y].setRenderable(o.isRenderable());
dest[x][y][i].setActive(o.isActive());
dest[x][y][i].setRenderable(o.isRenderable());
}
}
}
@ -191,11 +191,11 @@ public class RegionChunk {
* @return The slot it was added into.
*/
public int addStatDyn(Scenery object, int chunkOffsetX, int chunkOffsetY) {
for (int i = 0; i < statObjects.length; i++) {
Scenery current = statObjects[i][chunkOffsetX][chunkOffsetY];
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery current = statObjects[chunkOffsetX][chunkOffsetY][i];
if (current == null) {
statObjects[i][chunkOffsetX][chunkOffsetY] = object;
objects[i][chunkOffsetX][chunkOffsetY] = object.transform(object.getId()); //deep copy so that active/renderable flags don't synchronize across multiple copies of e.g. a POH
statObjects[chunkOffsetX][chunkOffsetY][i] = object;
objects[chunkOffsetX][chunkOffsetY][i] = object.transform(object.getId()); //deep copy so that active/renderable flags don't synchronize across multiple copies of e.g. a POH
return i;
}
if (current.getId() == object.getId()) {
@ -217,10 +217,10 @@ public class RegionChunk {
public int add(Scenery object) {
int chunkOffsetX = object.getLocation().getChunkOffsetX();
int chunkOffsetY = object.getLocation().getChunkOffsetY();
for (int i = 0; i < objects.length; i++) {
Scenery current = objects[i][chunkOffsetX][chunkOffsetY];
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery current = objects[chunkOffsetX][chunkOffsetY][i];
if (current == null) {
objects[i][chunkOffsetX][chunkOffsetY] = object.asConstructed();
objects[chunkOffsetX][chunkOffsetY][i] = object.asConstructed();
object.setRenderable(true);
object.setActive(true);
return i;
@ -321,7 +321,7 @@ public class RegionChunk {
for (int offsetX = 0; offsetX < 8; offsetX++) {
for (int offsetY = 0; offsetY < 8; offsetY++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery object = objects[i][offsetX][offsetY];
Scenery object = objects[offsetX][offsetY][i];
if (object != null) {
list.add(object);
}
@ -364,10 +364,10 @@ public class RegionChunk {
items.clear();
items = null;
}
for (int i = 0; i < objects.length; i++) {
for (int x = 0; x < objects[i].length; x++) {
for (int y = 0; y < objects[i][x].length; y++) {
statObjects[i][x][y] = objects[i][x][y] = null;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
statObjects[x][y][i] = objects[x][y][i] = null;
}
}
}
@ -392,11 +392,11 @@ public class RegionChunk {
*/
protected boolean appendUpdate(Player player, IoBuffer buffer) {
boolean updated = false;
for (int i = 0; i < objects.length; i++) {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
Scenery dyn = objects[i][x][y];
Scenery stat = statObjects[i][x][y];
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery dyn = objects[x][y][i];
Scenery stat = statObjects[x][y][i];
if (dyn == stat) {
continue;
}
@ -522,11 +522,7 @@ public class RegionChunk {
* @return The objects.
*/
public Scenery[] getObjects(int chunkOffsetX, int chunkOffsetY) {
Scenery[] result = new Scenery[ARRAY_SIZE];
for (int i = 0; i < objects.length; i++) {
result[i] = objects[i][chunkOffsetX][chunkOffsetY];
}
return result;
return objects[chunkOffsetX][chunkOffsetY];
}
/**
@ -536,11 +532,7 @@ public class RegionChunk {
* @return The objects.
*/
public Scenery[] getStatObjects(int chunkOffsetX, int chunkOffsetY) {
Scenery[] result = new Scenery[ARRAY_SIZE];
for (int i = 0; i < statObjects.length; i++) {
result[i] = statObjects[i][chunkOffsetX][chunkOffsetY];
}
return result;
return statObjects[chunkOffsetX][chunkOffsetY];
}
/**
@ -567,18 +559,14 @@ public class RegionChunk {
* @param type The scenery type.
*/
public int getIndex(int x, int y, int objectId, int type) {
for (int i = 0; i < objects.length; i++) {
Scenery o = objects[i][x][y];
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery o = objects[x][y][i];
if (o != null) {
if (objectId < 0) {
if (type >= 0 && o.getType() != type) {
continue;
}
// Caller didn't ask for anything specific, so we assume they want whatever *interactable-excluding-examine* object is in here
// We should probably discourage this and instead make functions calling this function more specific
if (o.getDefinition().hasOptions(false)) {
return i;
}
return i;
} else {
if (o.getId() == objectId) {
// TODO: what if there are multiple instances of the same object? Hopefully this is only a theoretical possibility in this game
@ -587,7 +575,7 @@ public class RegionChunk {
}
}
}
return 0;
return -1;
}
/**
@ -672,8 +660,8 @@ public class RegionChunk {
flags.getLandscape()[x][y] = from.getFlags().getLandscape()[x][y];
projectileFlags.getLandscape()[x][y] = from.getProjectileFlags().getLandscape()[x][y];
// Reflag any objects
for (int i = 0; i < objects.length; i++) {
Scenery obj = objects[i][x][y];
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery obj = objects[x][y][i];
if (obj != null) {
LandscapeParser.flagScenery(this, x, y, obj, false, true);
}
@ -691,16 +679,14 @@ public class RegionChunk {
log(this.getClass(), Log.ERR, "Region chunk was already rotated!");
return;
}
Scenery[][][] copy = new Scenery[ARRAY_SIZE][SIZE][SIZE];
int baseX = currentBase.getLocalX();
int baseY = currentBase.getLocalY();
Scenery[][][] copy = new Scenery[SIZE][SIZE][ARRAY_SIZE];
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
for (int i = 0; i < objects.length; i++) {
Scenery object = copy[i][x][y] = objects[i][x][y];
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery object = copy[x][y][i] = objects[x][y][i];
if (object != null) {
SceneryBuilder.remove(object);
objects[i][x][y] = null;
objects[x][y][i] = null;
}
}
}
@ -715,10 +701,10 @@ public class RegionChunk {
log(this.getClass(), Log.ERR, "Attempted to rotate a chunk in a non-cardinal direction - using fallback rotation code. This should be investigated!");
break;
};
for (int i = 0; i < objects.length; i++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
Scenery object = copy[i][x][y];
Scenery object = copy[x][y][i];
if (object != null) {
int[] pos = getRotatedPosition(x, y, object.getDefinition().getSizeX(), object.getDefinition().getSizeY(), object.getRotation(), rotation);
Scenery obj = object.transform(object.getId(), (object.getRotation() + rotation) % 4, object.getLocation().transform(pos[0] - x, pos[1] - y, 0));

View file

@ -355,7 +355,10 @@ object RegionManager {
Region.load(region)
val chunk = region.chunks[loc.chunkX][loc.chunkY][loc.z]
val index = chunk.getIndex(loc.chunkOffsetX, loc.chunkOffsetY, objectId, type)
val obj = chunk.objects[index][loc.chunkOffsetX][loc.chunkOffsetY]
if (index == -1) {
return null
}
val obj = chunk.objects[loc.chunkOffsetX][loc.chunkOffsetY][index]
if (obj != null && !obj.isRenderable) {
return null
}

View file

@ -158,13 +158,13 @@ public final class LandscapeParser {
Region.load(region);
RegionChunk chunk = region.getChunks()[loc.getChunkX()][loc.getChunkY()][loc.getZ()];
int index = chunk.getIndex(loc.getChunkOffsetX(), loc.getChunkOffsetY(), object.getId(), object.getType());
Scenery current = chunk.getObjects()[index][loc.getChunkOffsetX()][loc.getChunkOffsetY()];
if (current == null || current.getId() != object.getId()) {
if (index == -1) {
return null;
}
Scenery current = chunk.getObjects()[loc.getChunkOffsetX()][loc.getChunkOffsetY()][index];
current.setActive(false);
object.setActive(false);
chunk.getObjects()[index][loc.getChunkOffsetX()][loc.getChunkOffsetY()] = null;
chunk.getObjects()[loc.getChunkOffsetX()][loc.getChunkOffsetY()][index] = null;
SceneryDefinition def = object.getDefinition();
// Remove its clipping flags