Skip to content

Instantly share code, notes, and snippets.

@OmarAssadi
Last active July 5, 2021 15:27
Show Gist options
  • Save OmarAssadi/cf0dca7a0f82da737f6aad74f7c7879b to your computer and use it in GitHub Desktop.
Save OmarAssadi/cf0dca7a0f82da737f6aad74f7c7879b to your computer and use it in GitHub Desktop.
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