Last active
July 5, 2021 15:27
-
-
Save OmarAssadi/cf0dca7a0f82da737f6aad74f7c7879b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import io.netty.buffer.ByteBuf; | |
import io.netty.buffer.Unpooled; | |
import org.apache.commons.collections4.map.LRUMap; | |
import java.io.IOException; | |
import java.nio.file.Files; | |
import java.nio.file.Paths; | |
import java.util.*; | |
import java.util.stream.Collectors; | |
public class Component { | |
/** | |
* A layer, type 0, is an component which serves as a container for other components. | |
* A layer may contain [0,N] child components of any type. | |
*/ | |
public static final int TYPE_LAYER = 0; | |
public static final int TYPE_MODEL_LIST = 1; | |
public static final int TYPE_INVENTORY = 2; | |
public static final int TYPE_RECT = 3; | |
public static final int TYPE_TEXT = 4; | |
public static final int TYPE_GRAPHIC = 5; | |
public static final int TYPE_MODEL = 6; | |
public static final int TYPE_INVENTORY_TEXT = 7; | |
public static final int TYPE_TOOLTIP = 8; | |
public static final int NO_BUTTON = 0; | |
/** | |
* Represents a standard button. No special client-side functionality. | |
*/ | |
public static final int BUTTON = 1; | |
/** | |
* Represents a "target" button. Upon being clicked, a target button will | |
* allow the user to "use" the button on something, such as another player, | |
* NPC, location, or object. | |
* <p> | |
* Example usages would be the buttons representing spells in the magic tab. | |
*/ | |
public static final int TARGET_BUTTON = 2; | |
/** | |
* Represents a "close" button. Upon being clicked, a close button will simply | |
* close the current mainmodal, specified by {@link Game#mainmodalInterfaceId}. | |
*/ | |
public static final int CLOSE_BUTTON = 3; | |
/** | |
* Represents a "toggle" button. Upon being clicked, a toggle button will set the value of | |
* a varp to be {@code 1 - x} with x being the current value of the varp. | |
* <p> | |
* Example usages would be a 0/1 (off/on) toggle button, such as a checkbox. | |
* | |
* @see <a href="https://www.rune-server.ee/runescape-development/rs2-client/tutorials/697395-making-toggle-buttons-checkboxes-etc.html">making checkboxes (#317)</a> | |
*/ | |
public static final int TOGGLE_BUTTON = 4; | |
/** | |
* Represents a "select" button. Upon being clicked, a select button will set the value of | |
* a varp to be that of the associated {@link #scriptCompareValue}. | |
* <p> | |
* Example usages include things like radio buttons. | |
* | |
* @see <a href="https://www.rune-server.ee/runescape-development/rs2-client/tutorials/698022-making-radio-buttons.html">creating radio buttons (317)</a>. | |
*/ | |
public static final int SELECT_BUTTON = 5; | |
/** | |
* Represents a pause button. This is used for the "click to continue" dialogue messages. | |
* Upon being clicked, a pause button will send a packet to the server, consisting of | |
* the component id, indicating that the dialogue option has been clicked. | |
*/ | |
public static final int PAUSE_BUTTON = 6; | |
public static final LRUMap<Long, Model> modelCache = new LRUMap<>(30); | |
public static LRUMap<Long, Image24> imageCache; | |
public static Component[] instances; | |
public Image24 spriteDisabled; | |
public int seqCycle; | |
/** | |
* This is used by {@link #TYPE_INVENTORY} components for slot placeholder sprites. | |
* When an inventory is drawn, for each empty slot up to 20, the associated sprite will be drawn. | |
*/ | |
public Image24[] sprite; | |
/** | |
* An unused integer found in the decoder for {@link #TYPE_MODEL_LIST} type components. | |
* This is decoded as a 16-bit integer. | |
* <p> | |
* Although it is unused, we keep this in order to re-encode the original components losslessly. | |
*/ | |
public int unusedInt; | |
/** | |
* An array of integers consisting of the "expected" or "required" values for a given CS1 script. This is used | |
* with {@link #scriptCompareType} and {@link #script} by {@link Game#getComponentScriptState(Component)} to | |
* determine the state of an component ("disabled" or default / "enabled" or secondary). | |
*/ | |
public int[] scriptCompareValue; | |
/** | |
* The opcode used for non-standard functionality. This should be when adding custom functionality to a | |
* given component rather than adding hard-coded checks based on the ID of the component. | |
* <p> | |
* See {@link Game#updateComponentContent(Component)} and {@link Game#handleComponentAction(Component)} for | |
* examples. | |
*/ | |
public int clientCode; | |
public int[] slotX; | |
/** | |
* This value determines the color of a component (e.g., rectangle color, text color, etc) | |
* when in an "enabled" (secondary, non-default) state and when the user's mouse within the bounds of | |
* the component. | |
* <p> | |
* States are toggled via "CS1" scripts - see {@link #script} and {@link Game#getComponentScriptState}. | |
* <p> | |
* Note: the boundaries are determined by a combination of the {@link #width}, {@link #height}, {@link #x}, | |
* and {@link #y} fields. This may lead to confusion due to the fact that even components with unset/default (0) | |
* 'width' or 'height' fields will still draw "properly" (i.e., the 'message' text will not be cutoff vertically | |
* or horizontally), yet the 'hover' color will never end up being set. | |
*/ | |
public int hoverColorDisabled; | |
/** | |
* The option (button) type. This determines what sort of action should occur when the component is clicked. | |
*/ | |
public int optionType; | |
public String optionBase; | |
/** | |
* This value determines the color of a component (e.g., rectangle color, text color, etc) | |
* when in an "enabled" (secondary, non-default) state. | |
* <p> | |
* States are toggled via "CS1" scripts - see {@link #script} and {@link Game#getComponentScriptState}. | |
*/ | |
public int colorEnabled; | |
/** | |
* The width of the component. | |
*/ | |
public int width; | |
/** | |
* The associated option ("tooltip") message for the component. This is the text that is typically displayed | |
* in the top-left of the viewport when hovering over an component. | |
*/ | |
public String option; | |
/** | |
* The circumfix message for {@link #optionBase} messages, consisting of two strings separated by a space. | |
* <p> | |
* This is typically used for things like spells. An example circumfix would be "Cast on", which, | |
* given the example {@link #optionBase} "Wind strike", would be exploded at the delimiter into | |
* "Cast Wind strike on". | |
*/ | |
public String optionCircumfix; | |
/** | |
* Used by {@link #TYPE_TEXT}, {@link #TYPE_INVENTORY_TEXT}, and {@link #TYPE_MODEL_LIST} components | |
* to determine whether or not the text should be "centered". | |
* <p> | |
* If this is set to {@code true}, the text will start drawing at {@link #x} minus the width of the text / 2. | |
*/ | |
public boolean center; | |
/** | |
* The current Y of a scrolling container. The component should begin drawing the contents at the | |
* given inner height. | |
*/ | |
public int scrollY; | |
public String[] options; | |
/** | |
* A two-dimensional array containing CS1 scripts and their instructions. | |
* <p> | |
* See: {@link Game#getComponentScriptState(Component)} and {@link Game#getComponentValue(Component, int)} | |
*/ | |
public int[][] script; | |
/** | |
* Whether or not this component should be filled. As an example, this can be set to {@code true} | |
* on {@link #TYPE_RECT} components in order to draw them as filled rectangles rather than | |
* just outlines. | |
*/ | |
public boolean fill; | |
/** | |
* The message (text) associated with the component when in an "enabled" (secondary, non-default) state. | |
* <p> | |
* States are toggled via "CS1" scripts - see {@link #script} and {@link Game#getComponentScriptState}. | |
*/ | |
public String messageEnabled; | |
/** | |
* The layer that this component should display upon mouse over. This is typically used for stuff like hover | |
* effects on buttons. | |
* | |
* @see <a href="https://www.rune-server.ee/runescape-development/rs2-client/tutorials/697994-making-hover-buttons-standard-317-jagex-way.html">making hover buttons (317)</a> | |
*/ | |
public int mouseoverLayer; | |
public int slotMarginX; | |
/** | |
* This value determines the color of a component (e.g., rectangle color, text color, etc) | |
* when in a "disabled" (primary, default) state. | |
* <p> | |
* States are toggled via "CS1" scripts - see {@link #script} and {@link Game#getComponentScriptState}. | |
*/ | |
public int colorDisabled; | |
public int modelTypeDisabled; | |
public int modelDisabled; | |
/** | |
* When <code>true</code> moving an <code>Obj</code> from one slot to another will simply erase the item from the | |
* destination slot. | |
*/ | |
public boolean slotMoveReplaces; | |
/** | |
* The layer ("parent") this component belongs to. | |
*/ | |
public int layer; | |
public int optionFlags; | |
/** | |
* This value determines the color of a component (e.g., rectangle color, text color, etc) | |
* when in a "disabled" (primary, default) state and when the user's mouse within the bounds of | |
* the component. | |
* <p> | |
* States are toggled via "CS1" scripts - see {@link #script} and {@link Game#getComponentScriptState}. | |
* <p> | |
* Note: the boundaries are determined by a combination of the {@link #width}, {@link #height}, {@link #x}, | |
* and {@link #y} fields. This may lead to confusion due to the fact that even components with unset/default (0) | |
* 'width' or 'height' fields will still draw "properly" (i.e., the 'message' text will not be cutoff vertically | |
* or horizontally), yet the 'hover' color will never end up being set. | |
*/ | |
public int hoverColorEnabled; | |
/** | |
* An array containing the indices ("IDs") of the component's children in the component cache | |
* (see {@link #instances}. | |
*/ | |
public int[] children; | |
/** | |
* The X positions, relative to this component, of each child in the children array (see {@link #children}. | |
*/ | |
public int[] childX; | |
public boolean slotsUsable; | |
public BitmapFont font; | |
public int slotMarginY; | |
public int[] scriptCompareType; | |
public int seqFrame; | |
public int[] slotY; | |
/** | |
* The message (text) associated with the component when in a "disabled" (primary, default) state. | |
* <p> | |
* States are toggled via "CS1" scripts - see {@link #script} and {@link Game#getComponentScriptState}. | |
*/ | |
public String messageDisabled; | |
public boolean slotsHaveOptions; | |
/** | |
* The ID of the component and index in {@link #instances}. This should be unique and should fit into the space | |
* of a 16-bit unsigned integer [0, 65535]. | |
*/ | |
public int id; | |
/** | |
* An unused boolean found in the decoder for {@link #TYPE_MODEL_LIST} type components. | |
* This is decoded as an 8-bit integer. | |
* <p> | |
* Although it is unused, we keep this in order to re-encode the original components losslessly. | |
*/ | |
public boolean unusedBool; | |
/** | |
* An array containing the stack size of the item in the slot. | |
* <p> | |
* This is used for inventory components, such as the player inventory, | |
* equipment tab, bank, etc. And these IDs represent an <code>Obj</code>. | |
* <p> | |
* See: {@link ObjType} | |
*/ | |
public int[] slotAmount; | |
/** | |
* An array containing IDs associated with the given component slot. | |
* <p> | |
* This is used for inventory components, such as the player inventory, | |
* equipment tab, bank, etc. And these IDs represent an <code>Obj</code>. | |
* <p> | |
* See: {@link ObjType} | |
*/ | |
public int[] slotId; | |
/** | |
* The alpha channel. Alpha determines how opaque an component is and is represented as an | |
* unsigned 8-bit integer, with 0 being fully opaque and 255 being fully transparent. | |
* <p> | |
* The alpha channel is only used by {@link #TYPE_RECT} components. | |
*/ | |
public byte alpha; | |
public int modelTypeEnabled; | |
public int modelEnabled; | |
public int seqId; | |
public int scriptSeqId; | |
public boolean slotsDraggable; | |
public Image24 spriteEnabled; | |
/** | |
* The "true" height of a scrolling layer. If this number is greater than the {@link #height}, | |
* a vertical scrollbar will be drawn. | |
*/ | |
public int scrollHeight; | |
/** | |
* The type of this component (e.g., rect, graphic, layer, etc). | |
*/ | |
public int type; | |
/** | |
* The relative X position of the component, starting from 0,0 in the parent container. | |
*/ | |
public int x; | |
/** | |
* The relative Y position of the component, starting from 0,0 in the parent container. | |
*/ | |
public int y; | |
/** | |
* {@code true} to hide an component until it is hovered over. This is only used by | |
* {@link #TYPE_LAYER} components. | |
* <p> | |
* Related: {@link #mouseoverLayer}. | |
*/ | |
public boolean hidden; | |
/** | |
* The drawn height of the component. | |
*/ | |
public int height; | |
/** | |
* Used by {@link #TYPE_TEXT}, {@link #TYPE_INVENTORY_TEXT}, and {@link #TYPE_MODEL_LIST} components | |
* to determine whether or not the text should have a drop-shadow. | |
*/ | |
public boolean shadow; | |
public int modelZoom; | |
public int modelCameraPitch; | |
public int modelYaw; | |
public int[] childY; | |
/** | |
* Note: non-standard; this is not found in the original 317 component class. This is used to preserve the | |
* original index in the array of the associated font for use when re-encoding components. | |
*/ | |
private int fontId; | |
public Component() { | |
} | |
public static void unpack(final FileArchive componentArchive, final BitmapFont[] font, final FileArchive mediaArchive) throws IOException { | |
imageCache = new LRUMap<>(500); | |
final Buffer buffer = new Buffer(componentArchive.read("data")); | |
int parentId = -1; | |
final int size = buffer.get2U(); | |
instances = new Component[size]; | |
while (buffer.position < buffer.data.length) { | |
int id = buffer.get2U(); | |
if (id == 0xFFFF) { | |
parentId = buffer.get2U(); | |
id = buffer.get2U(); | |
} | |
final Component component = instances[id] = new Component(); | |
component.id = id; | |
component.layer = parentId; | |
component.type = buffer.get1U(); | |
component.optionType = buffer.get1U(); | |
component.clientCode = buffer.get2U(); | |
component.width = buffer.get2U(); | |
component.height = buffer.get2U(); | |
component.alpha = (byte) buffer.get1U(); | |
component.mouseoverLayer = buffer.get1U(); | |
if (component.mouseoverLayer == 0) { | |
component.mouseoverLayer = -1; | |
} else { | |
component.mouseoverLayer = (component.mouseoverLayer - 1 << 8) + buffer.get1U(); | |
} | |
final int comparators = buffer.get1U(); | |
if (comparators > 0) { | |
component.scriptCompareType = new int[comparators]; | |
component.scriptCompareValue = new int[comparators]; | |
for (int comparator = 0; comparator < comparators; comparator++) { | |
component.scriptCompareType[comparator] = buffer.get1U(); | |
component.scriptCompareValue[comparator] = buffer.get2U(); | |
} | |
} | |
final int scripts = buffer.get1U(); | |
if (scripts > 0) { | |
component.script = new int[scripts][]; | |
for (int script = 0; script < scripts; script++) { | |
final int instructions = buffer.get2U(); | |
component.script[script] = new int[instructions]; | |
for (int instruction = 0; instruction < instructions; instruction++) { | |
component.script[script][instruction] = buffer.get2U(); | |
} | |
} | |
} | |
if (component.type == TYPE_LAYER) { | |
component.scrollHeight = buffer.get2U(); | |
component.hidden = buffer.get1U() == 1; | |
final int children = buffer.get2U(); | |
component.children = new int[children]; | |
component.childX = new int[children]; | |
component.childY = new int[children]; | |
for (int child = 0; child < children; child++) { | |
component.children[child] = buffer.get2U(); | |
component.childX[child] = buffer.get2(); | |
component.childY[child] = buffer.get2(); | |
} | |
} | |
if (component.type == TYPE_MODEL_LIST) { | |
component.unusedInt = buffer.get2U(); | |
component.unusedBool = buffer.get1U() == 1; | |
} | |
if (component.type == TYPE_INVENTORY) { | |
component.slotId = new int[component.width * component.height]; | |
component.slotAmount = new int[component.width * component.height]; | |
component.slotsDraggable = buffer.get1U() == 1; | |
component.slotsHaveOptions = buffer.get1U() == 1; | |
component.slotsUsable = buffer.get1U() == 1; | |
component.slotMoveReplaces = buffer.get1U() == 1; | |
component.slotMarginX = buffer.get1U(); | |
component.slotMarginY = buffer.get1U(); | |
component.slotX = new int[20]; | |
component.slotY = new int[20]; | |
component.sprite = new Image24[20]; | |
for (int slot = 0; slot < 20; slot++) { | |
if (buffer.get1U() == 1) { | |
component.slotX[slot] = buffer.get2(); | |
component.slotY[slot] = buffer.get2(); | |
final String sprite = buffer.getString(); | |
if (mediaArchive != null && !sprite.isEmpty()) { | |
final int spriteId = sprite.lastIndexOf(","); | |
component.sprite[slot] = getSprite(Integer.parseInt(sprite.substring(spriteId + 1)), mediaArchive, sprite.substring(0, spriteId)); | |
} | |
} | |
} | |
component.options = new String[5]; | |
for (int option = 0; option < 5; option++) { | |
component.options[option] = buffer.getString(); | |
if (component.options[option].isEmpty()) { | |
component.options[option] = null; | |
} | |
} | |
} | |
if (component.type == TYPE_RECT) { | |
component.fill = buffer.get1U() == 1; | |
} | |
if (component.type == TYPE_TEXT || component.type == TYPE_MODEL_LIST) { | |
component.center = buffer.get1U() == 1; | |
component.fontId = buffer.get1U(); | |
if (font != null) { | |
component.font = font[component.fontId]; | |
} | |
component.shadow = buffer.get1U() == 1; | |
} | |
if (component.type == TYPE_TEXT) { | |
component.messageDisabled = buffer.getString(); | |
component.messageEnabled = buffer.getString(); | |
} | |
if (component.type == TYPE_MODEL_LIST || component.type == TYPE_RECT || component.type == TYPE_TEXT) { | |
component.colorDisabled = buffer.get4(); | |
} | |
if (component.type == TYPE_RECT || component.type == TYPE_TEXT) { | |
component.colorEnabled = buffer.get4(); | |
component.hoverColorDisabled = buffer.get4(); | |
component.hoverColorEnabled = buffer.get4(); | |
} | |
if (component.type == TYPE_GRAPHIC) { | |
String sprite = buffer.getString(); | |
if (mediaArchive != null && sprite.length() > 0) { | |
final int spriteId = sprite.lastIndexOf(","); | |
component.spriteDisabled = getSprite(Integer.parseInt(sprite.substring(spriteId + 1)), mediaArchive, sprite.substring(0, spriteId)); | |
} | |
sprite = buffer.getString(); | |
if (mediaArchive != null && sprite.length() > 0) { | |
final int spriteId = sprite.lastIndexOf(","); | |
component.spriteEnabled = getSprite(Integer.parseInt(sprite.substring(spriteId + 1)), mediaArchive, sprite.substring(0, spriteId)); | |
} | |
} | |
if (component.type == TYPE_MODEL) { | |
int content = buffer.get1U(); | |
if (content != 0) { | |
component.modelTypeDisabled = 1; | |
component.modelDisabled = (content - 1 << 8) + buffer.get1U(); | |
} | |
content = buffer.get1U(); | |
if (content != 0) { | |
component.modelTypeEnabled = 1; | |
component.modelEnabled = (content - 1 << 8) + buffer.get1U(); | |
} | |
content = buffer.get1U(); | |
if (content == 0) { | |
component.seqId = -1; | |
} else { | |
component.seqId = (content - 1 << 8) + buffer.get1U(); | |
} | |
content = buffer.get1U(); | |
if (content == 0) { | |
component.scriptSeqId = -1; | |
} else { | |
component.scriptSeqId = (content - 1 << 8) + buffer.get1U(); | |
} | |
component.modelZoom = buffer.get2U(); | |
component.modelCameraPitch = buffer.get2U(); | |
component.modelYaw = buffer.get2U(); | |
} | |
if (component.type == TYPE_INVENTORY_TEXT) { | |
component.slotId = new int[component.width * component.height]; | |
component.slotAmount = new int[component.width * component.height]; | |
component.center = buffer.get1U() == 1; | |
component.fontId = buffer.get1U(); | |
if (font != null) { | |
component.font = font[component.fontId]; | |
} | |
component.shadow = buffer.get1U() == 1; | |
component.colorDisabled = buffer.get4(); | |
component.slotMarginX = buffer.get2(); | |
component.slotMarginY = buffer.get2(); | |
component.slotsHaveOptions = buffer.get1U() == 1; | |
component.options = new String[5]; | |
for (int option = 0; option < 5; option++) { | |
component.options[option] = buffer.getString(); | |
if (component.options[option].isEmpty()) { | |
component.options[option] = null; | |
} | |
} | |
} | |
if (component.type == TYPE_TOOLTIP) { | |
component.messageDisabled = buffer.getString(); | |
} | |
if (component.optionType == TARGET_BUTTON || component.type == TYPE_INVENTORY) { | |
component.optionCircumfix = buffer.getString(); | |
component.optionBase = buffer.getString(); | |
component.optionFlags = buffer.get2U(); | |
} | |
if (component.optionType == BUTTON || component.optionType == TOGGLE_BUTTON || component.optionType == SELECT_BUTTON || component.optionType == PAUSE_BUTTON) { | |
component.option = buffer.getString(); | |
if (component.option.isEmpty()) { | |
if (component.optionType == BUTTON) { | |
component.option = "Ok"; | |
} | |
if (component.optionType == TOGGLE_BUTTON) { | |
component.option = "Select"; | |
} | |
if (component.optionType == SELECT_BUTTON) { | |
component.option = "Select"; | |
} | |
if (component.optionType == PAUSE_BUTTON) { | |
component.option = "Continue"; | |
} | |
} | |
} | |
System.out.println(component.id + " wtf " + component.type ); | |
} | |
imageCache = null; | |
} | |
public static Image24 getSprite(final int index, final FileArchive archive, final String group) { | |
final long key = (StringUtil.hashCode(group) << 8) + (long) index; | |
Image24 image = imageCache.get(key); | |
if (image != null) { | |
return image; | |
} | |
try { | |
image = new Image24(archive, group, index); | |
imageCache.put(key, image); | |
} catch (final Exception e) { | |
return null; | |
} | |
return image; | |
} | |
public static void clearAndReplaceModels(final int index, final int type, final Model model) { | |
modelCache.clear(); | |
if (model != null && type != 4) { | |
modelCache.put(((long) type << 16) + index, model); | |
} | |
} | |
public List<Component> getChildren() { | |
return Arrays.stream(children).filter(id -> id < Component.instances.length).mapToObj(id -> Component.instances[id]) | |
.filter(Objects::nonNull).collect(Collectors.toList()); | |
} | |
public List<Component> getComponents(final boolean includeHead) { | |
final List<Component> visited = new LinkedList<>(); | |
final Stack<Component> stack = new Stack<>(); | |
stack.push(this); | |
while (!stack.empty()) { | |
final Component component = stack.pop(); | |
stack.addAll(component.getChildren()); | |
visited.add(component); | |
} | |
if (!includeHead) { | |
visited.remove(this); | |
} | |
return visited; | |
} | |
public List<Component> getComponents() { | |
return getComponents(true); | |
} | |
public byte[] encode() { | |
final ByteBuf buffer = Unpooled.buffer(); | |
if (layer != -1) { | |
buffer.writeShort(0xFFFF); | |
buffer.writeShort(layer); | |
} | |
buffer.writeShort(id); | |
buffer.writeByte(type); | |
buffer.writeByte(optionType); | |
buffer.writeShort(clientCode); | |
buffer.writeShort(width); | |
buffer.writeShort(height); | |
buffer.writeByte(alpha); | |
if (mouseoverLayer == -1) { | |
buffer.writeByte(0); | |
} else { | |
buffer.writeByte((mouseoverLayer >> 8) + 1); | |
buffer.writeByte(mouseoverLayer & 0xFF); | |
} | |
if (scriptCompareType == null) { | |
buffer.writeByte(0); | |
} else { | |
final int operators = scriptCompareType.length; | |
buffer.writeByte(operators); | |
for (int i = 0; i < operators; i++) { | |
buffer.writeByte(scriptCompareType[i]); | |
buffer.writeShort(scriptCompareValue[i]); | |
} | |
} | |
if (script == null) { | |
buffer.writeByte(0); | |
} else { | |
final int scriptCount = script.length; | |
buffer.writeByte(scriptCount); | |
for (final int[] opcodes : script) { | |
final int instructionCount = opcodes.length; | |
buffer.writeShort(instructionCount); | |
for (final int opcode : opcodes) { | |
buffer.writeShort(opcode); | |
} | |
} | |
} | |
if (type == TYPE_LAYER) { // 0 | |
buffer.writeShort(scrollHeight); | |
buffer.writeBoolean(hidden); | |
buffer.writeShort(children != null ? children.length : 0); | |
if (children != null) { | |
for (int i = 0; i < children.length; i++) { | |
buffer.writeShort(children[i]); | |
buffer.writeShort(childX[i]); | |
buffer.writeShort(childY[i]); | |
} | |
} | |
} | |
if (type == TYPE_MODEL_LIST) { // 1 | |
buffer.writeShort(unusedInt); | |
buffer.writeBoolean(unusedBool); | |
} | |
if (type == TYPE_INVENTORY) { // 2 | |
buffer.writeBoolean(slotsDraggable); | |
buffer.writeBoolean(slotsHaveOptions); | |
buffer.writeBoolean(slotsUsable); | |
buffer.writeBoolean(slotMoveReplaces); | |
buffer.writeByte(slotMarginX); | |
buffer.writeByte(slotMarginY); | |
for (int i = 0; i < 20; i++) { | |
final boolean enabled = sprite[i] != null; | |
buffer.writeBoolean(enabled); | |
if (enabled) { | |
buffer.writeShort(slotX[i]); | |
buffer.writeShort(slotY[i]); | |
ByteBufUtil.writeJagexString(buffer, String.format("%s,%d", sprite[i].getGroup(), sprite[i].getId())); | |
} | |
} | |
Arrays.stream(options != null ? options : new String[5]).forEach(option -> ByteBufUtil.writeJagexString(buffer, option != null ? option : "")); | |
} | |
if (type == TYPE_RECT) { // 3 | |
buffer.writeBoolean(fill); | |
} | |
if (type == TYPE_TEXT || type == TYPE_MODEL_LIST) { // 4 || 1 | |
buffer.writeBoolean(center); | |
buffer.writeByte(fontId); | |
buffer.writeBoolean(shadow); | |
} | |
if (type == TYPE_TEXT) { | |
ByteBufUtil.writeJagexString(buffer, messageDisabled != null ? messageDisabled : ""); | |
ByteBufUtil.writeJagexString(buffer, messageEnabled != null ? messageEnabled : ""); | |
} | |
if (type == TYPE_MODEL_LIST || type == TYPE_RECT || type == TYPE_TEXT) { // 1 || 3 || 4 | |
buffer.writeInt(colorDisabled); | |
} | |
if (type == TYPE_RECT || type == TYPE_TEXT) { // 3 || 4 | |
buffer.writeInt(colorEnabled); | |
buffer.writeInt(hoverColorDisabled); | |
buffer.writeInt(hoverColorEnabled); | |
} | |
if (type == TYPE_GRAPHIC) { // 5 | |
if (spriteDisabled == null) { | |
ByteBufUtil.writeJagexString(buffer, ""); | |
} else { | |
ByteBufUtil.writeJagexString(buffer, String.format("%s,%d", spriteDisabled.getGroup(), spriteDisabled.getId())); | |
} | |
if (spriteEnabled == null) { | |
ByteBufUtil.writeJagexString(buffer, ""); | |
} else { | |
ByteBufUtil.writeJagexString(buffer, String.format("%s,%d", spriteEnabled.getGroup(), spriteEnabled.getId())); | |
} | |
} | |
if (type == TYPE_MODEL) { // 6 | |
if (modelTypeDisabled == 1) { | |
buffer.writeByte((modelDisabled >> 8) + 1); | |
buffer.writeByte(modelDisabled & 0xFF); | |
} else { | |
buffer.writeByte(0); | |
} | |
if (modelTypeEnabled == 1) { | |
buffer.writeByte((modelEnabled >> 8) + 1); | |
buffer.writeByte(modelEnabled & 0xFF); | |
} else { | |
buffer.writeByte(0); | |
} | |
if (seqId == -1) { | |
buffer.writeByte(0); | |
} else { | |
buffer.writeByte((seqId >> 8) + 1); | |
buffer.writeByte(seqId & 0xFF); | |
} | |
if (scriptSeqId == -1) { | |
buffer.writeByte(0); | |
} else { | |
buffer.writeByte((scriptSeqId >> 8) + 1); | |
buffer.writeByte(scriptSeqId & 0xFF); | |
} | |
buffer.writeShort(modelZoom); | |
buffer.writeShort(modelCameraPitch); | |
buffer.writeShort(modelYaw); | |
} | |
if (type == TYPE_INVENTORY_TEXT) { // 7 | |
buffer.writeBoolean(center); | |
buffer.writeByte(fontId); | |
buffer.writeBoolean(shadow); | |
buffer.writeInt(colorDisabled); | |
buffer.writeShort(slotMarginX); | |
buffer.writeShort(slotMarginY); | |
buffer.writeBoolean(slotsHaveOptions); | |
Arrays.stream(options).forEach(option -> ByteBufUtil.writeJagexString(buffer, option != null ? option : "")); | |
} | |
if (optionType == TARGET_BUTTON || type == TYPE_INVENTORY) { // 2 || 2 | |
ByteBufUtil.writeJagexString(buffer, optionCircumfix != null ? optionCircumfix : ""); | |
ByteBufUtil.writeJagexString(buffer, optionBase != null ? optionBase : ""); | |
buffer.writeShort(optionFlags); | |
} | |
if (type == TYPE_TOOLTIP) { // 8 | |
ByteBufUtil.writeJagexString(buffer, messageDisabled); | |
} | |
if (optionType == BUTTON || optionType == TOGGLE_BUTTON || optionType == SELECT_BUTTON || optionType == PAUSE_BUTTON) { | |
if (option.equals("Ok") && optionType == BUTTON | |
|| option.equals("Select") && (optionType == TOGGLE_BUTTON || optionType == SELECT_BUTTON) | |
|| option.equals("Continue") && optionType == PAUSE_BUTTON) { | |
ByteBufUtil.writeJagexString(buffer, ""); | |
} else { | |
ByteBufUtil.writeJagexString(buffer, option); | |
} | |
} | |
final byte[] component = new byte[buffer.readableBytes()]; | |
buffer.readBytes(component); | |
return component; | |
} | |
public void swapSlots(final int src, final int dst) { | |
int tmp = slotId[src]; | |
slotId[src] = slotId[dst]; | |
slotId[dst] = tmp; | |
tmp = slotAmount[src]; | |
slotAmount[src] = slotAmount[dst]; | |
slotAmount[dst] = tmp; | |
} | |
public Model getModel(final int type, final int index) { | |
Model model = modelCache.get(((long) type << 16) + index); | |
if (model != null) { | |
return model; | |
} | |
if (type == 1) { | |
model = Model.tryGet(index); | |
} | |
if (type == 2) { | |
model = NPCType.get(index).method160(); | |
} | |
if (type == 3) { | |
model = Game.self.method453(); | |
} | |
if (type == 4) { | |
model = ObjType.get(index).method202(50); | |
} | |
if (model != null) { | |
modelCache.put((long) ((type << 16) + index), model); | |
} | |
return model; | |
} | |
public Model getAnimatedModel(final int primaryFrame, final int secondaryFrame, final boolean flag) { | |
final Model model; | |
if (flag) { | |
model = getModel(modelTypeEnabled, modelEnabled); | |
} else { | |
model = getModel(modelTypeDisabled, modelDisabled); | |
} | |
if (model == null) { | |
return null; | |
} | |
if (secondaryFrame == -1 && primaryFrame == -1 && model.faceColor == null) { | |
return model; | |
} | |
final Model animated = new Model(true, SeqFrame.isNull(secondaryFrame) & SeqFrame.isNull(primaryFrame), false, model); | |
if (secondaryFrame != -1 || primaryFrame != -1) { | |
animated.createLabelReferences(); | |
} | |
if (secondaryFrame != -1) { | |
animated.applySequenceFrame(secondaryFrame); | |
} | |
if (primaryFrame != -1) { | |
animated.applySequenceFrame(primaryFrame); | |
} | |
animated.addLighting(64, 768, -50, -10, -50, true); | |
return animated; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment