Rewrote all the logic, encoding, etc for player/npc rendering masks (Animations, force movements, etc.)

Groundwork for modular 578 support has now been laid (at least in regards to render masks)
Introduced an improved and more accurate API for using force movement (the old API is still around, all of its methods marked as deprecated)
Converted a couple example cases to using the new force movement API just as a proof of concept
Added a method to tick/time conversion methods to convert between client cycles and ticks
Added all authentically-named buffer read/write methods to the buffer class
This commit is contained in:
Ceikry 2023-05-25 14:58:40 +00:00 committed by Ryan
parent 9329d9d008
commit 2cb8d82648
58 changed files with 963 additions and 1259 deletions

View file

@ -9,7 +9,7 @@ import core.game.node.item.Item
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.map.RegionManager import core.game.world.map.RegionManager
import core.game.world.update.flag.context.ChatMessage import core.game.world.update.flag.context.ChatMessage
import core.game.world.update.flag.player.ChatFlag import core.game.world.update.flag.*
import core.integrations.discord.Discord import core.integrations.discord.Discord
import org.json.simple.JSONArray import org.json.simple.JSONArray
import org.json.simple.JSONObject import org.json.simple.JSONObject
@ -132,14 +132,14 @@ class DoublingMoney : Script() {
val message = arrayOf("Doubling Money", "Doubling Money", "Doubling Money!", "Doubling moneyy").random() val message = arrayOf("Doubling Money", "Doubling Money", "Doubling Money!", "Doubling moneyy").random()
val messageEffect = arrayOf(0, 256).random() val messageEffect = arrayOf(0, 256).random()
val ctx = ChatMessage(bot, message, messageEffect, message.length) val ctx = ChatMessage(bot, message, messageEffect, message.length)
bot.updateMasks.register(ChatFlag(ctx)) bot.updateMasks.register(EntityFlag.Chat, ctx)
sleepTime = 8 sleepTime = 8
} }
Effort.VERY_HIGH -> { Effort.VERY_HIGH -> {
val message = arrayOf("Doubling money!", "Doubling money").random() val message = arrayOf("Doubling money!", "Doubling money").random()
val messageEffect = arrayOf(771, 2818, 2562, 768, 512, 2304, 2560, 769, 1792).random() val messageEffect = arrayOf(771, 2818, 2562, 768, 512, 2304, 2560, 769, 1792).random()
val ctx = ChatMessage(bot, message, messageEffect, message.length) val ctx = ChatMessage(bot, message, messageEffect, message.length)
bot.updateMasks.register(ChatFlag(ctx)) bot.updateMasks.register(EntityFlag.Chat, ctx)
sleepTime = 9 sleepTime = 9
} }
} }
@ -198,7 +198,7 @@ class DoublingMoney : Script() {
if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && coinsFromBot > 0 && effort == Effort.VERY_HIGH) { if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && coinsFromBot > 0 && effort == Effort.VERY_HIGH) {
val message = "Payed ${(if (coinsFromBot < 1000) "${coinsFromBot}gp" else "${coinsFromBot / 1000}k")}" val message = "Payed ${(if (coinsFromBot < 1000) "${coinsFromBot}gp" else "${coinsFromBot / 1000}k")}"
val ctx = ChatMessage(bot, message, 512, message.length) val ctx = ChatMessage(bot, message, 512, message.length)
bot.updateMasks.register(ChatFlag(ctx)) bot.updateMasks.register(EntityFlag.Chat, ctx)
sleepTime = 7 sleepTime = 7
} }
@ -325,7 +325,7 @@ class DoublingMoney : Script() {
if (effort == Effort.VERY_HIGH) { if (effort == Effort.VERY_HIGH) {
val message = "Received ${(if (coins.amount < 1000) "${coins.amount}gp" else "${coins.amount / 1000}k")}" val message = "Received ${(if (coins.amount < 1000) "${coins.amount}gp" else "${coins.amount / 1000}k")}"
val ctx = ChatMessage(bot, message, 256, message.length) val ctx = ChatMessage(bot, message, 256, message.length)
bot.updateMasks.register(ChatFlag(ctx)) bot.updateMasks.register(EntityFlag.Chat, ctx)
} }
} }

View file

@ -9,7 +9,6 @@ import core.game.system.task.Pulse
import core.game.world.GameWorld import core.game.world.GameWorld
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.player.FaceLocationFlag
import core.plugin.Initializable import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
@ -136,7 +135,7 @@ class BasaltRockShortcut : AgilityShortcut {
player.sendMessage(noJump) player.sendMessage(noJump)
} else { } else {
player.lock(3) player.lock(3)
player.faceLocation(FaceLocationFlag.getFaceLocation(player, Location.create(2518, 3611, 0))) player.faceLocation(Location.create(2518, 3611, 0))
AgilityHandler.forceWalk(player, -1, Location.create(2516, 3611, 0), Location.create(2518, 3611, 0), Animation.create(769), 20, 0.0, null, 1) AgilityHandler.forceWalk(player, -1, Location.create(2516, 3611, 0), Location.create(2518, 3611, 0), Animation.create(769), 20, 0.0, null, 1)
} }
return true return true
@ -147,7 +146,7 @@ class BasaltRockShortcut : AgilityShortcut {
player.sendMessage(noJump) player.sendMessage(noJump)
} else { } else {
player.lock(3) player.lock(3)
player.faceLocation(FaceLocationFlag.getFaceLocation(player, Location.create(2516, 3611, 0))) player.faceLocation(Location.create(2516, 3611, 0))
AgilityHandler.forceWalk(player, -1, Location.create(2518, 3611, 0), Location.create(2516, 3611, 0), Animation.create(769), 20, 0.0, null, 1) AgilityHandler.forceWalk(player, -1, Location.create(2518, 3611, 0), Location.create(2516, 3611, 0), Animation.create(769), 20, 0.0, null, 1)
} }
return true return true

View file

@ -12,7 +12,6 @@ import core.game.node.scenery.SceneryBuilder;
import core.game.world.GameWorld; import core.game.world.GameWorld;
import core.game.world.map.RegionManager; import core.game.world.map.RegionManager;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.tools.RandomFunction; import core.tools.RandomFunction;
/** /**
@ -129,7 +128,7 @@ public final class FireMakingPulse extends SkillPulse<Item> {
SceneryBuilder.add(object, fire.getLife(), getAsh(player, fire, object)); SceneryBuilder.add(object, fire.getLife(), getAsh(player, fire, object));
GroundItemManager.destroy(groundItem); GroundItemManager.destroy(groundItem);
player.moveStep(); player.moveStep();
player.faceLocation(FaceLocationFlag.getFaceLocation(player, object)); player.faceLocation(object.getFaceLocation(player.getLocation()));
player.getSkills().addExperience(Skills.FIREMAKING,fire.getXp()); player.getSkills().addExperience(Skills.FIREMAKING,fire.getXp());
int playerRegion = player.getViewport().getRegion().getId(); int playerRegion = player.getViewport().getRegion().getId();

View file

@ -4,7 +4,6 @@ import core.cache.def.impl.NPCDefinition;
import core.game.interaction.OptionHandler; import core.game.interaction.OptionHandler;
import core.game.node.Node; import core.game.node.Node;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.plugin.Plugin; import core.plugin.Plugin;
@ -39,7 +38,7 @@ public final class FamiliarNPCOptionPlugin extends OptionHandler {
} }
switch (option) { switch (option) {
case "pick-up": case "pick-up":
player.faceLocation(FaceLocationFlag.getFaceLocation(player, familiar)); player.faceLocation(familiar.getFaceLocation(player.getLocation()));
player.getFamiliarManager().pickup(); player.getFamiliarManager().pickup();
break; break;
case "interact-with": case "interact-with":

View file

@ -12,7 +12,6 @@ import core.game.system.task.Pulse;
import core.game.world.GameWorld; import core.game.world.GameWorld;
import core.game.world.map.RegionManager; import core.game.world.map.RegionManager;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.plugin.Plugin; import core.plugin.Plugin;
import core.plugin.ClassScanner; import core.plugin.ClassScanner;
@ -153,7 +152,7 @@ public class ForgeRegentNPC extends Familiar {
familiar.moveStep(); familiar.moveStep();
GroundItemManager.destroy(ground); GroundItemManager.destroy(ground);
player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10); player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10);
familiar.faceLocation(FaceLocationFlag.getFaceLocation(familiar, object)); familiar.faceLocation(object.getFaceLocation(familiar.getLocation()));
SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object)); SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object));
if (player.getViewport().getRegion().getId() == 10806) { if (player.getViewport().getRegion().getId() == 10806) {
player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9); player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9);

View file

@ -21,7 +21,6 @@ import core.game.world.GameWorld;
import core.game.world.map.RegionManager; import core.game.world.map.RegionManager;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.context.Graphics;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.plugin.Plugin; import core.plugin.Plugin;
import core.plugin.ClassScanner; import core.plugin.ClassScanner;
@ -132,7 +131,7 @@ public class PyreLordNPC extends Familiar {
familiar.moveStep(); familiar.moveStep();
GroundItemManager.destroy(ground); GroundItemManager.destroy(ground);
player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10); player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10);
familiar.faceLocation(FaceLocationFlag.getFaceLocation(familiar, object)); familiar.faceLocation(object.getFaceLocation(familiar.getLocation()));
SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object)); SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object));
if (player.getViewport().getRegion().getId() == 10806) { if (player.getViewport().getRegion().getId() == 10806) {
player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9); player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9);

View file

@ -38,6 +38,7 @@ import core.tools.RandomFunction;
import static core.api.ContentAPIKt.animateScenery; import static core.api.ContentAPIKt.animateScenery;
import static core.api.ContentAPIKt.submitWorldPulse; import static core.api.ContentAPIKt.submitWorldPulse;
import static core.api.ContentAPIKt.animationDuration; import static core.api.ContentAPIKt.animationDuration;
import static core.api.ContentAPIKt.forceMove;
/** /**
* Handles the puro puro activity. * Handles the puro puro activity.
@ -161,13 +162,7 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
player.sendMessage("You use your strength to push through the wheat. It's hard work though."); player.sendMessage("You use your strength to push through the wheat. It's hard work though.");
} }
player.setAttribute("cantMove", true); player.setAttribute("cantMove", true);
GameWorld.getPulser().submit(new Pulse(1) { forceMove(player, player.getLocation(), dest, 0, 265, null, 6595, null);
@Override
public boolean pulse() {
ForceMovement.run(player, player.getLocation(), dest, Animation.create(6594), Animation.create(6594), Direction.getLogicalDirection(player.getLocation(), object.getLocation()), 3, 3);
return true;
}
});
} }
/** /**

View file

@ -85,22 +85,7 @@ class TreeGnomeVillageListeners : InteractionListener {
return@on true return@on true
} }
on(looseRailing, IntType.SCENERY, "squeeze-through"){ player, _ -> on(looseRailing, IntType.SCENERY, "squeeze-through"){ player, _ ->
if(player.location != location(2516,3161,0)) {
squeezeThrough(player) squeezeThrough(player)
} else {
player.pulseManager.run(object : Pulse(0) {
var count = 0
override fun pulse(): Boolean {
when (count) {
0 -> ForceMovement.run(player,player.location,player.location.transform(Direction.WEST,1))
2 -> squeezeThrough(player)
3 -> return true
}
count++
return false
}
})
}
return@on true return@on true
} }
on(crumbledWall, IntType.SCENERY, "climb-over"){ player, _ -> on(crumbledWall, IntType.SCENERY, "climb-over"){ player, _ ->
@ -132,33 +117,16 @@ class TreeGnomeVillageListeners : InteractionListener {
return@on true return@on true
} }
} }
}
fun squeezeThrough(player: Player){ fun squeezeThrough(player: Player){
val squeezeAnim = Animation.create(3844) val squeezeAnim = Animation.create(3844)
if(player.location.y >= 3161) {
AgilityHandler.forceWalk( var dest = if (player.location.y >= 3161)
player, player.location.transform(Direction.SOUTH, 1)
-1, else
player.location, player.location.transform(Direction.NORTH, 1)
player.location.transform(Direction.SOUTH, 1),
squeezeAnim, forceMove(player, player.location, dest, 0, 80, anim = 3844)
5,
0.0,
null
)
} else {
AgilityHandler.forceWalk(
player,
-1,
player.location,
player.location.transform(Direction.NORTH, 1),
squeezeAnim,
5,
0.0,
null
)
}
} }
private class ClimbWall : DialogueFile() { private class ClimbWall : DialogueFile() {
@ -200,3 +168,4 @@ private class ClimbWall : DialogueFile() {
} }
} }
} }
}

View file

@ -9,7 +9,6 @@ import core.game.system.task.Pulse;
import core.game.world.GameWorld; import core.game.world.GameWorld;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.game.world.update.flag.player.FaceLocationFlag;
/** /**
* Represents the dialogue plugin used for mithril seeds. * Represents the dialogue plugin used for mithril seeds.
@ -64,7 +63,7 @@ public final class MithrilSeedsDialogue extends DialoguePlugin {
switch (buttonId) { switch (buttonId) {
case 1: // First option case 1: // First option
player.lock(2); player.lock(2);
player.faceLocation(FaceLocationFlag.getFaceLocation(player, flower)); player.faceLocation(flower.getFaceLocation(player.getLocation()));
player.animate(ANIMATION); player.animate(ANIMATION);
GameWorld.getPulser().submit(new Pulse(2, player, flower) { GameWorld.getPulser().submit(new Pulse(2, player, flower) {
@Override @Override

View file

@ -11,7 +11,6 @@ import core.game.system.task.Pulse;
import core.game.world.GameWorld; import core.game.world.GameWorld;
import core.game.world.map.RegionManager; import core.game.world.map.RegionManager;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.plugin.Plugin; import core.plugin.Plugin;
import core.tools.RandomFunction; import core.tools.RandomFunction;
@ -67,7 +66,7 @@ public final class MithrilSeedsPlugin extends OptionHandler {
player.getPulseManager().run(new Pulse(1, player) { player.getPulseManager().run(new Pulse(1, player) {
@Override @Override
public boolean pulse() { public boolean pulse() {
player.faceLocation(FaceLocationFlag.getFaceLocation(player, object)); player.faceLocation(object.getFaceLocation(player.getLocation()));
player.getDialogueInterpreter().open(1 << 16 | 1, object); player.getDialogueInterpreter().open(1 << 16 | 1, object);
return true; return true;
} }

View file

@ -29,7 +29,6 @@ import core.game.world.map.Location;
import core.game.world.map.RegionManager; import core.game.world.map.RegionManager;
import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBorders;
import core.game.world.map.zone.ZoneRestriction; import core.game.world.map.zone.ZoneRestriction;
import core.game.world.update.flag.player.AppearanceFlag;
import core.tools.RandomFunction; import core.tools.RandomFunction;
/** /**
@ -140,7 +139,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin {
lastVictor.getAchievementDiaryManager().finishTask(lastVictor, DiaryType.KARAMJA, 2, 0); lastVictor.getAchievementDiaryManager().finishTask(lastVictor, DiaryType.KARAMJA, 2, 0);
addTokkul(lastVictor); addTokkul(lastVictor);
lastVictor.getAppearance().setSkullIcon(SKULL_ID); lastVictor.getAppearance().setSkullIcon(SKULL_ID);
lastVictor.getUpdateMasks().register(new AppearanceFlag(lastVictor)); lastVictor.updateAppearance();
lastVictor.getPacketDispatch().sendString("Current Champion: " + getChampionName(), INTERFACE_ID, 0); lastVictor.getPacketDispatch().sendString("Current Champion: " + getChampionName(), INTERFACE_ID, 0);
resetDamagePulse(lastVictor); resetDamagePulse(lastVictor);
} }
@ -158,7 +157,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin {
player.getInteraction().remove(Option._P_ATTACK); player.getInteraction().remove(Option._P_ATTACK);
if (player.getAppearance().getSkullIcon() == SKULL_ID) { if (player.getAppearance().getSkullIcon() == SKULL_ID) {
player.getAppearance().setSkullIcon(-1); player.getAppearance().setSkullIcon(-1);
player.getUpdateMasks().register(new AppearanceFlag(player)); player.updateAppearance();
} }
} }
} }
@ -190,7 +189,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin {
} }
if (lastVictor.getAppearance().getSkullIcon() == SKULL_ID) { if (lastVictor.getAppearance().getSkullIcon() == SKULL_ID) {
player.getAppearance().setSkullIcon(-1); player.getAppearance().setSkullIcon(-1);
player.getUpdateMasks().register(new AppearanceFlag(player)); player.updateAppearance();
} }
} }
@ -238,7 +237,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin {
} }
if (e instanceof Player && (player = (Player) e).getAppearance().getSkullIcon() == SKULL_ID) { if (e instanceof Player && (player = (Player) e).getAppearance().getSkullIcon() == SKULL_ID) {
player.getAppearance().setSkullIcon(-1); player.getAppearance().setSkullIcon(-1);
player.getUpdateMasks().register(new AppearanceFlag(player)); player.updateAppearance();
} }
return super.leave(e, logout); return super.leave(e, logout);
} }

View file

@ -41,8 +41,6 @@ import core.game.world.map.zone.MapZone
import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneBuilder import core.game.world.map.zone.ZoneBuilder
import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag
import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.game.dialogue.DialogueFile import core.game.dialogue.DialogueFile
@ -55,9 +53,7 @@ import content.global.handlers.iface.ge.StockMarket
import content.global.skill.slayer.SlayerManager import content.global.skill.slayer.SlayerManager
import content.data.consumables.* import content.data.consumables.*
import core.game.activity.Cutscene import core.game.activity.Cutscene
import core.game.interaction.Clocks import core.game.interaction.*
import core.game.interaction.QueueStrength
import core.game.interaction.QueuedScript
import core.game.node.entity.player.info.LogType import core.game.node.entity.player.info.LogType
import core.game.node.entity.player.info.PlayerMonitor import core.game.node.entity.player.info.PlayerMonitor
import core.tools.SystemLogger import core.tools.SystemLogger
@ -68,11 +64,13 @@ import core.game.world.GameWorld.Pulser
import core.game.world.map.path.ProjectilePathfinder import core.game.world.map.path.ProjectilePathfinder
import core.game.world.repository.Repository import core.game.world.repository.Repository
import core.game.consumable.* import core.game.consumable.*
import core.tools.Log
import core.tools.tick
import core.ServerConstants import core.ServerConstants
import core.api.utils.Vector import core.api.utils.Vector
import core.tools.*
import core.game.world.update.flag.*
import core.game.world.update.flag.context.*
import java.util.regex.* import java.util.regex.*
import java.io.*
import kotlin.math.* import kotlin.math.*
/** /**
@ -1082,6 +1080,68 @@ fun forceWalk(entity: Entity, dest: Location, type: String) {
path.walk(entity) path.walk(entity)
} }
/**
* Force a player to move from the start location to the dest location
* @param player the player we are moving
* @param start the start location
* @param dest the end location
* @param startArrive the number of client cycles to take moving the player to the start location
* @param destArrive the number of client cycles to take moving the player to the end location from the start location
* @param direction (optional) the direction to face the player during the movement
* @param anim (optional) the animation to use throughout the movement
* @param callback (optional) a callback called when the forced movement completes
* @see NOTE: There are 30 client cycles per second.
*/
fun forceMove (player: Player, start: Location, dest: Location, startArrive: Int, destArrive: Int, dir: Direction? = null, anim: Int = -1, callback: (()->Unit)? = null) {
var direction: Direction
if (dir == null) {
var delta = Location.getDelta(start, dest)
var x = abs(delta.x)
var y = abs(delta.y)
if (x > y)
direction = Direction.getDirection(delta.x, 0)
else
direction = Direction.getDirection(0, delta.y)
} else direction = dir
val startLoc = Location.create(start)
val destLoc = Location.create(dest)
var startArriveTick = getWorldTicks() + cyclesToTicks (startArrive) + 1
var destArriveTick = startArriveTick + cyclesToTicks (destArrive)
var maskSet = false
delayEntity(player, (destArriveTick - getWorldTicks()) + 1)
queueScript (player, 0, QueueStrength.SOFT) { stage: Int ->
if (!finishedMoving(player))
return@queueScript keepRunning(player)
if (!maskSet) {
var ctx = ForceMoveCtx (startLoc, destLoc, startArrive, destArrive, direction)
player.updateMasks.register(EntityFlag.ForceMove, ctx)
maskSet = true
}
var tick = getWorldTicks()
if (tick < startArriveTick) {
return@queueScript keepRunning(player)
} else if (tick < destArriveTick) {
if (animationFinished(player))
animate (player, anim)
return@queueScript keepRunning(player)
} else if (tick >= destArriveTick) {
try {
callback?.invoke()
} catch (e: Exception) {
e.printStackTrace()
}
player.properties.teleportLocation = dest
return@queueScript stopExecuting(player)
}
return@queueScript stopExecuting(player)
}
}
/** /**
* Interrupts a given entity's walking queue * Interrupts a given entity's walking queue
* @param entity the entity to interrupt * @param entity the entity to interrupt
@ -2382,6 +2442,24 @@ fun log(origin: Class<*>, type: Log, message: String) {
SystemLogger.processLogEntry(origin, type, message) SystemLogger.processLogEntry(origin, type, message)
} }
/**
* Logs a message to the server console along with a stack trace leading up to it.
* @param origin simply put (Kotlin) this::class.java or (Java) this.getClass()
* @param type the type of log: Log.FINE (default, visible on VERBOSE), Log.INFO (visible on DETAILED), Log.WARN (visible on CAUTIOUS), Log.ERR (always visible)
* @param message the actual message to log.
*/
fun logWithStack(origin: Class<*>, type: Log, message: String) {
try {
throw Exception(message)
} catch (e: Exception) {
val sw = StringWriter()
val pw = PrintWriter(sw)
e.printStackTrace(pw)
log(origin, type, "$sw")
}
}
/** /**
* Used by content handlers to check if the entity is done moving yet * Used by content handlers to check if the entity is done moving yet
*/ */
@ -2403,7 +2481,7 @@ fun delayScript(entity: Entity, ticks: Int): Boolean {
*/ */
fun delayEntity(entity: Entity, ticks: Int) { fun delayEntity(entity: Entity, ticks: Int) {
entity.scripts.delay = GameWorld.ticks + ticks entity.scripts.delay = GameWorld.ticks + ticks
lock(entity, 5) //TODO: REMOVE WHEN EVERYTHING IMPORTANT USES PROPER QUEUES - THIS IS INCORRECT BEHAVIOR lock(entity, ticks) //TODO: REMOVE WHEN EVERYTHING IMPORTANT USES PROPER QUEUES - THIS IS INCORRECT BEHAVIOR
} }
fun apRange(entity: Entity, apRange: Int) { fun apRange(entity: Entity, apRange: Int) {

View file

@ -26,7 +26,7 @@ import core.game.world.map.path.Pathfinder
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.ChatMessage import core.game.world.update.flag.context.ChatMessage
import core.game.world.update.flag.context.Graphics import core.game.world.update.flag.context.Graphics
import core.game.world.update.flag.player.ChatFlag import core.game.world.update.flag.*
import core.tools.RandomFunction import core.tools.RandomFunction
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -90,7 +90,7 @@ class ScriptAPI(private val bot: Player) {
fun sendChat(message: String) { fun sendChat(message: String) {
bot.sendChat(message) bot.sendChat(message)
bot.updateMasks.register(ChatFlag(ChatMessage(bot, message, 0, 0))) bot.updateMasks.register(EntityFlag.Chat, ChatMessage(bot, message, 0, 0))
} }
/** /**

View file

@ -8,7 +8,6 @@ import core.game.node.entity.combat.equipment.WeaponInterface;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.game.node.entity.skill.Skills; import core.game.node.entity.skill.Skills;
import core.game.node.item.Item; import core.game.node.item.Item;
import core.game.world.update.flag.player.AppearanceFlag;
import core.net.packet.PacketRepository; import core.net.packet.PacketRepository;
import core.net.packet.context.ContainerContext; import core.net.packet.context.ContainerContext;
import core.net.packet.out.ContainerPacket; import core.net.packet.out.ContainerPacket;
@ -306,7 +305,7 @@ public final class EquipmentContainer extends Container {
} }
} }
player.getAppearance().setAnimations(); player.getAppearance().setAnimations();
player.getUpdateMasks().register(new AppearanceFlag(player)); player.updateAppearance();
player.getSettings().updateWeight(); player.getSettings().updateWeight();
updateBonuses(player); updateBonuses(player);
} }

View file

@ -9,7 +9,6 @@ import core.game.node.entity.player.Player;
import core.game.node.item.Item; import core.game.node.item.Item;
import core.game.node.scenery.Scenery; import core.game.node.scenery.Scenery;
import core.game.system.task.Pulse; import core.game.system.task.Pulse;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.net.packet.PacketRepository; import core.net.packet.PacketRepository;
import core.net.packet.context.InteractionOptionContext; import core.net.packet.context.InteractionOptionContext;
import core.net.packet.out.InteractionOption; import core.net.packet.out.InteractionOption;
@ -145,7 +144,7 @@ public class InteractPlugin {
@Override @Override
public boolean pulse() { public boolean pulse() {
try { try {
player.faceLocation(FaceLocationFlag.getFaceLocation(player, node)); player.faceLocation(node.getFaceLocation(player.getLocation()));
if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) {
return true; return true;
} }
@ -181,7 +180,7 @@ public class InteractPlugin {
@Override @Override
public boolean pulse() { public boolean pulse() {
try { try {
player.faceLocation(FaceLocationFlag.getFaceLocation(player, node)); player.faceLocation(node.getFaceLocation(player.getLocation()));
if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) {
return true; return true;
} }

View file

@ -12,7 +12,6 @@ import core.tools.Log;
import core.tools.SystemLogger; import core.tools.SystemLogger;
import core.game.system.task.Pulse; import core.game.system.task.Pulse;
import core.game.world.map.Location; import core.game.world.map.Location;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.plugin.Plugin; import core.plugin.Plugin;
import java.util.ArrayList; import java.util.ArrayList;
@ -175,7 +174,7 @@ public abstract class UseWithHandler implements Plugin<Object> {
@Override @Override
public boolean pulse() { public boolean pulse() {
event.getPlayer().dispatch(new UseWithEvent(event.getUsed().getId(), event.getUsedWith().getId())); event.getPlayer().dispatch(new UseWithEvent(event.getUsed().getId(), event.getUsedWith().getId()));
event.getPlayer().faceLocation(FaceLocationFlag.getFaceLocation(event.getPlayer(), event.getUsedWith())); event.getPlayer().faceLocation(event.getUsedWith().getFaceLocation(event.getPlayer().getLocation()));
boolean handled = false; boolean handled = false;
Item used = (Item) event.getUsed(); Item used = (Item) event.getUsed();
for (UseWithHandler h : handlers) { for (UseWithHandler h : handlers) {

View file

@ -27,6 +27,7 @@ import core.game.world.map.path.Pathfinder;
import core.game.world.map.zone.ZoneMonitor; import core.game.world.map.zone.ZoneMonitor;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.context.Graphics;
import core.game.world.update.flag.*;
import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.CombatSwingHandler;
import core.game.world.update.UpdateMasks; import core.game.world.update.UpdateMasks;
@ -48,7 +49,7 @@ public abstract class Entity extends Node {
/** /**
* The entity's update masks. * The entity's update masks.
*/ */
private final UpdateMasks updateMasks = new UpdateMasks(); private final UpdateMasks updateMasks = new UpdateMasks(this);
/** /**
* The walking queue. * The walking queue.
@ -631,21 +632,39 @@ public abstract class Entity extends Node {
* @param entity The entity to face. * @param entity The entity to face.
* @return {@code True} if succesful. * @return {@code True} if succesful.
*/ */
public abstract boolean face(Entity entity); public boolean face(Entity entity) {
if (entity == null) {
int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceEntity);
if (getUpdateMasks().unregisterSynced(ordinal)) {
return getUpdateMasks().register(EntityFlag.FaceEntity, null);
}
return true;
}
return getUpdateMasks().register(EntityFlag.FaceEntity, entity, true);
}
/** /**
* Registers a new face location update flag to the update masks. * Registers a new face location update flag to the update masks.
* @param location The location to face. * @param location The location to face.
* @return {@code True} if succesful. * @return {@code True} if succesful.
*/ */
public abstract boolean faceLocation(Location location); public boolean faceLocation(Location location) {
if (location == null) {
int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceLocation);
getUpdateMasks().unregisterSynced(ordinal);
return true;
}
return getUpdateMasks().register(EntityFlag.FaceLocation, location, true);
}
/** /**
* Registers a new force chat update flag to the update masks. * Registers a new force chat update flag to the update masks.
* @param string The string. * @param string The string.
* @return {@code True} if succesful. * @return {@code True} if succesful.
*/ */
public abstract boolean sendChat(String string); public boolean sendChat(String string) {
return getUpdateMasks().register(EntityFlag.ForceChat, string);
}
/** /**
* Gets the current combat swing handler. * Gets the current combat swing handler.

View file

@ -6,10 +6,7 @@ import core.game.node.entity.npc.NPC;
import core.game.world.GameWorld; import core.game.world.GameWorld;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.context.Graphics;
import core.game.world.update.flag.npc.NPCAnimation; import core.game.world.update.flag.EntityFlag;
import core.game.world.update.flag.npc.NPCGraphic;
import core.game.world.update.flag.player.AnimationFlag;
import core.game.world.update.flag.player.GraphicFlag;
/** /**
* Handles the animating of an Entity. * Handles the animating of an Entity.
@ -128,12 +125,12 @@ public final class Animator {
ticks = 0; ticks = 0;
} }
entity.clocks[Clocks.getANIMATION_END()] = ticks; entity.clocks[Clocks.getANIMATION_END()] = ticks;
entity.getUpdateMasks().register(entity instanceof NPC ? new NPCAnimation(animation) : new AnimationFlag(animation)); entity.getUpdateMasks().register(EntityFlag.Animate, animation);
priority = animation.getPriority(); priority = animation.getPriority();
} }
if (graphic != null) { if (graphic != null) {
this.graphics = graphic; this.graphics = graphic;
entity.getUpdateMasks().register(entity instanceof NPC ? new NPCGraphic(graphic) : new GraphicFlag(graphic)); entity.getUpdateMasks().register(EntityFlag.SpotAnim, graphic);
} }
return true; return true;
} }

View file

@ -6,8 +6,8 @@ import core.game.system.task.Pulse;
import core.game.world.GameWorld; import core.game.world.GameWorld;
import core.game.world.map.Direction; import core.game.world.map.Direction;
import core.game.world.map.Location; import core.game.world.map.Location;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.*;
import core.game.world.update.flag.player.ForceMovementFlag; import core.game.world.update.flag.*;
/** /**
* The force movement handler. * The force movement handler.
@ -91,8 +91,9 @@ public class ForceMovement extends Pulse {
* @param commenceSpeed The commencing speed. * @param commenceSpeed The commencing speed.
* @param pathSpeed The path speed. * @param pathSpeed The path speed.
* @param unlockAfter Whether to unlock the entity after the ForceMovement completes * @param unlockAfter Whether to unlock the entity after the ForceMovement completes
* * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public ForceMovement(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) { public ForceMovement(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) {
super(1, e); super(1, e);
this.entity = e; this.entity = e;
@ -113,11 +114,17 @@ public class ForceMovement extends Pulse {
* @param end the destination. * @param end the destination.
* @param animation the animation. * @param animation the animation.
* @param speed The path speed. * @param speed The path speed.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public ForceMovement(Entity e, Location start, Location end, Animation animation, int speed) { public ForceMovement(Entity e, Location start, Location end, Animation animation, int speed) {
this(e, start, end, WALK_ANIMATION, animation, direction(start, end), WALKING_SPEED, speed, true); this(e, start, end, WALK_ANIMATION, animation, direction(start, end), WALKING_SPEED, speed, true);
} }
/**
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/
@Deprecated
public ForceMovement(Entity e, Location destination, int startSpeed, int animSpeed){ public ForceMovement(Entity e, Location destination, int startSpeed, int animSpeed){
this(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),startSpeed,animSpeed, true); this(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),startSpeed,animSpeed, true);
} }
@ -128,7 +135,9 @@ public class ForceMovement extends Pulse {
* @param start the start location. * @param start the start location.
* @param destination the destination. * @param destination the destination.
* @param animation the animation. * @param animation the animation.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public ForceMovement(Entity e, Location start, Location destination, Animation animation) { public ForceMovement(Entity e, Location start, Location destination, Animation animation) {
this(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); this(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true);
} }
@ -138,7 +147,9 @@ public class ForceMovement extends Pulse {
* @param start the start loc. * @param start the start loc.
* @param destination the destination. * @param destination the destination.
* @param animation the animation. * @param animation the animation.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public ForceMovement(Location start, Location destination, Animation animation) { public ForceMovement(Location start, Location destination, Animation animation) {
this(null, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); this(null, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true);
} }
@ -148,7 +159,9 @@ public class ForceMovement extends Pulse {
* @param e The entity. * @param e The entity.
* @param destination The destination location. * @param destination The destination location.
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location destination) { public static ForceMovement run(Entity e, Location destination) {
return run(e, e.getLocation(), destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true); return run(e, e.getLocation(), destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true);
} }
@ -159,7 +172,9 @@ public class ForceMovement extends Pulse {
* @param start The start location. * @param start The start location.
* @param destination The destination location. * @param destination The destination location.
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination) { public static ForceMovement run(Entity e, Location start, Location destination) {
return run(e, start, destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true); return run(e, start, destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true);
} }
@ -171,7 +186,9 @@ public class ForceMovement extends Pulse {
* @param destination The destination location. * @param destination The destination location.
* @param animation The animation. * @param animation The animation.
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination, Animation animation) { public static ForceMovement run(Entity e, Location start, Location destination, Animation animation) {
return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true);
} }
@ -184,7 +201,9 @@ public class ForceMovement extends Pulse {
* @param animation The animation. * @param animation The animation.
* @param speed The path speed. * @param speed The path speed.
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination, Animation animation, int speed) { public static ForceMovement run(Entity e, Location start, Location destination, Animation animation, int speed) {
return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, speed, true); return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, speed, true);
} }
@ -196,7 +215,9 @@ public class ForceMovement extends Pulse {
* @param destination The destination location. * @param destination The destination location.
* @param animation The animation. * @param animation The animation.
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation) { public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation) {
return run(e, start, destination, startAnim, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); return run(e, start, destination, startAnim, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true);
} }
@ -209,7 +230,9 @@ public class ForceMovement extends Pulse {
* @param animation The animation. * @param animation The animation.
* @param direction The direction. * @param direction The direction.
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction) { public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction) {
return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, WALKING_SPEED, true); return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, WALKING_SPEED, true);
} }
@ -223,11 +246,17 @@ public class ForceMovement extends Pulse {
* @param direction The direction. * @param direction The direction.
* @param pathSpeed The speed (in ticks). * @param pathSpeed The speed (in ticks).
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int pathSpeed) { public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int pathSpeed) {
return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, pathSpeed, true); return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, pathSpeed, true);
} }
/**
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed) { public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed) {
return run(e, start, destination, startAnim, animation, direction, commenceSpeed, pathSpeed, true); return run(e, start, destination, startAnim, animation, direction, commenceSpeed, pathSpeed, true);
} }
@ -240,7 +269,9 @@ public class ForceMovement extends Pulse {
* @param animation The animation. * @param animation The animation.
* @param direction The direction. * @param direction The direction.
* @return The created ForceMovement object. * @return The created ForceMovement object.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) { public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) {
if (startAnim != null) { if (startAnim != null) {
startAnim.setPriority(Animator.Priority.VERY_HIGH); startAnim.setPriority(Animator.Priority.VERY_HIGH);
@ -254,7 +285,10 @@ public class ForceMovement extends Pulse {
GameWorld.getPulser().submit(fm); GameWorld.getPulser().submit(fm);
return fm; return fm;
} }
/*
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/
@Deprecated
public static ForceMovement run(Entity e, Location destination, int commenceSpeed, int pathSpeed){ public static ForceMovement run(Entity e, Location destination, int commenceSpeed, int pathSpeed){
return run(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),commenceSpeed,pathSpeed, true); return run(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),commenceSpeed,pathSpeed, true);
} }
@ -262,7 +296,9 @@ public class ForceMovement extends Pulse {
/** /**
* Method used to run the force movement. * Method used to run the force movement.
* @param e the entity. * @param e the entity.
* @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please.
*/ */
@Deprecated
public void run(final Entity e, final int speed) { public void run(final Entity e, final int speed) {
this.entity = e; this.entity = e;
int commence = (int) start.getDistance(e.getLocation()); int commence = (int) start.getDistance(e.getLocation());
@ -321,7 +357,7 @@ public class ForceMovement extends Pulse {
} }
int ticks = 1 + commenceSpeed + pathSpeed; int ticks = 1 + commenceSpeed + pathSpeed;
entity.getImpactHandler().setDisabledTicks(ticks); entity.getImpactHandler().setDisabledTicks(ticks);
entity.getUpdateMasks().register(new ForceMovementFlag(this)); entity.getUpdateMasks().register(EntityFlag.ForceMove, new ForceMoveCtx(start, destination, commenceSpeed * 30, pathSpeed * 30, direction));
if(entity instanceof Player) { if(entity instanceof Player) {
entity.getWalkingQueue().updateRegion(destination, false); entity.getWalkingQueue().updateRegion(destination, false);
} }

View file

@ -26,10 +26,7 @@ import core.game.world.map.build.DynamicRegion;
import core.game.world.map.path.Pathfinder; import core.game.world.map.path.Pathfinder;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.context.Graphics;
import core.game.world.update.flag.npc.NPCFaceEntity; import core.game.world.update.flag.*;
import core.game.world.update.flag.npc.NPCFaceLocation;
import core.game.world.update.flag.npc.NPCForceChat;
import core.game.world.update.flag.npc.NPCSwitchId;
import core.tools.RandomFunction; import core.tools.RandomFunction;
import core.api.utils.GlobalKillCounter; import core.api.utils.GlobalKillCounter;
import core.api.utils.Vector; import core.api.utils.Vector;
@ -579,31 +576,6 @@ public class NPC extends Entity {
super.reset(); super.reset();
} }
@Override
public boolean face(Entity entity) {
if (entity == null) {
if (getUpdateMasks().unregisterSynced(NPCFaceEntity.getOrdinal())) {
return getUpdateMasks().register(new NPCFaceEntity(null));
}
return true;
}
return getUpdateMasks().register(new NPCFaceEntity(entity), true);
}
@Override
public boolean faceLocation(Location location) {
if (location == null) {
getUpdateMasks().unregisterSynced(NPCFaceLocation.getOrdinal());
return true;
}
return getUpdateMasks().register(new NPCFaceLocation(location), true);
}
@Override
public boolean sendChat(String string) {
return getUpdateMasks().register(new NPCForceChat(string));
}
@Override @Override
public CombatSwingHandler getSwingHandler(boolean swing) { public CombatSwingHandler getSwingHandler(boolean swing) {
CombatSwingHandler original = getProperties().getCombatPulse().getStyle().getSwingHandler(); CombatSwingHandler original = getProperties().getCombatPulse().getStyle().getSwingHandler();
@ -703,9 +675,10 @@ public class NPC extends Entity {
configure(); configure();
interactPlugin.setDefault(); interactPlugin.setDefault();
if (id == originalId) { if (id == originalId) {
getUpdateMasks().unregisterSynced(NPCSwitchId.getOrdinal()); int ordinal = EntityFlags.getOrdinal (EFlagType.NPC, EntityFlag.TypeSwap);
getUpdateMasks().unregisterSynced(ordinal);
} }
getUpdateMasks().register(new NPCSwitchId(id), id != originalId); getUpdateMasks().register(EntityFlag.TypeSwap, id, id != originalId);
return this; return this;
} }

View file

@ -43,11 +43,7 @@ import core.game.world.map.build.DynamicRegion;
import core.game.world.map.path.Pathfinder; import core.game.world.map.path.Pathfinder;
import core.game.world.map.zone.ZoneType; import core.game.world.map.zone.ZoneType;
import core.game.world.update.flag.PlayerFlags; import core.game.world.update.flag.PlayerFlags;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.*;
import core.game.world.update.flag.context.Graphics;
import core.game.world.update.flag.player.FaceEntityFlag;
import core.game.world.update.flag.player.FaceLocationFlag;
import core.game.world.update.flag.player.ForceChatFlag;
import core.net.IoSession; import core.net.IoSession;
import core.net.packet.PacketRepository; import core.net.packet.PacketRepository;
import core.net.packet.context.DynamicSceneContext; import core.net.packet.context.DynamicSceneContext;
@ -81,6 +77,7 @@ import core.game.ge.GrandExchangeRecords;
import core.game.ge.GrandExchangeOffer; import core.game.ge.GrandExchangeOffer;
import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.ItemDefinition;
import core.worker.ManagementEvents; import core.worker.ManagementEvents;
import core.game.world.update.flag.context.*;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -549,31 +546,6 @@ public class Player extends Entity {
Arrays.fill(opCounts, (byte) 0); Arrays.fill(opCounts, (byte) 0);
} }
@Override
public boolean face(Entity entity) {
if (entity == null) {
if (getUpdateMasks().unregisterSynced(FaceEntityFlag.getOrdinal())) {
return getUpdateMasks().register(new FaceEntityFlag(entity));
}
return true;
}
return getUpdateMasks().register(new FaceEntityFlag(entity), true);
}
@Override
public boolean faceLocation(Location location) {
if (location == null) {
getUpdateMasks().unregisterSynced(FaceLocationFlag.getOrdinal());
return true;
}
return getUpdateMasks().register(new FaceLocationFlag(location), true);
}
@Override
public boolean sendChat(String string) {
return getUpdateMasks().register(new ForceChatFlag(string));
}
@Override @Override
public int getClientIndex() { public int getClientIndex() {
return this.getIndex() | 0x8000; return this.getIndex() | 0x8000;
@ -1393,4 +1365,8 @@ public class Player extends Entity {
} }
states.remove(key); states.remove(key);
} }
public void updateAppearance() {
getUpdateMasks().register(EntityFlag.Appearance, this);
}
} }

View file

@ -6,7 +6,6 @@ import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.emote.Emotes; import core.game.node.entity.player.link.emote.Emotes;
import core.game.node.item.Item; import core.game.node.item.Item;
import core.game.world.map.RegionManager; import core.game.world.map.RegionManager;
import core.game.world.update.flag.player.AppearanceFlag;
import core.net.packet.PacketRepository; import core.net.packet.PacketRepository;
import core.net.packet.context.InterfaceContext; import core.net.packet.context.InterfaceContext;
import core.net.packet.out.Interface; import core.net.packet.out.Interface;
@ -136,7 +135,7 @@ public final class LoginConfiguration {
UpdateSequence.getRenderablePlayers().add(player); UpdateSequence.getRenderablePlayers().add(player);
RegionManager.move(player); RegionManager.move(player);
player.getMusicPlayer().init(); player.getMusicPlayer().init();
player.getUpdateMasks().register(new AppearanceFlag(player)); player.updateAppearance();
player.getPlayerFlags().setUpdateSceneGraph(true); player.getPlayerFlags().setUpdateSceneGraph(true);
player.getStateManager().init(); player.getStateManager().init();
player.getPacketDispatch().sendInterfaceConfig(226, 1, true); player.getPacketDispatch().sendInterfaceConfig(226, 1, true);

View file

@ -12,8 +12,7 @@ import core.game.world.map.RegionManager;
import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag; import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.context.Graphics;
import core.game.world.update.flag.player.AnimationFlag; import core.game.world.update.flag.EntityFlag;
import core.game.world.update.flag.player.GraphicFlag;
import core.net.packet.PacketRepository; import core.net.packet.PacketRepository;
import core.net.packet.context.*; import core.net.packet.context.*;
import core.net.packet.context.DisplayModelContext.ModelType; import core.net.packet.context.DisplayModelContext.ModelType;
@ -300,7 +299,7 @@ public final class PacketDispatch {
* @param id The animation id. * @param id The animation id.
*/ */
public void sendAnimation(int id) { public void sendAnimation(int id) {
player.getUpdateMasks().register(new AnimationFlag(new Animation(id))); player.getUpdateMasks().register(EntityFlag.Animate, new Animation(id));
} }
/** /**
@ -309,7 +308,7 @@ public final class PacketDispatch {
* @param delay The animation delay. * @param delay The animation delay.
*/ */
public void sendAnimation(int id, int delay) { public void sendAnimation(int id, int delay) {
player.getUpdateMasks().register(new AnimationFlag(new Animation(id, delay))); player.getUpdateMasks().register(EntityFlag.Animate, new Animation(id, delay));
} }
/** /**
@ -317,7 +316,7 @@ public final class PacketDispatch {
* @param id The graphic id. * @param id The graphic id.
*/ */
public void sendGraphic(int id) { public void sendGraphic(int id) {
player.getUpdateMasks().register(new GraphicFlag(new Graphics(id))); player.getUpdateMasks().register(EntityFlag.SpotAnim, new Graphics(id));
} }
/** /**
@ -383,7 +382,7 @@ public final class PacketDispatch {
* @param height The graphic height. * @param height The graphic height.
*/ */
public void sendGraphic(int id, int height) { public void sendGraphic(int id, int height) {
player.getUpdateMasks().register(new GraphicFlag(new Graphics(id, height))); player.getUpdateMasks().register(EntityFlag.SpotAnim, new Graphics(id, height));
} }
public void sendVarClient(int id, int value, boolean cs2) { public void sendVarClient(int id, int value, boolean cs2) {
PacketRepository.send(Config.class, new ConfigContext(player, id, value, cs2)); PacketRepository.send(Config.class, new ConfigContext(player, id, value, cs2));

View file

@ -3,7 +3,6 @@ package core.game.node.entity.player.link;
import core.game.node.entity.Entity; import core.game.node.entity.Entity;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.game.node.entity.state.EntityState; import core.game.node.entity.state.EntityState;
import core.game.world.update.flag.player.AppearanceFlag;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -86,7 +85,7 @@ public final class SkullManager {
*/ */
public void setSkullIcon(int skullIcon) { public void setSkullIcon(int skullIcon) {
player.getAppearance().setSkullIcon(skullIcon); player.getAppearance().setSkullIcon(skullIcon);
player.getUpdateMasks().register(new AppearanceFlag(player)); player.updateAppearance();
} }
/** /**

View file

@ -8,7 +8,6 @@ import core.game.node.entity.impl.Animator.Priority;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.game.node.item.Item; import core.game.node.item.Item;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.player.AppearanceFlag;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import core.game.system.config.ItemConfigParser; import core.game.system.config.ItemConfigParser;
@ -190,7 +189,7 @@ public final class Appearance {
* Method used to sync this appearance with the client. * Method used to sync this appearance with the client.
*/ */
public void sync() { public void sync() {
player.getUpdateMasks().register(new AppearanceFlag(player)); player.updateAppearance();
} }
/** /**

View file

@ -11,7 +11,6 @@ import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.request.assist.AssistSession; import core.game.node.entity.player.link.request.assist.AssistSession;
import core.game.node.item.Item; import core.game.node.item.Item;
import core.game.world.update.flag.player.AppearanceFlag;
import core.net.packet.PacketRepository; import core.net.packet.PacketRepository;
import core.net.packet.context.SkillContext; import core.net.packet.context.SkillContext;
import core.net.packet.out.SkillLevel; import core.net.packet.out.SkillLevel;
@ -279,9 +278,7 @@ public final class Skills {
staticLevels[slot] = newLevel; staticLevels[slot] = newLevel;
if (entity instanceof Player) { if (entity instanceof Player) {
if (updateCombatLevel()) { player.updateAppearance();
player.getUpdateMasks().register(new AppearanceFlag(player));
}
LevelUp.levelup(player, slot, amount); LevelUp.levelup(player, slot, amount);
} }
} }

View file

@ -27,6 +27,10 @@ import java.io.FileWriter
import java.util.Arrays import java.util.Arrays
import core.net.packet.PacketWriteQueue import core.net.packet.PacketWriteQueue
import core.tools.Log import core.tools.Log
import core.game.world.update.flag.*
import core.game.world.update.flag.context.*
import core.game.node.entity.impl.*
import core.game.world.map.Location
@Initializable @Initializable
class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) {
@ -239,5 +243,34 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) {
define("drawroute", Privilege.ADMIN, "", "Visualizes the path your player is taking") {player, _ -> define("drawroute", Privilege.ADMIN, "", "Visualizes the path your player is taking") {player, _ ->
setAttribute (player, "routedraw", !getAttribute(player, "routedraw", false)) setAttribute (player, "routedraw", !getAttribute(player, "routedraw", false))
} }
define ("fmstart", Privilege.ADMIN, "", "") {player, _ ->
setAttribute(player, "fmstart", Location.create(player.location))
}
define ("fmend", Privilege.ADMIN, "", "") {player, _ ->
setAttribute(player, "fmend", Location.create(player.location))
}
define ("fmspeed", Privilege.ADMIN, "", "") {player, args ->
setAttribute(player, "fmspeed", args[1].toIntOrNull() ?: 10)
}
define ("fmspeedend", Privilege.ADMIN, "", "") {player, args ->
setAttribute(player, "fmspeedend", args[1].toIntOrNull() ?: 10)
}
define("testfm", Privilege.ADMIN, "", "") { player, _ ->
val start = getAttribute(player, "fmstart", Location.create(player.location))
val end = getAttribute(player, "fmend", Location.create(player.location))
val speed = getAttribute(player, "fmspeed", 10)
val speedEnd = getAttribute(player, "fmspeedend", 10)
val ani = getAttribute(player, "fmanim", -1)
forceMove(player, start, end, speed, speedEnd, anim = ani)
}
define("fmanim", Privilege.ADMIN, "", "") {player, args ->
setAttribute(player, "fmanim", args[1].toIntOrNull() ?: -1)
}
} }
} }

View file

@ -5,82 +5,42 @@ import core.game.node.entity.combat.ImpactHandler
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.world.update.flag.UpdateFlag import core.game.world.update.flag.UpdateFlag
import core.game.world.update.flag.context.HitMark import core.game.world.update.flag.context.HitMark
import core.game.world.update.flag.npc.NPCHitFlag import core.game.world.update.flag.*
import core.game.world.update.flag.npc.NPCHitFlag1
import core.game.world.update.flag.player.AppearanceFlag
import core.game.world.update.flag.player.HitUpdateFlag
import core.game.world.update.flag.player.HitUpdateFlag1
import core.net.packet.IoBuffer import core.net.packet.IoBuffer
import core.api.*
import core.tools.*
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
/** class UpdateMasks (val owner: Entity) {
* Holds an entity's update masks.
* @author Emperor
*/
class UpdateMasks {
/**
* The mask data.
*/
private var maskData = 0
/**
* The update masks array.
*/
private val masks = arrayOfNulls<UpdateFlag<*>?>(SIZE)
/**
* Gets the appearanceStamp.
* @return The appearanceStamp.
*/
/**
* Sets the appearanceStamp.
* @param appearanceStamp The appearanceStamp to set.
*/
/**
* The appearance time stamp.
*/
var appearanceStamp: Long = 0 var appearanceStamp: Long = 0
private val type = EFlagType.of (owner)
/**
* The synced mask data.
*/
private var syncedMask = 0
/**
* The update masks array.
*/
private val syncedMasks = arrayOfNulls<UpdateFlag<*>?>(SIZE)
/**
* If the update masks are being updated.
*/
private val updating = AtomicBoolean() private val updating = AtomicBoolean()
/** private var presenceFlags = 0
* Registers an update flag. private var syncedPresenceFlags = 0
* @param flag The update flag. private val elements = arrayOfNulls<MaskElement?>(SIZE)
* @return `True` if successful. private val syncedElements = arrayOfNulls<MaskElement?>(SIZE)
*/ private data class MaskElement (val encoder: EFlagProvider, val context: Any?)
/**
* Registers an update flag.
* @param flag The update flag.
* @return `True` if successful.
*/
@JvmOverloads @JvmOverloads
fun register(flag: UpdateFlag<*>, synced: Boolean = false): Boolean { fun register(flag: EntityFlag, context: Any?, sync: Boolean = false) : Boolean {
var synced = synced var synced = sync
if (updating.get()) { var provider = EntityFlags.getFlagFor (type, flag)
if (provider == null) {
logWithStack(this::class.java, Log.ERR, "Tried to use flag ${flag.name} which is not available for ${type.name} in this revision.")
return false return false
} }
if (flag is AppearanceFlag) { if (updating.get())
return false
if (flag == EntityFlag.Appearance) {
appearanceStamp = System.currentTimeMillis() appearanceStamp = System.currentTimeMillis()
synced = true synced = true
} }
if (synced) { if (synced) {
syncedMasks[flag.ordinal()] = flag syncedElements[provider.ordinal] = MaskElement (provider, context)
syncedMask = syncedMask or flag.data() syncedPresenceFlags = syncedPresenceFlags or provider.presenceFlag
} }
maskData = maskData or flag.data() elements[provider.ordinal] = MaskElement (provider, context)
masks[flag.ordinal()] = flag presenceFlags = presenceFlags or provider.presenceFlag
return true return true
} }
@ -90,9 +50,9 @@ class UpdateMasks {
* @return `True` if the mask got removed. * @return `True` if the mask got removed.
*/ */
fun unregisterSynced(ordinal: Int): Boolean { fun unregisterSynced(ordinal: Int): Boolean {
if (syncedMasks[ordinal] != null) { if (syncedElements[ordinal] != null) {
syncedMask = syncedMask and syncedMasks[ordinal]!!.data().inv() syncedPresenceFlags = syncedPresenceFlags and syncedElements[ordinal]!!.encoder.presenceFlag.inv()
syncedMasks[ordinal] = null syncedElements[ordinal] = null
return true return true
} }
return false return false
@ -105,16 +65,16 @@ class UpdateMasks {
* @param buffer The buffer to write on. * @param buffer The buffer to write on.
*/ */
fun write(p: Player?, e: Entity?, buffer: IoBuffer) { fun write(p: Player?, e: Entity?, buffer: IoBuffer) {
var maskData = maskData var maskData = presenceFlags
if (maskData >= 0x100) { if (maskData >= 0x100) {
maskData = maskData or if (e is Player) 0x10 else 0x8 maskData = maskData or if (e is Player) 0x10 else 0x8
buffer.put(maskData).put(maskData shr 8) buffer.put(maskData).put(maskData shr 8)
} else { } else {
buffer.put(maskData) buffer.put(maskData)
} }
for (i in masks.indices) { for (i in elements.indices) {
val flag = masks[i] val element = elements[i]
flag?.writeDynamic(buffer, p) element?.encoder?.writeToDynamic(buffer, element.context, p!!)
} }
} }
@ -126,10 +86,11 @@ class UpdateMasks {
* @param appearance If the appearance mask should be written. * @param appearance If the appearance mask should be written.
*/ */
fun writeSynced(p: Player?, e: Entity?, buffer: IoBuffer, appearance: Boolean) { fun writeSynced(p: Player?, e: Entity?, buffer: IoBuffer, appearance: Boolean) {
var maskData = maskData var maskData = presenceFlags
var synced = syncedMask var synced = syncedPresenceFlags
if (!appearance && synced and AppearanceFlag.getData() != 0) { var appearanceFlag = EntityFlags.getPresenceFlag(type, EntityFlag.Appearance)
synced = synced and AppearanceFlag.getData().inv() if (!appearance && synced and appearanceFlag != 0) {
synced = synced and appearanceFlag.inv()
} }
maskData = maskData or synced maskData = maskData or synced
if (maskData >= 0x100) { if (maskData >= 0x100) {
@ -138,15 +99,15 @@ class UpdateMasks {
} else { } else {
buffer.put(maskData) buffer.put(maskData)
} }
for (i in masks.indices) { for (i in elements.indices) {
var flag = masks[i] var element = elements[i]
if (flag == null) { if (element == null) {
flag = syncedMasks[i] element = syncedElements[i]
if (!appearance && flag is AppearanceFlag) { if (!appearance && element != null && element.encoder.flag == EntityFlag.Appearance) {
continue continue
} }
} }
flag?.writeDynamic(buffer, p) element?.encoder?.writeToDynamic(buffer, element.context, p!!)
} }
} }
@ -173,13 +134,8 @@ class UpdateMasks {
* @param secondary If the hit update is secondary. * @param secondary If the hit update is secondary.
*/ */
private fun registerHitUpdate(e: Entity, impact: ImpactHandler.Impact, secondary: Boolean): HitMark { private fun registerHitUpdate(e: Entity, impact: ImpactHandler.Impact, secondary: Boolean): HitMark {
val player = e is Player
val mark = HitMark(impact.amount, impact.type.ordinal, e) val mark = HitMark(impact.amount, impact.type.ordinal, e)
if (player) { register(if (secondary) EntityFlag.SecondaryHit else EntityFlag.PrimaryHit, mark)
register(if (secondary) HitUpdateFlag1(mark) else HitUpdateFlag(mark))
} else {
register(if (secondary) NPCHitFlag1(mark) else NPCHitFlag(mark))
}
return mark return mark
} }
@ -187,10 +143,10 @@ class UpdateMasks {
* Resets the update masks. * Resets the update masks.
*/ */
fun reset() { fun reset() {
for (i in masks.indices) { for (i in elements.indices) {
masks[i] = null elements[i] = null
} }
maskData = 0 presenceFlags = 0
updating.set(false) updating.set(false)
} }
@ -203,14 +159,14 @@ class UpdateMasks {
* @return `True` if so. * @return `True` if so.
*/ */
val isUpdateRequired: Boolean val isUpdateRequired: Boolean
get() = maskData != 0 get() = presenceFlags != 0
/** /**
* Checks if synced update masks have been registered. * Checks if synced update masks have been registered.
* @return `True` if so. * @return `True` if so.
*/ */
fun hasSynced(): Boolean { fun hasSynced(): Boolean {
return syncedMask != 0 return syncedPresenceFlags != 0
} }
companion object { companion object {

View file

@ -0,0 +1,29 @@
package core.game.world.update.flag
import core.net.packet.IoBuffer
import core.game.node.entity.Entity
import kotlin.reflect.*
import core.api.*
import core.tools.*
enum class EFlagType {
Player,
NPC;
companion object {
@JvmStatic fun of (e: Entity) : EFlagType {
if (e is core.game.node.entity.player.Player) return EFlagType.Player
else return EFlagType.NPC
}
}
}
open class EFlagProvider (val revision: Int, val type: EFlagType, val presenceFlag: Int, val ordinal: Int, val flag: EntityFlag) {
open fun writeTo (buffer: IoBuffer, context: Any?) {}
open fun writeToDynamic (buffer: IoBuffer, context: Any?, e: Entity) {
writeTo (buffer, context)
}
fun logInvalidType (context: Any?, expected: KType) {
logWithStack(this::class.java, Log.ERR, "Invalid context of type ${context?.let { it::class.java.simpleName } ?: "null"} passed to ${this::class.simpleName} flag which expects $expected.")
}
}

View file

@ -0,0 +1,16 @@
package core.game.world.update.flag
enum class EntityFlag {
Chat,
ForceChat,
PrimaryHit,
SecondaryHit,
Animate,
SpotAnim,
Appearance,
FaceEntity,
FaceLocation,
ForceMove,
AnimSeq,
TypeSwap
}

View file

@ -0,0 +1,42 @@
package core.game.world.update.flag
import core.api.*
import core.ServerConstants
import kotlin.reflect.*
import kotlin.reflect.full.*
object EntityFlags {
lateinit var flagProviders : HashMap<Int, EFlagProvider>
init {
flagProviders = HashMap()
registerFlagProviders (PlayerFlags530::class)
registerFlagProviders (NPCFlags530::class)
}
fun registerFlagProviders (parent: KClass<*>) {
val clazzes = parent.sealedSubclasses
for (clazz in clazzes) {
val p = clazz.primaryConstructor?.call() as? EFlagProvider ?: continue
flagProviders[getMapToken(p.revision, p.type, p.flag)] = p
}
}
fun getFlagFor (type: EFlagType, flag: EntityFlag) : EFlagProvider? {
val revision = 530
return flagProviders[getMapToken(revision, type, flag)]
}
@JvmStatic fun getOrdinal (type: EFlagType, flag: EntityFlag) : Int {
return getFlagFor(type, flag)?.ordinal ?: -1
}
@JvmStatic fun getPresenceFlag (type: EFlagType, flag: EntityFlag) : Int {
return getFlagFor(type, flag)?.presenceFlag ?: 0
}
private fun getMapToken (revision: Int, type: EFlagType, flag: EntityFlag) : Int {
var token = ((revision shl 8) + (type.ordinal shl 4) + flag.ordinal)
return token
}
}

View file

@ -0,0 +1,108 @@
package core.game.world.update.flag
import core.net.packet.IoBuffer
import core.game.world.update.flag.context.*
import core.game.world.update.flag.*
import core.game.world.map.Location
import core.game.node.entity.Entity
import core.game.node.entity.impl.ForceMovement
import core.game.node.entity.player.Player
import core.tools.*
import core.api.*
import java.nio.charset.StandardCharsets
import kotlin.reflect.*
sealed class NPCFlags530 (p: Int, o: Int, f: EntityFlag) : EFlagProvider (530, EFlagType.NPC, p, o, f) {
class PrimaryHit : NPCFlags530 (0x40, 0, EntityFlag.PrimaryHit) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is HitMark) {
logInvalidType (context, typeOf<HitMark>())
return
}
buffer.p1 (context.damage)
buffer.p1neg (context.type)
val e = context.entity
var ratio = 255
val max = e.skills.maximumLifepoints
if (e.skills.lifepoints < max)
ratio = e.skills.lifepoints * 255 / max
buffer.p1sub (ratio)
}
}
class SecondaryHit : NPCFlags530 (0x2, 1, EntityFlag.SecondaryHit) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is HitMark) {
logInvalidType (context, typeOf<HitMark>())
return
}
buffer.p1neg (context.damage)
buffer.p1sub (context.type)
}
}
class Animate : NPCFlags530 (0x10, 2, EntityFlag.Animate) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Animation) {
logInvalidType (context, typeOf<Animation>())
return
}
buffer.p2 (context.id)
buffer.p1 (context.delay)
}
}
class FaceEntity : NPCFlags530 (0x4, 3, EntityFlag.FaceEntity) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Entity?) {
logInvalidType (context, typeOf<Entity>())
return
}
if (context == null) {
buffer.p2add (-1)
} else {
buffer.p2add (context.getClientIndex())
}
}
}
class SpotAnim : NPCFlags530 (0x80, 4, EntityFlag.SpotAnim) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Graphics) {
logInvalidType (context, typeOf<Graphics>())
return
}
buffer.p2add (context.id)
buffer.ip4 ((context.height shl 16) + context.delay)
}
}
class TypeSwap : NPCFlags530 (0x1, 5, EntityFlag.TypeSwap) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Int) {
logInvalidType (context, typeOf<Graphics>())
return
}
buffer.ip2 (context)
}
}
class ForceChat : NPCFlags530 (0x20, 6, EntityFlag.ForceChat) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is String) {
logInvalidType (context, typeOf<String>())
return
}
buffer.putString (context)
}
}
class AnimationSequence : NPCFlags530 (0x100, 7, EntityFlag.AnimSeq) {
//TODO
}
class FaceLocation : NPCFlags530 (0x200, 8, EntityFlag.FaceLocation) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Location) {
logInvalidType (context, typeOf<Location>())
return
}
buffer.p2add ((context.x shl 1) + 1)
buffer.p2 ((context.y shl 1) + 1)
}
}
}

View file

@ -0,0 +1,195 @@
package core.game.world.update.flag
import core.net.packet.IoBuffer
import core.game.world.update.flag.context.*
import core.game.world.update.flag.*
import core.game.world.map.Location
import core.game.node.entity.Entity
import core.game.node.entity.impl.ForceMovement
import core.game.node.entity.player.Player
import core.tools.*
import core.api.*
import java.nio.charset.StandardCharsets
import kotlin.reflect.*
sealed class PlayerFlags530 (p: Int, o: Int, f: EntityFlag) : EFlagProvider (530, EFlagType.Player, p, o, f) {
class Chat : PlayerFlags530 (0x80, 0, EntityFlag.Chat) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is ChatMessage) {
logInvalidType (context, typeOf<ChatMessage>())
return
}
buffer.ip2 (context.effects)
if (context.isQuickChat)
buffer.p1 (4)
else
buffer.p1 (context.chatIcon)
val chatBuf = ByteArray(256)
chatBuf[0] = context.text.length.toByte()
val offset = 1 + StringUtils.encryptPlayerChat (
chatBuf,
0, 1,
context.text.length,
context.text.toByteArray(StandardCharsets.UTF_8)
)
buffer.p1 (offset + 1)
buffer.putReverse (chatBuf, 0, offset)
}
}
class PrimaryHit : PlayerFlags530 (0x1, 1, EntityFlag.PrimaryHit) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is HitMark) {
logInvalidType (context, typeOf<HitMark>())
return
}
buffer.psmarts (context.damage)
buffer.p1add (context.type)
var ratio = 255
var e = context.entity
var max = e.skills.maximumLifepoints
if (max > e.skills.lifepoints)
ratio = e.skills.lifepoints * 255 / max
buffer.p1sub (ratio)
}
}
class Animate : PlayerFlags530 (0x8, 2, EntityFlag.Animate) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Animation) {
logInvalidType (context, typeOf<Animation>())
return
}
buffer.p2 (context.id)
buffer.p1 (context.delay)
}
}
class Appearance : PlayerFlags530 (0x4, 3, EntityFlag.Appearance) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Player) {
logInvalidType (context, typeOf<Player>())
return
}
val appearance = context.appearance
appearance.prepareBodyData(context)
var settings = appearance.gender.toByte().toInt()
val nonPvp = context.skullManager.isWilderness && context.skullManager.isWildernessDisabled
if (context.size() > 1)
settings += (context.size() - 1) shl 3
if (nonPvp)
settings += 0x4 //flag the player as being non-pvp, which shows skill level instead of combat level.
buffer.p1 (0) //stand-in for size.
val startPos = buffer.toByteBuffer().position()
buffer.p1 (settings)
buffer.p1 (appearance.skullIcon)
buffer.p1 (appearance.headIcon)
val npcId = appearance.npcId
if (npcId == -1) {
val parts = appearance.bodyParts
for (i in 0 until 12) {
if (parts[i] == 0) buffer.p1 (0)
else buffer.p2 (parts[i])
}
} else {
buffer.p2 (-1)
buffer.p2 (npcId)
buffer.p1 (255)
}
arrayOf ( appearance.hair, appearance.torso, appearance.legs, appearance.feet, appearance.skin ).forEach { part ->
buffer.p1 (part.color)
}
buffer.p2 (appearance.renderAnimation)
buffer.p8 (StringUtils.stringToLong(context.username))
if (nonPvp) {
buffer.p1 (context.properties.currentCombatLevel) //with summoning
buffer.p2 (context.skills.getTotalLevel())
} else {
buffer.p1 (context.properties.currentCombatLevel) //without summoning
buffer.p1 (context.properties.combatLevel) //with summoning
buffer.p1 (context.skullManager.level) //combat range
}
buffer.p1 (0) //this is the sound radius, and if set, 4 shorts need to be set as well which include the sound IDs
//to play to everyone in that radius when the player is rendered onscreen for the first time.
buffer.psizeadd (buffer.toByteBuffer().position() - startPos)
}
}
class FaceEntity : PlayerFlags530 (0x2, 4, EntityFlag.FaceEntity) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Entity?) {
logInvalidType (context, typeOf<Entity>())
return
}
if (context == null)
buffer.p2add (-1)
else
buffer.p2add (context.getClientIndex())
}
}
class ForceMove : PlayerFlags530 (0x400, 5, EntityFlag.ForceMove) {
override fun writeToDynamic (buffer: IoBuffer, context: Any?, e: Entity) {
if (context !is ForceMoveCtx) {
logInvalidType (context, typeOf<ForceMoveCtx>())
return
}
if (e !is Player) {
logInvalidType (context, typeOf<Player>())
return
}
val l = e.playerFlags.getLastSceneGraph()
//start location
buffer.p1neg (context.start.getSceneX(l))
buffer.p1 (context.start.getSceneY(l))
//end location
buffer.p1add (context.dest.getSceneX(l))
buffer.p1 (context.dest.getSceneY(l))
//arrival times (in client cycles)
buffer.ip2 (context.startArrive) //# of client cycles to start location
buffer.ip2 (context.startArrive + context.destArrive) //# of client cycles to end location
buffer.p1neg (context.direction.toInteger()) //direction of movement
}
}
class ForceChat : PlayerFlags530 (0x20, 6, EntityFlag.ForceChat) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is String) {
logInvalidType (context, typeOf<String>())
return
}
buffer.putString(context)
}
}
class SecondaryHit : PlayerFlags530 (0x200, 7, EntityFlag.SecondaryHit) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is HitMark) {
logInvalidType (context, typeOf<HitMark>())
return
}
buffer.psmarts (context.damage)
buffer.p1sub (context.type)
}
}
class AnimationSequence : PlayerFlags530 (0x800, 8, EntityFlag.AnimSeq) {
//TODO
}
class SpotAnim : PlayerFlags530 (0x100, 9, EntityFlag.SpotAnim) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Graphics) {
logInvalidType (context, typeOf<Graphics>())
return
}
buffer.ip2 (context.id)
buffer.mp4 ((context.height shl 16) + context.delay)
}
}
class FaceLocation : PlayerFlags530 (0x40, 10, EntityFlag.FaceLocation) {
override fun writeTo (buffer: IoBuffer, context: Any?) {
if (context !is Location) {
logInvalidType (context, typeOf<Location>())
return
}
buffer.p2 ((context.x shl 1) + 1)
buffer.ip2add ((context.y shl 1) + 1)
}
}
}

View file

@ -1,6 +1,7 @@
package core.game.world.update.flag.context package core.game.world.update.flag.context
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.info.Rights
/** /**
* Represents a chat message. * Represents a chat message.
@ -35,6 +36,8 @@ class ChatMessage
var numChars = numChars var numChars = numChars
private set private set
var chatIcon = Rights.getChatIcon(player)
@JvmField @JvmField
var isQuickChat = false var isQuickChat = false
} }

View file

@ -0,0 +1,5 @@
package core.game.world.update.flag.context
import core.game.world.map.*
data class ForceMoveCtx (val start: Location, val dest: Location, val startArrive: Int, val destArrive: Int, val direction: Direction)

View file

@ -27,6 +27,8 @@ public class HitMark {
*/ */
private final Entity entity; private final Entity entity;
public boolean showHealthBar = true;
/** /**
* Constructs a new {@code HitMark} {@code Object}. * Constructs a new {@code HitMark} {@code Object}.
* @param damage The amount of damage. * @param damage The amount of damage.
@ -39,6 +41,13 @@ public class HitMark {
this.entity = entity; this.entity = entity;
} }
public HitMark(int damage, int type, Entity entity, boolean showHealthBar) {
this.damage = damage;
this.type = type;
this.entity = entity;
this.showHealthBar = showHealthBar;
}
/** /**
* Gets the damage. * Gets the damage.
* @return The damage. * @return The damage.

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.Animation;
import core.net.packet.IoBuffer;
/**
* The NPC animation update flag.
* @author Emperor
*
*/
public final class NPCAnimation extends UpdateFlag<Animation> {
/**
* Constructs a new {@code NPCAnimation} {@code Object}.
* @param context The animation.
*/
public NPCAnimation(Animation context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putShort(context.getId()).put(context.getDelay());
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 2;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x10;
}
}

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.node.entity.Entity;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* The face entity update flag for NPCs.
* @author Emperor
*
*/
public final class NPCFaceEntity extends UpdateFlag<Entity> {
/**
* Constructs a new {@code NPCFaceEntity} {@code Object}.
* @param context The context.
*/
public NPCFaceEntity(Entity context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putShortA(context == null ? -1 : context.getClientIndex());
}
@Override
public int data() {
return 0x4;
}
@Override
public int ordinal() {
return getOrdinal();
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int getOrdinal() {
return 3;
}
}

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.world.map.Location;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* The NPC face coordinates update flag.
* @author Emperor
*
*/
public final class NPCFaceLocation extends UpdateFlag<Location> {
/**
* Constructs a new {@code NPCFaceLocation} {@code Object}.
* @param context The location to face.
*/
public NPCFaceLocation(Location context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putShortA((context.getX() << 1) + 1).putShort((context.getY() << 1) + 1);
}
@Override
public int data() {
return 0x200;
}
@Override
public int ordinal() {
return getOrdinal();
}
/**
* Gets the mask ordinal.
* @return The ordinal.
*/
public static int getOrdinal() {
return 8;
}
}

View file

@ -1,43 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* The NPC force chat update flag.
* @author Emperor
*
*/
public final class NPCForceChat extends UpdateFlag<String> {
/**
* Constructs a new {@code NPCForceChat} {@code Object}.
* @param context The chat message.
*/
public NPCForceChat(String context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putString(context);
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 6;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x20;
}
}

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.Graphics;
import core.net.packet.IoBuffer;
/**
* Handles an NPC's graphic update flag.
* @author Emperor
*
*/
public final class NPCGraphic extends UpdateFlag<Graphics> {
/**
* Constructs a new {@code NPCGraphic} {@code Object}.
* @param context The graphics.
*/
public NPCGraphic(Graphics context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putShortA(context.getId()).putLEInt(context.getHeight() << 16 | context.getDelay());
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 4;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x80;
}
}

View file

@ -1,56 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.node.entity.Entity;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.HitMark;
import core.net.packet.IoBuffer;
/**
* The NPC's main hit update flag.
* @author Emperor
*
*/
public final class NPCHitFlag extends UpdateFlag<HitMark> {
/**
* Constructs a new {@code NPCHitFlag} {@code Object}.
* @param context The hit mark.
*/
public NPCHitFlag(HitMark context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
Entity e = context.getEntity();
int max = e.getSkills().getMaximumLifepoints();
int ratio = 0;
if (max > 0) {
if (max < e.getSkills().getLifepoints()) {
ratio = 255;
} else {
ratio = e.getSkills().getLifepoints() * 255 / max;
}
}
buffer.put(context.getDamage()).putC(context.getType()).putS(ratio);
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 0;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x40;
}
}

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.HitMark;
import core.net.packet.IoBuffer;
/**
* The NPC's supporting hit update flag.
* @author Emperor
*
*/
public class NPCHitFlag1 extends UpdateFlag<HitMark> {
/**
* Constructs a new {@code NPCHitFlag1} {@code Object}.
* @param context The hit mark.
*/
public NPCHitFlag1(HitMark context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putC(context.getDamage()).putS(context.getType());
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 1;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x2;
}
}

View file

@ -1,44 +0,0 @@
package core.game.world.update.flag.npc;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* The switch NPC id update flag.
* @author Emperor
*
*/
public final class NPCSwitchId extends UpdateFlag<Integer> {
/**
* Constructs a new {@code NPCSwitchId} {@code Object}.
* @param context The new NPC id.
*/
public NPCSwitchId(int context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putLEShort(context);
}
@Override
public int data() {
return 0x1;
}
@Override
public int ordinal() {
return getOrdinal();
}
/**
* Gets the mask ordinal.
* @return The ordinal.
*/
public static int getOrdinal() {
return 5;
}
}

View file

@ -1,43 +0,0 @@
package core.game.world.update.flag.player;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.Animation;
import core.net.packet.IoBuffer;
/**
* Handles the animation update flag.
* @author Emperor
*/
public final class AnimationFlag extends UpdateFlag<Animation> {
/**
* Constructs a new {@code AnimationFlag} {@code Object}.
*/
public AnimationFlag(Animation context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putShort(context.getId());
buffer.put(context.getDelay());
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 2;
}
/**
* Gets the mask data of the animation update flag.
* @return The mask data.
*/
public static int maskData() {
return 0x8;
}
}

View file

@ -1,91 +0,0 @@
package core.game.world.update.flag.player;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.appearance.Appearance;
import core.game.node.entity.player.link.appearance.BodyPart;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
import core.tools.StringUtils;
/**
* Handles the appearance update flag.
* @author Emperor
*
*/
public final class AppearanceFlag extends UpdateFlag<Player> {
/**
* Constructs a new {@code AppearanceFlag} {@code Object}.
* @param player The player.
*/
public AppearanceFlag(Player player) {
super(player);
}
@Override
public void write(IoBuffer buffer) {
Appearance appearance = context.getAppearance();
appearance.prepareBodyData(context);
IoBuffer block = new IoBuffer();
int settings = appearance.getGender().toByte();
if (context.size() > 1) {
settings |= (context.size() - 1) << 3;
}
block.put(settings); // settings hash.
block.put(appearance.getSkullIcon()); // Skull icon
block.put(appearance.getHeadIcon()); // Head icon
int npcId = appearance.getNpcId();
if (npcId == -1) {
int[] parts = appearance.getBodyParts();
for (int i = 0; i < 12; i++) {
int value = parts[i];
if (value == 0) {
block.put(0);
} else {
block.putShort(value);
}
}
} else {
block.putShort(-1);
block.putShort(npcId);
block.put(255);
}
final BodyPart[] colors = new BodyPart[] { appearance.getHair(), appearance.getTorso(), appearance.getLegs(), appearance.getFeet(), appearance.getSkin() };
for (int i = 0; i < colors.length; i++) {// colours
block.put(colors[i].getColor());
}
block.putShort(appearance.getRenderAnimation());
block.putLong(StringUtils.stringToLong(context.getUsername()));
block.put(context.getProperties().getCurrentCombatLevel());
block.putShort(0);
block.put(0);
buffer.putA(block.toByteBuffer().position());
buffer.put(block);
}
@Override
public int data() {
return getData();
}
@Override
public int ordinal() {
return getOrdinal();
}
/**
* Gets the ordinal for this flag.
* @return The flag ordinal.
*/
public static int getOrdinal() {
return 3;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int getData() {
return 0x4;
}
}

View file

@ -1,59 +0,0 @@
package core.game.world.update.flag.player;
import core.game.node.entity.player.info.Rights;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.ChatMessage;
import core.net.packet.IoBuffer;
import core.tools.StringUtils;
import java.nio.charset.StandardCharsets;
/**
* Handles the chat flag.
* @author Emperor
*/
public class ChatFlag extends UpdateFlag<ChatMessage> {
/**
* Constructs a new {@code ChatFlag.java} {@code Object}.
* @param context The context.
*/
public ChatFlag(ChatMessage context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
byte[] chatStr = new byte[256];
chatStr[0] = (byte) context.getText().length();
int offset = 1 + StringUtils.encryptPlayerChat(chatStr, 0, 1, context.getText().length(), context.getText().getBytes(StandardCharsets.UTF_8));
buffer.putLEShort(context.getEffects()); // 0x8000 does something (you'd
// need to send something
// extra.
if(context.isQuickChat){
buffer.put((byte) 4);
} else {
buffer.put((byte) Rights.getChatIcon(context.getPlayer()));
}
buffer.put(offset + 1);
buffer.putReverse(chatStr, 0, offset);
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 0;
}
/**
* Gets the mask data of the chat update flag.
* @return The mask data.
*/
public static int maskData() {
return 0x80;
}
}

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.player;
import core.game.node.entity.Entity;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* The face entity update flag.
* @author Emperor
*
*/
public final class FaceEntityFlag extends UpdateFlag<Entity> {
/**
* Constructs a new {@code FaceEntityFlag} {@code Object}.
* @param context The entity to face.
*/
public FaceEntityFlag(Entity context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putShortA(context == null ? -1 : context.getClientIndex());
}
@Override
public int data() {
return 0x2;
}
@Override
public int ordinal() {
return getOrdinal();
}
/**
* Gets the mask ordinal.
* @return The ordinal.
*/
public static int getOrdinal() {
return 4;
}
}

View file

@ -1,84 +0,0 @@
package core.game.world.update.flag.player;
import core.game.node.Node;
import core.game.node.scenery.Scenery;
import core.game.world.map.Location;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* Handles the face coordinates update flag.
* @author Emperor
*/
public class FaceLocationFlag extends UpdateFlag<Location> {
/**
* Constructs a new {@code FaceLocationFlag} {@code Object}.
* @param location The location to face.
*/
public FaceLocationFlag(Location location) {
super(location);
}
/**
* Gets the face location of the node.
* @param n The facing node.
* @param node The node to face.
* @return The location.
*/
public static Location getFaceLocation(Node n, Node node) {
int x = node.size() >> 1;
int y = node.size() >> 1;
if (node instanceof Scenery) {
Scenery o = (Scenery) node;
x = o.getDefinition().sizeX >> 1;
y = o.getDefinition().sizeY >> 1;
if (o.getRotation() % 2 != 0) {
x = y;
y = o.getDefinition().sizeX >> 1;
}
if (n.getLocation().equals(o.getLocation()) && node.size() == 1) {
switch (o.getRotation()) {
case 0:
x -= 1;
break;
case 1:
y += 1;
break;
case 2:
x += 1;
break;
case 3:
y -= 1;
break;
}
}
}
return node.getLocation().transform(x, y, 0);
}
@Override
public void write(IoBuffer buffer) {
buffer.putShort((context.getX() << 1) + 1);
buffer.putLEShortA((context.getY() << 1) + 1);
}
@Override
public int data() {
return 0x40;
}
@Override
public int ordinal() {
return getOrdinal();
}
/**
* Gets the mask ordinal.
* @return The ordinal.
*/
public static int getOrdinal() {
return 10;
}
}

View file

@ -1,44 +0,0 @@
package core.game.world.update.flag.player;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* The force chat update flag.
* @author Emperor
*
*/
public final class ForceChatFlag extends UpdateFlag<String> {
/**
* Constructs a new {@code ForceChatFlag} {@code Object}.
* @param context The chat message.
*/
public ForceChatFlag(String context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putString(context);
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 6;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x20;
}
}

View file

@ -1,60 +0,0 @@
package core.game.world.update.flag.player;
import core.game.node.entity.Entity;
import core.game.node.entity.impl.ForceMovement;
import core.game.node.entity.player.Player;
import core.game.world.map.Location;
import core.game.world.update.flag.UpdateFlag;
import core.net.packet.IoBuffer;
/**
* Handles the force movement player update flag.
* @author Emperor
*
*/
public final class ForceMovementFlag extends UpdateFlag<ForceMovement> {
/**
* Constructs a new {@code ForceMovementFlag} {@code Object}.
* @param forceMovement The force movement data.
*/
public ForceMovementFlag(ForceMovement forceMovement) {
super(forceMovement);
}
@Override
public void write(IoBuffer buffer) {
}
@Override
public void writeDynamic(IoBuffer buffer, Entity e) {
Location l = ((Player) e).getPlayerFlags().getLastSceneGraph();
buffer.putC(context.getStart().getSceneX(l)) // Start location
.put(context.getStart().getSceneY(l)).putA(context.getDestination().getSceneX(l)) // Destination
// location
.put(context.getDestination().getSceneY(l)).putLEShort(context.getCommenceSpeed() * 30)// Commencing
// speed
.putLEShort((context.getCommenceSpeed() * 30) + (context.getPathSpeed() * 30 + 1)) // Path
// speed
.putC(context.getDirection().toInteger());
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 5;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x400;
}
}

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.player;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.Graphics;
import core.net.packet.IoBuffer;
/**
* Handles the graphic update flag.
* @author Emperor
*
*/
public final class GraphicFlag extends UpdateFlag<Graphics> {
/**
* Constructs a new {@code GraphicFlag} {@code Object}.
* @param context The context.
*/
public GraphicFlag(Graphics context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putLEShort(context.getId());
buffer.putIntB(context.getHeight() << 16 | context.getDelay());
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 9;
}
/**
* Gets the mask data of the graphic update flag.
* @return The mask data.
*/
public static int maskData() {
return 0x100;
}
}

View file

@ -1,52 +0,0 @@
package core.game.world.update.flag.player;
import core.game.node.entity.Entity;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.HitMark;
import core.net.packet.IoBuffer;
/**
* The main hit update flag.
* @author Emperor
*
*/
public final class HitUpdateFlag extends UpdateFlag<HitMark> {
/**
* Constructs a new {@code HitUpdateFlag} {@code Object}.
* @param context The hit mark.
*/
public HitUpdateFlag(HitMark context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
Entity e = context.getEntity();
int max = e.getSkills().getMaximumLifepoints();
int ratio = 255;
if (max > e.getSkills().getLifepoints()) {
ratio = e.getSkills().getLifepoints() * 255 / max;
}
buffer.putSmart(context.getDamage()).putA(context.getType()).putS(ratio);
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 1;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x1;
}
}

View file

@ -1,45 +0,0 @@
package core.game.world.update.flag.player;
import core.game.world.update.flag.UpdateFlag;
import core.game.world.update.flag.context.HitMark;
import core.net.packet.IoBuffer;
/**
* The supportive hit update flag.
* @author Emperor
*
*/
public final class HitUpdateFlag1 extends UpdateFlag<HitMark> {
/**
* Constructs a new {@code HitUpdateFlag1} {@code Object}.
* @param context The hit mark.
*/
public HitUpdateFlag1(HitMark context) {
super(context);
}
@Override
public void write(IoBuffer buffer) {
buffer.putSmart(context.getDamage()).putS(context.getType());
}
@Override
public int data() {
return maskData();
}
@Override
public int ordinal() {
return 7;
}
/**
* Gets the mask data.
* @return The mask data.
*/
public static int maskData() {
return 0x200;
}
}

View file

@ -92,6 +92,219 @@ public class IoBuffer {
return this; return this;
} }
/**
* What follows are put/get methods using authentic naming.
* The older methods are kept for the sake of backwards compatibility within the codebase.
*/
public IoBuffer p1 (int value) {
buf.put((byte) value);
return this;
}
public IoBuffer p1add (int value) {
buf.put((byte) (value + 128));
return this;
}
public IoBuffer p1sub (int value) {
buf.put((byte) (128 - value));
return this;
}
public IoBuffer p1neg (int value) {
buf.put ((byte) -value);
return this;
}
public IoBuffer p2 (int value) {
buf.put((byte) (value >> 8));
buf.put((byte) value);
return this;
}
public IoBuffer p2add (int value) {
buf.put((byte) (value >> 8));
buf.put((byte) (value + 128));
return this;
}
public IoBuffer ip2 (int value) {
buf.put((byte) value);
buf.put((byte) (value >> 8));
return this;
}
public IoBuffer ip2add (int value) {
buf.put((byte) (value + 128));
buf.put((byte) (value >> 8));
return this;
}
public IoBuffer p3 (int value) {
buf.put((byte) (value >> 16));
buf.put((byte) (value >> 8));
buf.put((byte) value);
return this;
}
public IoBuffer ip3 (int value) {
buf.put((byte) value);
buf.put((byte) (value >> 8));
buf.put((byte) (value >> 16));
return this;
}
public IoBuffer p4 (int value) {
buf.put((byte) (value >> 24));
buf.put((byte) (value >> 16));
buf.put((byte) (value >> 8));
buf.put((byte) value);
return this;
}
public IoBuffer ip4 (int value) {
buf.put((byte) value);
buf.put((byte) (value >> 8));
buf.put((byte) (value >> 16));
buf.put((byte) (value >> 24));
return this;
}
public IoBuffer mp4 (int value) {
buf.put((byte) (value >> 16));
buf.put((byte) (value >> 24));
buf.put((byte) value);
buf.put((byte) (value >> 8));
return this;
}
public IoBuffer imp4 (int value) {
buf.put((byte) (value >> 8));
buf.put((byte) value);
buf.put((byte) (value >> 24));
buf.put((byte) (value >> 16));
return this;
}
public IoBuffer p8 (long value) {
buf.put((byte) (value >> 56));
buf.put((byte) (value >> 48));
buf.put((byte) (value >> 40));
buf.put((byte) (value >> 32));
buf.put((byte) (value >> 24));
buf.put((byte) (value >> 16));
buf.put((byte) (value >> 8));
buf.put((byte) value);
return this;
}
public IoBuffer pVarInt (int value) {
if ((value & 0xffffff80) != 0) {
if ((value & 0xffffc000) != 0) {
if ((value & 0xFFE00000) != 0) {
if ((value & 0xF0000000) != 0) {
this.p1(value >>> 28 | 0x80);
}
this.p1(value >>> 21 | 0x80);
}
this.p1(value >>> 14 | 0x80);
}
this.p1(value >>> 7 | 0x80);
}
return this.p1(value & 0x7F);
}
public IoBuffer pVarLong (int size, long value) {
int bytes = size - 1;
if (bytes < 0 || bytes > 7)
throw new IllegalArgumentException();
for (int shift = bytes * 8; shift >= 0; shift -= 8)
this.p1 ((byte) (value >> shift));
return this;
}
public IoBuffer psmarts (int value) {
if (value >= 0 && value < 128)
this.p1(value);
else if (value >= 0 && value < 0x8000)
this.p2(value + 0x8000);
else
throw new IllegalArgumentException("smart out of range: $value");
return this;
}
public IoBuffer psize (int length) {
buf.put (buf.position() - length - 1, (byte) length);
return this;
}
public IoBuffer psizeadd (int length) {
buf.put (buf.position() - length - 1, (byte) (length + 128));
return this;
}
public int g1() {
return buf.get() & 0xFF;
}
public int g1add() {
return (buf.get() - 128) & 0xFF;
}
public int g1neg() {
return -(buf.get() & 0xFF);
}
public int g1sub() {
return (128 - buf.get()) & 0xFF;
}
public int g2() {
return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF);
}
public int g2add() {
return ((buf.get() & 0xff) << 8) + ((buf.get() - 128) & 0xFF);
}
public int ig2() {
return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8);
}
public int ig2add() {
return ((buf.get() - 128) & 0xFF) + ((buf.get() & 0xFF) << 8);
}
public int g3() {
return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF);
}
public int ig3() {
return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8) + ((buf.get() & 0xFF) << 16);
}
public int g4() {
return ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF);
}
public int ig4() {
return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8) + ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24);
}
public int m4() {
return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8);
}
public int im4() {
return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16);
}
public long g8() {
long low = (long) this.g4() & 0xFFFFFFFFL;
long high = (long) this.g4() & 0xFFFFFFFFL;
return high + (low << 32);
}
/** /**
* @param val * @param val
* @return * @return

View file

@ -25,7 +25,7 @@ import core.game.system.task.Pulse
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.map.RegionManager import core.game.world.map.RegionManager
import core.game.world.update.flag.context.ChatMessage import core.game.world.update.flag.context.ChatMessage
import core.game.world.update.flag.player.ChatFlag import core.game.world.update.flag.*
import core.net.amsc.MSPacketRepository import core.net.amsc.MSPacketRepository
import core.net.packet.context.PlayerContext import core.net.packet.context.PlayerContext
import core.net.packet.out.ClearMinimapFlag import core.net.packet.out.ClearMinimapFlag
@ -229,7 +229,7 @@ object PacketProcessor {
} }
PlayerMonitor.logChat(pkt.player, "public", pkt.message) PlayerMonitor.logChat(pkt.player, "public", pkt.message)
val ctx = ChatMessage(pkt.player, pkt.message, pkt.effects, pkt.message.length) val ctx = ChatMessage(pkt.player, pkt.message, pkt.effects, pkt.message.length)
pkt.player.updateMasks.register(ChatFlag(ctx)) pkt.player.updateMasks.register(EntityFlag.Chat, ctx)
} }
} }
is Packet.ChatSetting -> { is Packet.ChatSetting -> {

View file

@ -11,7 +11,7 @@ import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.system.task.Pulse import core.game.system.task.Pulse
import core.game.world.update.flag.context.ChatMessage import core.game.world.update.flag.context.ChatMessage
import core.game.world.update.flag.player.ChatFlag import core.game.world.update.flag.*
import proto.management.ClanMessage import proto.management.ClanMessage
import core.game.world.GameWorld.Pulser import core.game.world.GameWorld.Pulser
import core.net.packet.`in`.QCPacketType import core.net.packet.`in`.QCPacketType
@ -54,7 +54,7 @@ object QCRepository {
ctx.isQuickChat = true ctx.isQuickChat = true
Pulser.submit(object : Pulse(0, player) { Pulser.submit(object : Pulse(0, player) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
player.updateMasks.register(ChatFlag(ctx)) player.updateMasks.register(EntityFlag.Chat, ctx)
return true return true
} }
}) })

View file

@ -2,6 +2,7 @@ package core.tools
const val tick = 600 //ms const val tick = 600 //ms
const val second = 1000 //ms const val second = 1000 //ms
const val cycle = 20 //ms
fun secondsToTicks(seconds: Int): Int { fun secondsToTicks(seconds: Int): Int {
val seconds = seconds * second //seconds -> ms val seconds = seconds * second //seconds -> ms
@ -13,6 +14,11 @@ fun ticksToSeconds(ticks: Int): Int {
return ticksMs / 1000 return ticksMs / 1000
} }
fun cyclesToTicks (cycles: Int) : Int {
val cyclesPerTick = tick / cycle
return kotlin.math.ceil (cycles / cyclesPerTick.toDouble()).toInt()
}
fun minutesToTicks(minutes: Int): Int { fun minutesToTicks(minutes: Int): Int {
val minutesMs = minutes * 60 * 1000 val minutesMs = minutes * 60 * 1000
return minutesMs / tick return minutesMs / tick