Hopefully last review comments for greg

This commit is contained in:
Player Name 2026-07-28 20:07:12 +02:00
parent f2456b676f
commit 33f1ea94d1
28 changed files with 206 additions and 208 deletions

View file

@ -16,7 +16,7 @@ import core.tools.RandomFunction;
import org.rs09.consts.Sounds;
import static core.api.ContentAPIKt.playGlobalAudio;
import static core.game.node.entity.combat.MultihitTargetsKt.findMultihitTargets;
import static core.game.node.entity.combat.MultihitTargetsKt.findMultihitTargetsForD2h;
/**
* Handles the Powerstab special attack.
@ -73,7 +73,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements
if (!multi) {
return super.swing(entity, victim, state);
}
List<Entity> list = findMultihitTargets(entity.getLocation(), entity, CombatStyle.MELEE);
List<Entity> list = findMultihitTargetsForD2h(entity.getLocation(), entity, CombatStyle.MELEE);
BattleState[] targets = new BattleState[list.size()];
int count = 0;
for (Entity e : list) {

View file

@ -26,14 +26,12 @@ import static core.api.ContentAPIKt.*;
import static core.api.regionspec.RegionSpecificationKt.fillWith;
import static core.api.regionspec.RegionSpecificationKt.using;
/**
* Manages the player's house.
* @author Emperor
*
*/
public final class HouseManager {
/**
* The current region.
*/
@ -98,7 +96,6 @@ public final class HouseManager {
*/
}
public void parse(JSONObject data){
location = HouseLocation.values()[Integer.parseInt( data.get("location").toString())];
style = HousingStyle.values()[Integer.parseInt( data.get("style").toString())];
@ -261,7 +258,7 @@ public final class HouseManager {
*/
public void expelGuests(Player player) {
if (isLoaded()) {
for (int z = 0; z < 4; z++) {
for (int z = 0; z < Region.PLANES; z++) {
ArrayList<Player> players = new ArrayList<>(houseRegion.assemblePlayerList(z));
if (dungeonRegion != null) {
players.addAll(dungeonRegion.assemblePlayerList(z));
@ -292,7 +289,7 @@ public final class HouseManager {
if (h.getDecorationIndex() > -1) {
Decoration d = h.getHotspot().getDecorations()[h.getDecorationIndex()];
if (d == Decoration.PORTAL) {
return houseRegion.getBaseLocation().transform(x * 8 + h.getChunkX(), y * 8 + h.getChunkY() + 2, 0);
return houseRegion.getBaseLocation().transform(x*RegionChunk.SIZE + h.getChunkX(), y*RegionChunk.SIZE + h.getChunkY() + 2, 0);
}
}
}
@ -308,9 +305,9 @@ public final class HouseManager {
*/
public void redecorate(HousingStyle style) {
this.style = style;
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < Region.PLANES; z++) {
for (int x = 0; x < RegionChunk.SIZE; x++) {
for (int y = 0; y < RegionChunk.SIZE; y++) {
Room room = rooms[z][x][y];
if (room != null) {
room.decorate(style);
@ -325,9 +322,9 @@ public final class HouseManager {
*/
@Deprecated
public void clearRooms() {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < Region.PLANES; z++) {
for (int x = 0; x < RegionChunk.SIZE; x++) {
for (int y = 0; y < RegionChunk.SIZE; y++) {
rooms[z][x][y] = null;
}
}
@ -368,7 +365,7 @@ public final class HouseManager {
}
private DynamicRegion getPreparedRegion() {
ZoneBorders borders = DynamicRegion.reserveArea(8,8);
ZoneBorders borders = DynamicRegion.reserveArea(Region.CHUNKS_SIZE,Region.CHUNKS_SIZE);
DynamicRegion region = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6);
region.setBorders(borders);
region.setBuild(true);
@ -604,8 +601,8 @@ public final class HouseManager {
int startY = 99;
int endX = 0;
int endY = 0;
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < Region.CHUNKS_SIZE; x++) {
for (int y = 0; y < Region.CHUNKS_SIZE; y++) {
if (rooms[0][x][y] != null) {
if (x < startX) startX = x;
if (y < startY) startY = y;
@ -699,9 +696,6 @@ public final class HouseManager {
* Checks if the house region was constructed and active.
* @return {@code True} if an active region for the house exists.
*/
//public boolean isLoaded() {
// return (houseRegion != null) || (dungeonRegion != null);
//}
public boolean isLoaded() {
return (houseRegion != null && houseRegion.isActive()) || (dungeonRegion != null && dungeonRegion.isActive());
}

View file

@ -149,7 +149,7 @@ public final class Room {
}
}
}
if (rotation != Direction.NORTH && chunk.getRotation() == 0) {
if (rotation != Direction.NORTH && chunk.getRotation() == 0) { //TODO: it is absolutely insane that this is using Direction.NORTH and such rather than RegionChunk.NORTH_ROTATION and such.
chunk.rotate(rotation);
}
if (!house.isBuildingMode()) {

View file

@ -214,11 +214,11 @@ class WoodcuttingListener : InteractionListener {
player.animate(SkillingTool.getHatchet(player).animation)
val playersAroundMe: List<Player> = RegionManager.getLocalPlayers(player.location, 2)
.stream()
.filter { p -> p.name != player.name }
.filter { p -> p != player }
.toList()
val soundIndex = RandomFunction.random(0, woodcuttingSounds.size)
for (player in playersAroundMe) {
playAudio(player, woodcuttingSounds[soundIndex])
for (p in playersAroundMe) {
playAudio(p, woodcuttingSounds[soundIndex])
}
}
}

View file

@ -68,7 +68,7 @@ public final class AncientTeleportPlugin extends MagicSpell {
return false;
}
homeTeleport((Player) entity, Location.create(3087, 3495, 0));
entity.dispatch(new SpellCastEvent(SpellBook.ANCIENT, getSpellId(), target));
entity.dispatch(new SpellCastEvent(SpellBook.ANCIENT, getSpellId(), target));
} else if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.ANCIENT)) {
if (!super.meetsRequirements(entity, true, true)) {
entity.getTeleporter().getCurrentTeleport().stop();

View file

@ -17,8 +17,6 @@ import static core.api.ContentAPIKt.getAttribute;
*/
@Initializable
public final class DraynorBankGuard extends DialoguePlugin {
private final static String HAS_SEEN_RECORDING = "draynor-recording";
/**
* Represents the coins item needed to re-watch the recording.
*/
@ -58,7 +56,7 @@ public final class DraynorBankGuard extends DialoguePlugin {
public boolean handle(int interfaceId, int buttonId) {
switch (stage) {
case 0:
if (!getAttribute(player, HAS_SEEN_RECORDING, false)) {
if (!getAttribute(player, DBRCutscene.HAS_SEEN_RECORDING, false)) {
interpreter.sendOptions("Select an option", "Can I deposit my stuff here?", "That wall doesn't look very good.", "Sorry, I don't want anything.");
stage = 1;
} else {

View file

@ -134,7 +134,7 @@ class ScriptAPI(private val bot: Player) {
*/
fun getNearestNodeFromList(acceptedNames: List<String>, isObject: Boolean): Node? {
val region = RegionManager.forId(bot.location.regionId)
val list = if (isObject) region.assembleObjectList(bot.location.z) else region.assembleEntityList(bot.location.z)
val list = if (isObject) region.assembleObjectList(bot.location.z) else region.assembleNodeList(bot.location.z)
return processEvaluationList(list, acceptedName = acceptedNames)
}
@ -147,7 +147,7 @@ class ScriptAPI(private val bot: Player) {
*/
fun getNearestNode(id: Int, isObject: Boolean): Node? {
val region = RegionManager.forId(bot.location.regionId)
val list = if (isObject) region.assembleObjectList(bot.location.z) else region.assembleEntityList(bot.location.z)
val list = if (isObject) region.assembleObjectList(bot.location.z) else region.assembleNodeList(bot.location.z)
return processEvaluationList(list, acceptedId = id)
}
@ -159,7 +159,7 @@ class ScriptAPI(private val bot: Player) {
*/
fun getNearestNode(entityName: String): Node? {
val region = RegionManager.forId(bot.location.regionId)
val list = region.assembleEntityList(bot.location.z)
val list = region.assembleNodeList(bot.location.z)
return processEvaluationList(list, acceptedName = listOf(entityName))
}
@ -172,7 +172,7 @@ class ScriptAPI(private val bot: Player) {
*/
fun getNearestNode(name: String, isObject: Boolean): Node? {
val region = RegionManager.forId(bot.location.regionId)
val list = region.assembleEntityList(bot.location.z)
val list = region.assembleNodeList(bot.location.z)
return processEvaluationList(list, acceptedName = listOf(name))
}

View file

@ -240,7 +240,7 @@ public abstract class MovementPulse extends Pulse {
private boolean tryInteract() {
Location ml = mover.getLocation();
// Allow being within 1 square of moving entities to interact with them.
int radius = destination instanceof Entity && ((Entity)destination).getWalkingQueue().hasPath() ? 1 : 0;
int radius = destination instanceof Entity && ((Entity) destination).getWalkingQueue().hasPath() ? 1 : 0;
if (interactLocation == null)
return false;
boolean atInteractLocation = Math.max(Math.abs(ml.getX() - interactLocation.getX()), Math.abs(ml.getY() - interactLocation.getY())) <= radius;

View file

@ -20,7 +20,12 @@ private fun isAttackable(victim: Entity, attacker: Entity, combatStyle: CombatSt
fun findMultihitTargets(center: Location, attacker: Entity, combatStyle: CombatStyle?): List<Entity> {
val targets = getLocalEntities(center, 1).filter { it != attacker && isAttackable(it, attacker, combatStyle, false) }
return targets.take(9)
return targets.take(9) //https://runescape.wiki/w/Vesta%27s_spear "the opponent and up to 9 additional targets in a 3x3 area around the player"; https://oldschool.runescape.wiki/w/Ice_Barrage "up to nine targets"
}
fun findMultihitTargetsForD2h(center: Location, attacker: Entity, combatStyle: CombatStyle?): List<Entity> {
val targets = getLocalEntities(center, 1).filter { it != attacker && isAttackable(it, attacker, combatStyle, false) }
return targets.filter { it.location.withinDistance(center, 1) } .take(14) //runescape.wiki/w/Dragon_2h_sword?oldid=859949 "up to 14 enemies in the squares around the player (Only directly in front of and to the sides, it does not attack those diagonal of the player)"
}
fun findMultihitTargetsForChinchompa(center: Location, attacker: Entity, victim: Entity): List<Entity> {
@ -50,5 +55,5 @@ fun findMultihitTargetsForChinchompa(center: Location, attacker: Entity, victim:
}
targets = targetCandidates.filter { it is NPC || isOwnerOf(it, npcs) }
}
return targets.take(9)
}
return targets.take(9) //https://runescape.wiki/w/Chinchompa "up to 9 enemies"
}

View file

@ -856,7 +856,7 @@ public class Player extends Entity {
getZoneMonitor().getZones().clear();
playerFlags.setLastSceneGraph(null);
playerFlags.setUpdateSceneGraph(false);
playerFlags.setLastViewport(new RegionChunk[13][13]);
playerFlags.setLastViewport(new RegionChunk[MapChunkRenderer.BUILD_AREA_SIZE][MapChunkRenderer.BUILD_AREA_SIZE]);
renderInfo.getLocalNPCs().clear();
renderInfo.getLocalPlayers().clear();
renderInfo.setLastLocation(null);

View file

@ -151,7 +151,7 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){
if (args.size != 1) reject(player, "::removeobject doesn't support arguments")
val obj = getObject(player.location)
if (obj == null) {
sendMessage(player, "All ten objects on the tile were null.")
sendMessage(player, "All four objects on the tile were null.")
return@define
}
sendMessage(player, "The first object found on the tile was ${obj.id}; it will now be removed.")
@ -161,12 +161,10 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){
define("objects", Privilege.STANDARD, "::objects", "Prints a list of all ten sceneries at your current coordinates") { player, args ->
if (args.size != 1) reject(player, "::objects doesn't support arguments")
val chunk = getRegionChunk(player.location)
val region = RegionManager.forId(player.location.regionId)
fun dump(label: String, objects: List<Scenery?>) {
sendMessage(player, "--- $label ---")
val nulls = ArrayList<Int>(10)
for (i in 0 until objects.size) {
val o = objects[i]
for ((i, o) in objects.withIndex()) {
if (o == null) {
nulls.add(i)
} else {
@ -174,7 +172,7 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){
val r = if (o.isRenderable) "R" else ""
val a = if (o.isActive) "A" else ""
var props = c + r + a
if (props.length > 0) {
if (props.isNotEmpty()) {
props = " *$props"
}
sendMessage(player, " $i: $o$props")

View file

@ -34,8 +34,8 @@ class ConfigParser : Commands {
fun reloadConfigs(callback: () -> Unit) {
GlobalScope.launch {
RegionManager.apply { r ->
r.addScenery.clear()
r.removeScenery.clear()
r.addSceneries.clear()
r.removeSceneries.clear()
}
Repository.npcs.toTypedArray().forEach { npc ->

View file

@ -45,8 +45,8 @@ class ObjectOverrideParser {
val obj = Scenery(id, loc, type, rotation)
val region = RegionManager.forId(loc.regionId)
when (mode) {
"remove" -> region.removeScenery.add(obj) //type and rotation are currently ignored, but this can be changed in Region.java if needed
"add" -> region.addScenery.add(obj)
"remove" -> region.removeSceneries.add(obj) //type and rotation are currently ignored, but this can be changed in Region.java if needed
"add" -> region.addSceneries.add(obj)
else -> log(this::class.java, Log.ERR, "Ignored unknown ObjectOverride mode $mode!")
}
count++

View file

@ -32,6 +32,21 @@ import static core.api.ContentAPIKt.log;
* @author Emperor
*/
public class Region {
/**
* The number of tiles per region.
*/
public static final int SIZE = 64;
/**
* The number of chunks per region.
*/
public static final int CHUNKS_SIZE = 8;
/**
* The number of z levels per x,y coordinate.
*/
public static final int PLANES = 4;
/**
* The region x-coordinate.
*/
@ -45,7 +60,7 @@ public class Region {
/**
* The chunks in this region.
*/
protected RegionChunk[][][] chunks = new RegionChunk[8][8][4];
protected RegionChunk[][][] chunks = new RegionChunk[CHUNKS_SIZE][CHUNKS_SIZE][PLANES];
/**
* The activity pulse.
@ -105,8 +120,8 @@ public class Region {
/**
* Any scenery overrides for this region
*/
private ArrayList<Scenery> addScenery = new ArrayList<>();
private ArrayList<Scenery> removeScenery = new ArrayList<>();
private final ArrayList<Scenery> addSceneries = new ArrayList<>();
private final ArrayList<Scenery> removeSceneries = new ArrayList<>();
/**
* Constructs a new {@code Region} {@code Object}.
@ -117,10 +132,10 @@ public class Region {
this.x = x;
this.y = y;
Location swCorner = getBaseLocation();
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
Location loc = swCorner.transform(x*8, y*8, z);
for (x = 0; x < CHUNKS_SIZE; x++) {
for (y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
Location loc = swCorner.transform(x*RegionChunk.SIZE, y*RegionChunk.SIZE, z);
chunks[x][y][z] = new RegionChunk(loc, 0);
}
}
@ -157,9 +172,9 @@ public class Region {
*/
public void add(RegionZone zone) {
regionZones.add(zone);
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
for (NPC npc : chunks[x][y][z].getNpcs()) {
npc.getZoneMonitor().updateLocation(npc.getLocation());
}
@ -175,9 +190,9 @@ public class Region {
public void remove(RegionZone zone) {
regionZones.remove(zone);
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
for (NPC npc : chunks[x][y][z].getNpcs()) {
npc.getZoneMonitor().updateLocation(npc.getLocation());
}
@ -213,9 +228,9 @@ public class Region {
if (isViewed()) {
return false;
}
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
if (!chunks[x][y][z].getPlayers().isEmpty()) {
return false;
}
@ -240,9 +255,9 @@ public class Region {
if (!active) {
active = true;
load(this);
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
for (NPC npc : chunks[x][y][z].getNpcs()) {
if (npc.isActive()) {
Repository.addRenderableNPC(npc);
@ -300,11 +315,11 @@ public class Region {
return;
}
byte[][][] mapscapeData = new byte[4][64][64];
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
r.chunks[x][y][z].getFlags().setLandscape(new boolean[8][8]);
byte[][][] mapscapeData = new byte[PLANES][SIZE][SIZE];
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
r.chunks[x][y][z].getFlags().setLandscape(new boolean[RegionChunk.SIZE][RegionChunk.SIZE]);
}
}
}
@ -328,14 +343,14 @@ public class Region {
}
}
MapscapeParser.clipMapscape(r, r.chunks, mapscapeData);
for (Scenery object : r.removeScenery) {
for (Scenery object : r.removeSceneries) {
// Get the actual object, not the instance that's kept in the removeScenery array
Scenery realObject = RegionManager.getObject(object.getLocation()); //can be extended to take into account type and rotation if needed
if (realObject != null) {
SceneryBuilder.remove(realObject);
}
}
for (Scenery object : r.addScenery) {
for (Scenery object : r.addSceneries) {
SceneryBuilder.add(object);
}
} catch (Throwable e) {
@ -353,20 +368,20 @@ public class Region {
r.flagActive();
return false;
}
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
if (!force && !chunks[x][y][z].getPlayers().isEmpty()) {
log(CommunicationInfo.class, Log.ERR, "Players still in region!");
log(CommunicationInfo.class, Log.ERR, "Players still in region! (region id " + getId() + "; " + chunks[x][y][z].getPlayers().size() + " players on a chunk)");
r.flagActive();
return false;
}
}
}
}
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
if (!(r instanceof DynamicRegion)) {
for (NPC npc : chunks[x][y][z].getNpcs()) {
npc.onRegionInactivity();
@ -552,12 +567,12 @@ public class Region {
public List<Scenery> assembleObjectList(int z) {
ArrayList<Scenery> list = new ArrayList<>();
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
RegionChunk chunk = chunks[x][y][z];
for (int offsetX = 0; offsetX < 8; offsetX++) {
for (int offsetY = 0; offsetY < 8; offsetY++) {
for (int i = 0; i < 4; i++) {
for (int offsetX = 0; offsetX < RegionChunk.SIZE; offsetX++) {
for (int offsetY = 0; offsetY < RegionChunk.SIZE; offsetY++) {
for (int i = 0; i < RegionChunk.ARRAY_SIZE; i++) {
Scenery object = chunk.getObjects()[offsetX][offsetY][i];
if (object != null) {
list.add(object);
@ -572,8 +587,8 @@ public class Region {
public List<NPC> assembleNpcList(int z) {
ArrayList<NPC> list = new ArrayList<>();
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
RegionChunk chunk = chunks[x][y][z];
list.addAll(chunk.getNpcs());
}
@ -583,8 +598,8 @@ public class Region {
public List<Player> assemblePlayerList(int z) {
ArrayList<Player> list = new ArrayList<>();
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
RegionChunk chunk = chunks[x][y][z];
list.addAll(chunk.getPlayers());
}
@ -592,7 +607,7 @@ public class Region {
return list;
}
public List<Node> assembleEntityList(int z) {
public List<Node> assembleNodeList(int z) {
ArrayList<Node> list = new ArrayList<>(assembleObjectList(z));
list.addAll(assemblePlayerList(z));
list.addAll(assembleNpcList(z));
@ -601,7 +616,6 @@ public class Region {
/**
* Getter for region aggro tolerances.
* @return
*/
public HashMap<String, Long> getTolerances() {
return tolerances;
@ -610,10 +624,10 @@ public class Region {
/**
* Getters for addScenery and removeScenery.
*/
public ArrayList<Scenery> getAddScenery() {
return addScenery;
public ArrayList<Scenery> getAddSceneries() {
return addSceneries;
}
public ArrayList<Scenery> getRemoveScenery() {
return removeScenery;
public ArrayList<Scenery> getRemoveSceneries() {
return removeSceneries;
}
}

View file

@ -19,6 +19,7 @@ import core.game.world.map.build.DynamicRegion;
import core.game.world.map.build.LandscapeParser;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
import org.rs09.consts.Items;
import java.util.ArrayList;
import java.util.List;
@ -188,22 +189,21 @@ public class RegionChunk {
* Adds the scenery to the static and dynamic object lists. You never want this unless you are the landscape-parsing
* code.
* @param object The object to add.
* @return The slot it was added into.
*/
public int addStatDyn(Scenery object, int chunkOffsetX, int chunkOffsetY) {
public void addStatDyn(Scenery object, int chunkOffsetX, int chunkOffsetY) {
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery current = statObjects[chunkOffsetX][chunkOffsetY][i];
if (current == null) {
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;
return;
}
if (current.getId() == object.getId()) {
// It's possible that we already have this object. One instance of this was found at 1906, 5082, 0,
// where our cache has more than one copy of the same [Scenery 15349, [1906, 5082, 0], type=22, rot=1]
// object. Since authentically, the engine can only support one of the same type of object per tile,
// if the ID matches we can be confident that this is a duplicate.
return i;
return;
}
}
throw new IllegalStateException("RC addStatDyn insufficient array length for storing object " + object);
@ -249,8 +249,7 @@ public class RegionChunk {
*/
public void addPlayer(Player player) {
players.add(player);
Region region = getCurrentBase().getRegion();
region.flagActive();
currentBase.getRegion().flagActive();
}
/**
@ -282,8 +281,7 @@ public class RegionChunk {
*/
public void removePlayer(Player player) {
players.remove(player);
Region region = getCurrentBase().getRegion();
region.checkInactive();
currentBase.getRegion().checkInactive();
}
/**
@ -318,8 +316,8 @@ public class RegionChunk {
*/
public List<Scenery> assembleObjectList() {
ArrayList<Scenery> list = new ArrayList<>(ARRAY_SIZE);
for (int offsetX = 0; offsetX < 8; offsetX++) {
for (int offsetY = 0; offsetY < 8; offsetY++) {
for (int offsetX = 0; offsetX < SIZE; offsetX++) {
for (int offsetY = 0; offsetY < SIZE; offsetY++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
Scenery object = objects[offsetX][offsetY][i];
if (object != null) {
@ -331,12 +329,6 @@ public class RegionChunk {
return list;
}
public List<Node> getEntities() {
List<Node> entities = new ArrayList<>(assembleObjectList());
entities.addAll(npcs);
return entities;
}
/**
* Gets the players.
* @return The players.
@ -424,7 +416,7 @@ public class RegionChunk {
}
public ArrayList<GroundItem> drawItems(List<GroundItem> items, Player player) {
ArrayList<GroundItem> totalItems = items != null ? new ArrayList<GroundItem>(items) : new ArrayList<GroundItem>();
ArrayList<GroundItem> totalItems = items != null ? new ArrayList<>(items) : new ArrayList<GroundItem>();
if (player.getAttribute("chunkdraw", false)) {
Location l = currentBase;
@ -436,7 +428,7 @@ public class RegionChunk {
else if (x == 0 || x == SIZE - 1)
add = true;
if (add)
totalItems.add(new GroundItem(new Item(13444), l.transform(x, y, 0), player));
totalItems.add(new GroundItem(new Item(Items.ABYSSAL_WHIP_13444), l.transform(x, y, 0), player));
}
}
}
@ -461,7 +453,7 @@ public class RegionChunk {
add = true;
if (add)
totalItems.add(new GroundItem(new Item(13444), l.transform(x,y,0), player));
totalItems.add(new GroundItem(new Item(Items.ABYSSAL_WHIP_13444), l.transform(x,y,0), player));
}
}
@ -502,7 +494,7 @@ public class RegionChunk {
*/
public List<GroundItem> getItems() {
if (items == null) {
items = new ArrayList<GroundItem>();
items = new ArrayList<>();
}
return items;
}
@ -555,7 +547,7 @@ public class RegionChunk {
* Gets the objects index for the given object id and/or scenery type.
* @param x The x-coordinate on the region chunk.
* @param y The y-coordinate on the region chunk.
* @param objectId The object id.
* @param objectId The object id. -1 if any.
* @param type The scenery type.
*/
public int getIndex(int x, int y, int objectId, int type) {
@ -569,7 +561,6 @@ public class RegionChunk {
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
return i;
}
}
@ -654,8 +645,8 @@ public class RegionChunk {
* @param from The region plane to get the new clipping flags from.
*/
public void rebuildClippingFlags(RegionChunk from) {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
// Import the landscape flags from the template chunk to the new chunk
flags.getLandscape()[x][y] = from.getFlags().getLandscape()[x][y];
projectileFlags.getLandscape()[x][y] = from.getProjectileFlags().getLandscape()[x][y];
@ -670,6 +661,15 @@ public class RegionChunk {
}
}
/**
* Rotation consts. Greg loves these :)
*/
private static int NORTH_ROTATION = 0;
private static int EAST_ROTATION = 1;
private static int SOUTH_ROTATION = 2;
private static int WEST_ROTATION = 2;
private static int NUMBER_OF_CARDINAL_ROTATIONS = 4;
/**
* Rotates the chunk.
* @param direction The direction.
@ -692,22 +692,22 @@ public class RegionChunk {
}
}
clear();
switch(direction) {
case NORTH: rotation = 0; break;
case EAST: rotation = 1; break;
case SOUTH: rotation = 2; break;
case WEST: rotation = 3; break;
default: rotation = (direction.toInteger() + (direction.toInteger() % 2 == 0 ? 2 : 0)) % 4;
switch (direction) {
case NORTH: rotation = NORTH_ROTATION; break;
case EAST: rotation = EAST_ROTATION; break;
case SOUTH: rotation = SOUTH_ROTATION; break;
case WEST: rotation = WEST_ROTATION; break;
default: rotation = (direction.toInteger() + (direction.toInteger() % 2 == 0 ? 2 : 0)) % NUMBER_OF_CARDINAL_ROTATIONS;
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 < ARRAY_SIZE; i++) {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
for (int i = 0; i < ARRAY_SIZE; i++) {
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));
Scenery obj = object.transform(object.getId(), (object.getRotation() + rotation) % NUMBER_OF_CARDINAL_ROTATIONS, object.getLocation().transform(pos[0] - x, pos[1] - y, 0));
if (object instanceof Constructed) {
obj = obj.asConstructed();
}
@ -740,12 +740,12 @@ public class RegionChunk {
return new int[] { x, y };
}
if (chunkRotation == 1) {
return new int[] { y, 7 - x - (sizeX - 1) };
return new int[] { y, (SIZE-1) - x - (sizeX - 1) };
}
if (chunkRotation == 2) {
return new int[] { 7 - x - (sizeX - 1), 7 - y - (sizeY - 1) };
return new int[] { (SIZE-1) - x - (sizeX - 1), 7 - y - (sizeY - 1) };
}
return new int[] { 7 - y - (sizeY - 1), x };
return new int[] { (SIZE-1) - y - (sizeY - 1), x };
}
public ChunkFlags getFlags() {

View file

@ -25,7 +25,7 @@ object RegionManager {
@JvmStatic val CLIPPING_FLAGS = HashMap<Int, Array<Int>>()
@JvmStatic val PROJECTILE_FLAGS = HashMap<Int, Array<Int>>()
val LOCK = ReentrantLock()
private val LOCK = ReentrantLock()
/**
* Gets the region for the given region id.
@ -378,12 +378,10 @@ object RegionManager {
}
}
is NPC -> {
if (src != null) {
src.chunk.remove(entity)
}
src?.chunk?.remove(entity)
dst.chunk.add(entity)
}
else -> throw IllegalStateException("Tried to move an Entity that was neither Player nor NPC")
else -> log(this::class.java, Log.ERR, "Tried to move an Entity that was neither Player nor NPC, but $entity of class ${entity.javaClass}! It tried to move from $src to $dst.")
}
}
entity.zoneMonitor.updateLocation(entity.walkingQueue.footPrint)
@ -393,7 +391,6 @@ object RegionManager {
* Gets local entities. You never call the below function directly (it's inlined); instead, you use one of the typed helpers defined directly below this function.
* @param location the location.
* @param distance the distance.
* @param maximum the maximum number of entities to return.
* @return the list.
*/
private inline fun<reified T: Entity> getLocalEntitiesOfType(location: Location, distance: Int): List<T> {
@ -402,10 +399,10 @@ object RegionManager {
for (cy in (location.absChunkY - 8)..(location.absChunkY + 8)) {
val chunk = Location(cx shl 3, cy shl 3, location.z).chunk
if (T::class.java.isAssignableFrom(Player::class.java)) {
entities.addAll(chunk.players as Collection<T>)
entities.addAll(chunk.players.filterIsInstance<T>())
}
if (T::class.java.isAssignableFrom(NPC::class.java)) {
entities.addAll(chunk.npcs as Collection<T>)
entities.addAll(chunk.npcs.filterIsInstance<T>())
}
}
}

View file

@ -15,7 +15,6 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Represents a dynamically constructed region.
*
@ -69,9 +68,9 @@ public final class DynamicRegion extends Region {
super(x, y);
this.regionId = regionId;
// Reset any stale flags at this region's chunk coordinates
for (int chunkX = 0; chunkX < 8; chunkX++) {
for (int chunkY = 0; chunkY < 8; chunkY++) {
for (int cz = 0; cz < 4; cz++) {
for (int chunkX = 0; chunkX < CHUNKS_SIZE; chunkX++) {
for (int chunkY = 0; chunkY < CHUNKS_SIZE; chunkY++) {
for (int cz = 0; cz < Region.PLANES; cz++) {
RegionChunk chunk = chunks[chunkX][chunkY][cz];
for (int chunkOffsetX = 0; chunkOffsetX < 8; chunkOffsetX++) {
for (int chunkOffsetY = 0; chunkOffsetY < 8; chunkOffsetY++) {
@ -98,7 +97,7 @@ public final class DynamicRegion extends Region {
public static DynamicRegion create(int regionId) {
int x = (regionId >> 8) << 6;
int y = (regionId & 0xFF) << 6;
return create(new ZoneBorders(x, y, x + 64, y + 64))[0];
return create(new ZoneBorders(x, y, x + SIZE, y + SIZE))[0];
}
/**
@ -112,7 +111,7 @@ public final class DynamicRegion extends Region {
int y = (regionOne & 0xFF) << 6;
int x1 = (regionTwo >> 8) << 6;
int y1 = (regionTwo & 0xFF) << 6;
return create(new ZoneBorders(x, y, x1 + 64, y1 + 64))[0];
return create(new ZoneBorders(x, y, x1 + SIZE, y1 + SIZE))[0];
}
/**
@ -123,7 +122,6 @@ public final class DynamicRegion extends Region {
public static DynamicRegion[] create(ZoneBorders copy) {
int baseX = copy.getSouthWestX() >> 6;
int baseY = copy.getSouthWestY() >> 6;
ZoneBorders border = findZoneBorders((copy.getNorthEastX() - copy.getSouthWestX()) >> 3, (copy.getNorthEastY() - copy.getSouthWestY()) >> 3);
RESERVED_AREAS.add(border);
Location l = Location.create(border.getSouthWestX(), border.getSouthWestY(), 0);
@ -139,7 +137,7 @@ public final class DynamicRegion extends Region {
}
}
for (Region r : regions) {
for (int z = 0; z < 4; z++) {
for (int z = 0; z < PLANES; z++) {
for (Player player : r.assemblePlayerList(z)) {
if (player != null) {
player.updateSceneGraph(false);
@ -174,11 +172,11 @@ public final class DynamicRegion extends Region {
int count = 0;
int width = (sizeX >> 3) << 6;
int height = (sizeY >> 3) << 6;
if (width < 64) {
width = 64;
if (width < SIZE) {
width = SIZE;
}
if (height < 64) {
height = 64;
if (height < SIZE) {
height = SIZE;
}
while (true) {
int endX = x + width;
@ -195,10 +193,10 @@ public final class DynamicRegion extends Region {
return new ZoneBorders(x, y, endX, endY);
}
if (++count % 15 == 0) {
y += 64;
y += SIZE;
x = 0;
} else {
x += 64;
x += SIZE;
}
}
}
@ -219,7 +217,7 @@ public final class DynamicRegion extends Region {
for (int offsetY = 0; offsetY < 8; offsetY++) {
int x = regionX + (offsetX << 3);
int y = regionY + (offsetY << 3);
for (int plane = 0; plane < 4; plane++) {
for (int plane = 0; plane < PLANES; plane++) {
RegionChunk c = base.getChunks()[offsetX][offsetY][plane];
if (c == null) {
c = new RegionChunk(Location.create(0, 0, 0), 0);
@ -319,9 +317,9 @@ public final class DynamicRegion extends Region {
if (!super.flagInactive(force)) {
return false;
}
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < CHUNKS_SIZE; x++) {
for (int y = 0; y < CHUNKS_SIZE; y++) {
for (int z = 0; z < PLANES; z++) {
RegionChunk chunk = chunks[x][y][z];
for (NPC npc : chunk.getNpcs()) {
npc.clear();

View file

@ -23,8 +23,8 @@ public final class MapscapeParser {
}
// The cache is packed by region, so we need to parse it along the local-x/y coordinates
for (int z = 0; z < 4; z++) {
for (int localX = 0; localX < 64; localX++) {
for (int localY = 0; localY < 64; localY++) {
for (int localX = 0; localX < Region.SIZE; localX++) {
for (int localY = 0; localY < Region.SIZE; localY++) {
int chunkX = localX >> 3;
int chunkY = localY >> 3;
int chunkOffsetX = localX & 7;
@ -33,7 +33,7 @@ public final class MapscapeParser {
boolean[][] landscape;
if (chunk == null) {
// Still need to consume the buffer data even for null chunks
landscape = new boolean[8][8];
landscape = new boolean[RegionChunk.SIZE][RegionChunk.SIZE];
} else {
landscape = chunk.getFlags().getLandscape();
}
@ -71,15 +71,15 @@ public final class MapscapeParser {
* @param mapscape The mapscape.
*/
public static void clipMapscape(Region r, RegionChunk[][][] chunks, byte[][][] mapscape) {
for (int z = 0; z < 4; z++) {
for (int chunkX = 0; chunkX < 8; chunkX++) {
for (int chunkY = 0; chunkY < 8; chunkY++) {
for (int z = 0; z < Region.PLANES; z++) {
for (int chunkX = 0; chunkX < RegionChunk.SIZE; chunkX++) {
for (int chunkY = 0; chunkY < RegionChunk.SIZE; chunkY++) {
RegionChunk chunk = chunks[chunkX][chunkY][z];
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < RegionChunk.SIZE; x++) {
for (int y = 0; y < RegionChunk.SIZE; y++) {
chunk.getFlags().flagEmptyTile(x, y);
int chunkOffsetX = chunkX*8 + x;
int chunkOffsetY = chunkY*8 + y;
int chunkOffsetX = chunkX*RegionChunk.SIZE + x;
int chunkOffsetY = chunkY*RegionChunk.SIZE + y;
if ((mapscape[z][chunkOffsetX][chunkOffsetY] & 0x1) == 1) {
int plane = z;
if ((mapscape[1][chunkOffsetX][chunkOffsetY] & 0x2) == 2) {

View file

@ -64,11 +64,6 @@ public class ModeratorZone extends MapZone {
return true;
}
@Override
public boolean interact(Entity entity, Node target, Option option) {
return super.interact(entity, target, option);
}
@Override
public void configure() {
register(new ZoneBorders(2840, 5204, 2853, 5224));

View file

@ -1,7 +1,22 @@
package core.game.world.update
import core.game.world.map.RegionChunk
import java.util.Collections
import java.util.IdentityHashMap
object ChunkUpdateTracker : ChunkDirtyListener {
private val dirty = Collections.newSetFromMap(IdentityHashMap<RegionChunk, Boolean>())
override fun onFlagged(chunk: RegionChunk) {
dirty.add(chunk)
}
/** Resets flags on exactly the chunks flagged this tick. Must run after all per-player flushes. */
fun resetAll() {
dirty.forEach { it.resetUpdateFlags() }
dirty.clear()
}
}
fun interface ChunkDirtyListener {
fun onFlagged(chunk: RegionChunk)
}
}

View file

@ -1,19 +0,0 @@
package core.game.world.update
import core.game.world.map.RegionChunk
import java.util.Collections
import java.util.IdentityHashMap
object ChunkUpdateTracker : ChunkDirtyListener {
private val dirty = Collections.newSetFromMap(IdentityHashMap<RegionChunk, Boolean>())
override fun onFlagged(chunk: RegionChunk) {
dirty.add(chunk)
}
/** Resets flags on exactly the chunks flagged this tick. Must run after all per-player flushes. */
fun resetAll() {
dirty.forEach { it.resetUpdateFlags() }
dirty.clear()
}
}

View file

@ -3,7 +3,6 @@ package core.game.world.update
import core.game.node.entity.player.Player
import core.game.world.map.Location
import core.game.world.map.RegionChunk
import core.game.world.map.build.DynamicRegion
import core.net.packet.PacketRepository
import core.net.packet.context.ClearChunkContext
import core.net.packet.out.ClearRegionChunk
@ -14,7 +13,8 @@ import java.util.*
* @author Emperor
*/
object MapChunkRenderer {
const val buildAreaDepth = 6
const val BUILD_AREA_DEPTH = 6
const val BUILD_AREA_SIZE = 2*BUILD_AREA_DEPTH + 1
/**
* Sends the map chunk rendering packet.
@ -23,8 +23,8 @@ object MapChunkRenderer {
@JvmStatic
fun render(player: Player) {
val last = player.playerFlags.lastViewport
val updated: MutableList<RegionChunk> = ArrayList()
val current: MutableList<MutableList<RegionChunk>> = ArrayList()
val updated: ArrayList<RegionChunk> = ArrayList()
val current: ArrayList<MutableList<RegionChunk>> = ArrayList()
// Center the build area on the loaded scene anchor (lastSceneGraph), NOT the live player position.
// The client's 13x13 build area is anchored at lastSceneGraph; addressing chunks relative to it keeps
@ -33,9 +33,9 @@ object MapChunkRenderer {
// pushes the viewport edge to drift+6 chunks from the anchor, off-window, so edge items never render.
val anchor = player.playerFlags.lastSceneGraph ?: player.location
val center = Location.create(anchor.x, anchor.y, player.location.z)
for (dcx in -buildAreaDepth..+buildAreaDepth) {
for (dcx in -BUILD_AREA_DEPTH..BUILD_AREA_DEPTH) {
val addX = ArrayList<RegionChunk>()
for (dcy in -buildAreaDepth..+buildAreaDepth) {
for (dcy in -BUILD_AREA_DEPTH..BUILD_AREA_DEPTH) {
val newloc = center.transform(dcx * 8, dcy * 8, 0)
addX.add(newloc.chunk)
}

View file

@ -6,6 +6,7 @@ import core.game.world.map.RegionManager
import core.net.packet.IoBuffer
import core.net.packet.PacketHeader
import core.game.world.GameWorld
import core.game.world.map.MapDistance
import java.nio.ByteBuffer
import java.util.*
@ -30,7 +31,7 @@ object NPCRenderer {
val it: Iterator<NPC> = localNPCs.iterator()
while (it.hasNext()) {
val npc = it.next()
val withinDistance = player.location.withinDistance(npc.location, 15)
val withinDistance = player.location.withinDistance(npc.location, MapDistance.RENDERING.distance)
if (npc.isHidden(player) || !withinDistance || npc.properties.isTeleporting) {
buffer.putBits(1, 1).putBits(2, 3)
toRemove.add(npc)
@ -51,7 +52,7 @@ object NPCRenderer {
}
}
localNPCs.removeAll(toRemove)
for (npc in RegionManager.getLocalNPCs(player.location, 15)) {
for (npc in RegionManager.getLocalNPCs(player.location, MapDistance.RENDERING.distance)) {
if (localNPCs.size >= 255) {
break
}

View file

@ -2,6 +2,7 @@ package core.game.world.update.flag;
import core.game.world.map.Location;
import core.game.world.map.RegionChunk;
import core.game.world.update.MapChunkRenderer;
/**
* A class holding a player's updating flags.
@ -17,7 +18,7 @@ public final class PlayerFlags {
/**
* The last viewport.
*/
private RegionChunk[][] lastViewport = new RegionChunk[13][13];
private RegionChunk[][] lastViewport = new RegionChunk[MapChunkRenderer.BUILD_AREA_SIZE][MapChunkRenderer.BUILD_AREA_SIZE];
/**
* The location the player was standing on when last scene graph update

View file

@ -69,4 +69,4 @@ class SnowStormHolidayRandomNPC : HolidayRandomEventNPC(NPCs.SNOW_6740) {
override fun talkTo(npc: NPC) {
}
}
}

View file

@ -5,6 +5,7 @@ import core.game.world.map.Region;
import core.game.world.map.RegionChunk;
import core.game.world.map.RegionManager;
import core.game.world.map.build.DynamicRegion;
import core.game.world.update.MapChunkRenderer;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.PacketHeader;
@ -29,12 +30,12 @@ public final class BuildDynamicScene implements OutgoingPacket<DynamicSceneConte
buffer.putS(player.getLocation().getZ());
buffer.putLEShortA(player.getLocation().getSceneY());
buffer.setBitAccess();
RegionChunk[][][] chunks = new RegionChunk[13][13][4];
RegionChunk[][][] chunks = new RegionChunk[MapChunkRenderer.BUILD_AREA_SIZE][MapChunkRenderer.BUILD_AREA_SIZE][Region.PLANES];
int baseX = player.getLocation().getRegionX() - 6;
int baseY = player.getLocation().getRegionY() - 6;
for (int x = baseX; x <= player.getLocation().getRegionX() + 6; x++) {
for (int y = baseY; y <= player.getLocation().getRegionY() + 6; y++) {
for (int z = 0; z < 4; z++) {
for (int z = 0; z < Region.PLANES; z++) {
Region r = RegionManager.forId((x >> 3) << 8 | (y >> 3));
if (r instanceof DynamicRegion) {
chunks[x - baseX][y - baseY][z] = r.getChunks()[x - (r.getX() << 3)][y - (r.getY() << 3)][z];
@ -42,9 +43,9 @@ public final class BuildDynamicScene implements OutgoingPacket<DynamicSceneConte
}
}
}
for (int plane = 0; plane < 4; plane++) {
for (int offsetX = 0; offsetX < 13; offsetX++) {
for (int offsetY = 0; offsetY < 13; offsetY++) {
for (int plane = 0; plane < Region.PLANES; plane++) {
for (int offsetX = 0; offsetX < MapChunkRenderer.BUILD_AREA_SIZE; offsetX++) {
for (int offsetY = 0; offsetY < MapChunkRenderer.BUILD_AREA_SIZE; offsetY++) {
RegionChunk c = chunks[offsetX][offsetY][plane];
if (c == null || c.getBase().getX() < 0 || c.getBase().getY() < 0) {
buffer.putBits(1, 0);

View file

@ -11,7 +11,7 @@ import core.net.packet.context.ClearChunkContext;
* @author Emperor
*/
public final class ClearRegionChunk implements OutgoingPacket<ClearChunkContext> {
static final int buildAreaDepthInTiles = (2*MapChunkRenderer.buildAreaDepth+1) * 8;
static final int buildAreaDepthInTiles = (2*MapChunkRenderer.BUILD_AREA_DEPTH +1) * 8;
@Override
public void send(ClearChunkContext context) {

View file

@ -242,4 +242,4 @@ class BlastFurnaceAreaTests {
BlastFurnaceListeners().defineListeners()
}
}
}
}