package net.scapeemulator.game.update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.scapeemulator.game.model.Position;
import net.scapeemulator.game.model.World;
import net.scapeemulator.game.model.npc.NPC;
import net.scapeemulator.game.model.player.Player;
import net.scapeemulator.game.model.player.skills.prayer.Prayer;
import net.scapeemulator.game.msg.impl.NpcUpdateMessage;
import net.scapeemulator.game.msg.impl.PlayerUpdateMessage;
import net.scapeemulator.game.msg.impl.RegionChangeMessage;
import net.scapeemulator.game.msg.impl.RegionConstructMessage;
import net.scapeemulator.game.msg.impl.ResetMinimapFlagMessage;
public final class PlayerUpdater {
private final World world;
public PlayerUpdater(World world) {
this.world = world;
}
public void tick() {
for (Player player : world.getPlayers()) {
if (player.getSession() != null) {
preprocess(player);
}
}
for (NPC npc : world.getNpcs())
preprocess(npc);
for (Player player : world.getPlayers()) {
if (player.getSession() != null) {
updatePlayers(player);
updateNpcs(player);
}
}
for (Player player : world.getPlayers())
if (player.getSession() != null) {
postprocess(player);
}
for (NPC npc : world.getNpcs())
postprocess(npc);
}
private void preprocess(Player player) {
if (player.getWalkingQueue().isMinimapFlagReset())
player.send(new ResetMinimapFlagMessage());
if (player.isTeleporting()) {
player.setUpdateModelLists(true);
}
if (isRegionChangeRequired(player)) {
Position position = player.getPosition();
player.setLastKnownRegion(position);
// TODO find a better way to do this?
if (player.getConstructedRegion() != null) {
player.send(new RegionConstructMessage(position, player.getConstructedRegion()));
player.setClipped(false);
} else {
player.send(new RegionChangeMessage(position));
player.setClipped(true);
}
}
player.getTimers().tick();
player.getFarms().tick();
player.getPrayers().tick();
player.getSkillSet().tick(player.getHealthRegen(), player.getPrayers().prayerActive(Prayer.RESTORE) ? 2 : 1);
player.getWalkingQueue().tick();
player.getCombatHandler().tick();
player.getHits().tick();
}
private void preprocess(NPC npc) {
/* Tick the NPC to update all the...things that need updating */
npc.tick();
if (npc.getDefinition().isAttackable()) {
npc.getSkillSet().tick(npc.getHealthRegen(), 2);
npc.getCombatHandler().tick();
npc.getHits().tick();
}
npc.getWalkingQueue().tick();
}
private void updatePlayers(Player player) {
Position lastKnownRegion = player.getLastKnownRegion();
Position position = player.getPosition();
int[] tickets = player.getAppearanceTickets();
PlayerDescriptor selfDescriptor;
if (player.isTeleporting())
selfDescriptor = new TeleportPlayerDescriptor(player, tickets);
else
selfDescriptor = PlayerDescriptor.create(player, tickets);
List<PlayerDescriptor> descriptors = new ArrayList<>();
List<Player> localPlayers = player.getLocalPlayers();
int localPlayerCount = localPlayers.size();
for (Iterator<Player> it = localPlayers.iterator(); it.hasNext();) {
Player p = it.next();
if (!p.isActive() || p.isTeleporting() || !position.isWithinDistance(p.getPosition())
|| position.getHeight() != p.getPosition().getHeight()) {
it.remove();
descriptors.add(new RemovePlayerDescriptor(p, tickets));
} else {
descriptors.add(PlayerDescriptor.create(p, tickets));
}
}
for (Player p : world.getPlayers()) {
if (localPlayers.size() >= 255)
break;
if (p != player && position.getHeight() == p.getPosition().getHeight() && position.isWithinDistance(p.getPosition())
&& !localPlayers.contains(p)) {
localPlayers.add(p);
descriptors.add(new AddPlayerDescriptor(p, tickets));
}
}
player.send(new PlayerUpdateMessage(lastKnownRegion, position, localPlayerCount, selfDescriptor, descriptors));
}
private void updateNpcs(Player player) {
Position lastKnownRegion = player.getLastKnownRegion();
Position position = player.getPosition();
List<NpcDescriptor> descriptors = new ArrayList<>();
List<NPC> localNpcs = player.getLocalNpcs();
int localNpcCount = localNpcs.size();
for (Iterator<NPC> it = localNpcs.iterator(); it.hasNext();) {
NPC n = it.next();
if (!n.isActive() || n.isTeleporting() || n.isHidden() || !position.isWithinDistance(n.getPosition())) {
it.remove();
descriptors.add(new RemoveNpcDescriptor(n));
} else {
descriptors.add(NpcDescriptor.create(n));
}
}
for (NPC n : world.getNpcs()) {
if (localNpcs.size() >= 255)
break;
if (position.isWithinDistance(n.getPosition()) && !localNpcs.contains(n) && !n.isHidden()) {
localNpcs.add(n);
descriptors.add(new AddNpcDescriptor(n));
}
}
player.send(new NpcUpdateMessage(lastKnownRegion, position, localNpcCount, descriptors));
}
private void postprocess(Player player) {
if (player.getUpdateModelLists()) {
player.refreshGroundItems();
player.refreshGroundObjects();
}
player.reset();
}
private void postprocess(NPC npc) {
npc.reset();
}
private boolean isRegionChangeRequired(Player player) {
if (player.getConstructedRegion() != null) {
return true;
}
Position lastKnownRegion = player.getLastKnownRegion();
Position position = player.getPosition();
int deltaX = position.getLocalX(lastKnownRegion.getRegionX());
int deltaY = position.getLocalY(lastKnownRegion.getRegionY());
return deltaX < 16 || deltaX >= 88 || deltaY < 16 || deltaY >= 88;
}
}