Add npc and object definition decoding.

This commit is contained in:
Major-
2013-11-03 04:02:06 +00:00
parent e87addb8ed
commit c8b73c871e
17 changed files with 1409 additions and 76 deletions
@@ -1,4 +1,4 @@
package org.apollo.fs.parser;
package org.apollo.fs.decoder;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -9,35 +9,35 @@ import org.apollo.game.model.def.ItemDefinition;
import org.apollo.util.ByteBufferUtil;
/**
* A class which parses item definitions.
* Decodes item data from the {@code obj.dat} file into {@link ItemDefinition}s.
*
* @author Graham
*/
public final class ItemDefinitionParser {
public final class ItemDefinitionDecoder {
/**
* The indexed file system.
* The {@link IndexedFileSystem}.
*/
private final IndexedFileSystem fs;
/**
* Creates the item definition parser.
* Creates the item definition decoder.
*
* @param fs The indexed file system.
*/
public ItemDefinitionParser(IndexedFileSystem fs) {
public ItemDefinitionDecoder(IndexedFileSystem fs) {
this.fs = fs;
}
/**
* Parses the item definitions.
* Decodes the item definitions.
*
* @return The item definitions.
* @throws IOException If an I/O error occurs.
*/
public ItemDefinition[] parse() throws IOException {
public ItemDefinition[] decode() throws IOException {
Archive config = Archive.decode(fs.getFile(0, 2));
ByteBuffer dat = config.getEntry("obj.dat").getBuffer();
ByteBuffer data = config.getEntry("obj.dat").getBuffer();
ByteBuffer idx = config.getEntry("obj.idx").getBuffer();
int count = idx.getShort();
@@ -50,33 +50,33 @@ public final class ItemDefinitionParser {
ItemDefinition[] defs = new ItemDefinition[count];
for (int i = 0; i < count; i++) {
dat.position(indices[i]);
defs[i] = parseDefinition(i, dat);
data.position(indices[i]);
defs[i] = decode(i, data);
}
return defs;
}
/**
* Parses a single definition.
* Decodes a single definition.
*
* @param id The item's id.
* @param buffer The buffer.
* @return The definition.
* @return The {@link ItemDefinition}.
*/
private ItemDefinition parseDefinition(int id, ByteBuffer buffer) {
ItemDefinition def = new ItemDefinition(id);
private ItemDefinition decode(int id, ByteBuffer buffer) {
ItemDefinition definition = new ItemDefinition(id);
while (true) {
int code = buffer.get() & 0xFF;
if (code == 0) {
return def;
return definition;
} else if (code == 1) {
buffer.getShort();// & 0xFFFF; // model Id
} else if (code == 2) {
def.setName(ByteBufferUtil.readString(buffer));
definition.setName(ByteBufferUtil.readString(buffer));
} else if (code == 3) {
def.setDescription(ByteBufferUtil.readString(buffer));
definition.setDescription(ByteBufferUtil.readString(buffer));
} else if (code == 4) {
buffer.getShort();// & 0xFFFF; // sprite scale
} else if (code == 5) {
@@ -88,13 +88,13 @@ public final class ItemDefinitionParser {
} else if (code == 8) {
buffer.getShort(); // sprite dY
} else if (code == 10) {
buffer.getShort();// & 0xFFFF;
buffer.getShort();
} else if (code == 11) {
def.setStackable(true);
definition.setStackable(true);
} else if (code == 12) {
buffer.getInt(); // model height
} else if (code == 16) {
def.setMembersOnly(true);
definition.setMembersOnly(true);
} else if (code == 23) {
buffer.getShort(); // & 0xFFFF; //primaryMaleEquipModelId
buffer.get(); // maleEquipYTranslation
@@ -110,10 +110,10 @@ public final class ItemDefinitionParser {
if (str.equalsIgnoreCase("hidden")) {
str = null;
}
def.setGroundAction(code - 30, str);
definition.setGroundAction(code - 30, str);
} else if (code >= 35 && code < 40) {
String str = ByteBufferUtil.readString(buffer);
def.setInventoryAction(code - 35, str);
definition.setInventoryAction(code - 35, str);
} else if (code == 40) {
int colourCount = buffer.get() & 0xFF;
for (int i = 0; i < colourCount; i++) {
@@ -136,10 +136,10 @@ public final class ItemDefinitionParser {
buffer.getShort(); // & 0xFFFF; // spriteCameraYaw
} else if (code == 97) {
int noteInfoId = buffer.getShort() & 0xFFFF;
def.setNoteInfoId(noteInfoId);
definition.setNoteInfoId(noteInfoId);
} else if (code == 98) {
int noteGraphicId = buffer.getShort() & 0xFFFF;
def.setNoteGraphicId(noteGraphicId);
definition.setNoteGraphicId(noteGraphicId);
} else if (code >= 100 && code < 110) {
buffer.getShort(); // & 0xFFFF; // stack id
buffer.getShort(); // & 0xFFFF; // stack size
@@ -154,9 +154,9 @@ public final class ItemDefinitionParser {
} else if (code == 114) {
buffer.getShort(); // * 5; // light diffusion
} else if (code == 115) {
buffer.get(); // & 0xFF; // team
definition.setTeam(buffer.get() & 0xFF);
}
}
}
}
}
@@ -0,0 +1,171 @@
package org.apollo.fs.decoder;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apollo.fs.IndexedFileSystem;
import org.apollo.fs.archive.Archive;
import org.apollo.game.model.def.NpcDefinition;
import org.apollo.util.ByteBufferUtil;
/**
* Decodes npc data from the {@code npc.dat} file into {@link NpcDefinition}s.
*
* @author Major
*/
public final class NpcDefinitionDecoder {
/**
* The {@link IndexedFileSystem}.
*/
private final IndexedFileSystem fs;
/**
* Creates the npc definition decoder.
*
* @param fs The indexed file system.
*/
public NpcDefinitionDecoder(IndexedFileSystem fs) {
this.fs = fs;
}
/**
* Decodes the npc definitions.
*
* @return An array of all parsed npc definitions.
* @throws IOException If an I/O error occurs.
*/
public NpcDefinition[] decode() throws IOException {
Archive config = Archive.decode(fs.getFile(0, 2));
ByteBuffer data = config.getEntry("npc.dat").getBuffer();
ByteBuffer idx = config.getEntry("npc.idx").getBuffer();
int count = idx.getShort();
int[] indices = new int[count];
int index = 2;
for (int i = 0; i < count; i++) {
indices[i] = index;
index += idx.getShort();
}
NpcDefinition[] defs = new NpcDefinition[count];
for (int i = 0; i < count; i++) {
data.position(indices[i]);
defs[i] = decode(i, data);
}
return defs;
}
/**
* Decodes a single definition.
*
* @param id The npc's id.
* @param buffer The buffer.
* @return The {@link NpcDefinition}.
*/
private NpcDefinition decode(int id, ByteBuffer buffer) {
NpcDefinition definition = new NpcDefinition(id);
while (true) {
int code = buffer.get() & 0xFF;
if (code == 0) {
return definition;
} else if (code == 1) {
int length = buffer.get() & 0xFF;
int[] npcModels = new int[length];
for (int i = 0; i < length; i++) {
npcModels[i] = buffer.getShort();
}
} else if (code == 2) {
definition.setName(ByteBufferUtil.readString(buffer));
} else if (code == 3) {
definition.setDescription(ByteBufferUtil.readString(buffer));
} else if (code == 12) {
definition.setSize(buffer.get());
} else if (code == 13) {
definition.setStandAnimation(buffer.getShort());
} else if (code == 14) {
definition.setWalkAnimation(buffer.getShort());
} else if (code == 17) {
definition
.setWalkAnimations(buffer.getShort(), buffer.getShort(), buffer.getShort(), buffer.getShort());
} else if (code >= 30 && code < 40) {
String str = ByteBufferUtil.readString(buffer);
if (str.equals("hidden")) {
str = null;
}
definition.setInteraction(code - 30, str);
} else if (code == 40) {
int length = buffer.get() & 0xFF;
int[] originalColours = new int[length];
int[] replacementColours = new int[length];
for (int i = 0; i < length; i++) {
originalColours[i] = buffer.getShort();
replacementColours[i] = buffer.getShort();
}
} else if (code == 60) {
int length = buffer.get() & 0xFF;
int[] additionalModels = new int[length];
for (int i = 0; i < length; i++) {
additionalModels[i] = buffer.getShort();
}
} else if (code == 90) {
buffer.getShort(); // Dummy
} else if (code == 91) {
buffer.getShort(); // Dummy
} else if (code == 92) {
buffer.getShort(); // Dummy
} else if (code == 93) {
@SuppressWarnings("unused")
boolean drawMinimapDot = false;
} else if (code == 95) {
definition.setCombatLevel(buffer.getShort());
} else if (code == 97) {
@SuppressWarnings("unused")
int scaleXZ = buffer.getShort();
} else if (code == 98) {
@SuppressWarnings("unused")
int scaleY = buffer.getShort();
} else if (code == 99) {
@SuppressWarnings("unused")
boolean unknown = true;
} else if (code == 100) {
@SuppressWarnings("unused")
int lightModifier = buffer.get();
} else if (code == 101) {
@SuppressWarnings("unused")
int shadowModifier = buffer.get() * 5;
} else if (code == 102) {
@SuppressWarnings("unused")
int headIcon = buffer.getShort();
} else if (code == 103) {
@SuppressWarnings("unused")
int degreesToRotate = buffer.getShort();
} else if (code == 106) {
int morphVariableBitsIndex = buffer.getShort();
if (morphVariableBitsIndex == 65535) {
morphVariableBitsIndex = -1;
}
int morphismCount = buffer.getShort();
if (morphismCount == 65535) {
morphismCount = -1;
}
int count = buffer.get() & 0xFF;
int[] morphisms = new int[count + 1];
for (int i = 0; i <= count; i++) {
int morphism = buffer.getShort();
if (morphism == 65535) {
morphism = -1;
}
morphisms[i] = morphism;
}
} else if (code == 107) {
@SuppressWarnings("unused")
boolean clickable = false;
}
}
}
}
@@ -0,0 +1,161 @@
package org.apollo.fs.decoder;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apollo.fs.IndexedFileSystem;
import org.apollo.fs.archive.Archive;
import org.apollo.game.model.def.ObjectDefinition;
import org.apollo.util.ByteBufferUtil;
/**
* Decodes object data from the {@code loc.dat} file into {@link ObjectDefinition}s.
*
* @author Major
*/
public final class ObjectDefinitionDecoder {
/**
* The {@link IndexedFileSystem}.
*/
private final IndexedFileSystem fs;
/**
* Creates the decoder.
*
* @param fs The {@link IndexedFileSystem}.
*/
public ObjectDefinitionDecoder(IndexedFileSystem fs) {
this.fs = fs;
}
/**
* Decodes all of the data into {@link ObjectDefinition}s.
*
* @return The definitions.
* @throws IOException If an error occurs when decoding the archive or finding an entry.
*/
public ObjectDefinition[] decode() throws IOException {
Archive config = Archive.decode(fs.getFile(0, 2));
ByteBuffer data = config.getEntry("loc.dat").getBuffer();
ByteBuffer idx = config.getEntry("loc.idx").getBuffer();
int count = idx.getShort();
int[] indices = new int[count];
int index = 2;
for (int i = 0; i < count; i++) {
indices[i] = index;
index += idx.getShort();
}
ObjectDefinition[] defs = new ObjectDefinition[count];
for (int i = 0; i < count; i++) {
data.position(indices[i]);
defs[i] = decode(i, data);
}
return defs;
}
/**
* Decodes data from the cache into an {@link ObjectDefinition}.
*
* @param id The id of the object.
* @param data The {@link ByteBuffer} containing the data.
* @return The object definition.
*/
public ObjectDefinition decode(int id, ByteBuffer data) {
ObjectDefinition definition = new ObjectDefinition(id);
while (true) {
int opcode = data.get() & 0xFF;
if (opcode == 0) {
return definition;
} else if (opcode == 1) {
int amount = data.get() & 0xFF;
for (int i = 0; i < amount; i++) {
data.getShort(); // model id
data.get(); // model position
}
} else if (opcode == 2) {
definition.setName(ByteBufferUtil.readString(data));
} else if (opcode == 3) {
definition.setDescription(ByteBufferUtil.readString(data));
} else if (opcode == 5) {
int amount = data.get() & 0xFF;
for (int i = 0; i < amount; i++) {
data.getShort(); // model id
}
} else if (opcode == 14) {
definition.setWidth(data.get() & 0xFF);
} else if (opcode == 15) {
definition.setHeight(data.get() & 0xFF);
} else if (opcode == 17) {
definition.setSolid(false);
} else if (opcode == 18) {
definition.setImpenetrable(false);
} else if (opcode == 19) {
definition.setInteractive((data.get() & 0xFF) == 1);
} else if (opcode == 21) {
// boolean contouredGround = true;
} else if (opcode == 22) {
// boolean delayShading = true;
} else if (opcode == 23) {
// boolean occlues = true;
} else if (opcode == 24) {
data.getShort(); // animation
} else if (opcode == 28) {
data.get(); // decorDisplacement
} else if (opcode == 29) {
data.get(); // ambientLight
} else if (opcode >= 30 && opcode < 39) {
String[] actions = definition.getMenuActions();
if (actions == null) {
actions = new String[10];
}
String action = ByteBufferUtil.readString(data);
actions[opcode - 30] = action;
definition.setMenuActions(actions);
} else if (opcode == 39) {
data.get(); // light diffusion
} else if (opcode == 40) {
int amount = data.get() & 0xFF;
for (int i = 0; i < amount; i++) {
data.getShort(); // original colour
data.getShort(); // replacement colour
}
} else if (opcode == 60) {
data.getShort(); // minimap function
} else if (opcode == 62) {
// boolean inverted = true
} else if (opcode == 64) {
// boolean castsShadow = false;
} else if (opcode == 65) {
data.getShort(); // scale X
} else if (opcode == 66) {
data.getShort(); // scale Y
} else if (opcode == 67) {
data.getShort(); // scale Z
} else if (opcode == 68) {
data.getShort(); // map scene
} else if (opcode == 69) {
data.get(); // surroundings
} else if (opcode == 70) {
data.getShort(); // translate dX
} else if (opcode == 71) {
data.getShort(); // translate dY
} else if (opcode == 72) {
data.getShort(); // translate dZ
} else if (opcode == 73) {
// boolean obstructiveGround = true;
} else if (opcode == 74) {
// boolean ethereal = true;
} else if (opcode == 75) {
data.get(); // support items
} else {
continue;
}
}
}
}
@@ -0,0 +1,119 @@
package org.apollo.fs.decoder;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apollo.fs.IndexedFileSystem;
import org.apollo.fs.archive.Archive;
import org.apollo.game.model.Position;
import org.apollo.game.model.obj.StaticObject;
import org.apollo.util.ByteBufferUtil;
import org.apollo.util.CompressionUtil;
/**
* Decodes map object data from the {@code map_index.dat} file into {@link StaticObject}s.
*
* @author Chris Fletcher
*/
public final class StaticObjectDecoder {
/**
* The {@link IndexedFileSystem}.
*/
private final IndexedFileSystem fs;
/**
* Creates the decoder.
*
* @param fs The indexed file system.
*/
public StaticObjectDecoder(IndexedFileSystem fs) {
this.fs = fs;
}
/**
* Decodes all static objects and places them in the returned array.
*
* @return The decoded objects.
* @throws IOException If an I/O error occurs.
*/
public StaticObject[] decode() throws IOException {
Archive versionList = Archive.decode(fs.getFile(0, 5));
ByteBuffer buffer = versionList.getEntry("map_index").getBuffer();
int indices = buffer.remaining() / 7;
int[] areas = new int[indices];
int[] landscapes = new int[indices];
for (int i = 0; i < indices; i++) {
areas[i] = buffer.getShort() & 0xFFFF;
@SuppressWarnings("unused")
int mapFile = buffer.getShort() & 0xFFFF;
landscapes[i] = buffer.getShort() & 0xFFFF;
@SuppressWarnings("unused")
boolean members = (buffer.get() & 0xFF) == 1;
}
List<StaticObject> objects = new ArrayList<StaticObject>();
for (int i = 0; i < indices; i++) {
ByteBuffer compressed = fs.getFile(4, landscapes[i]);
ByteBuffer uncompressed = ByteBuffer.wrap(CompressionUtil.ungzip(compressed));
Collection<StaticObject> areaObjects = parseArea(areas[i], uncompressed);
objects.addAll(areaObjects);
}
return objects.toArray(new StaticObject[objects.size()]);
}
/**
* Parses a single area from the specified buffer.
*
* @param area The identifier of that area.
* @param buffer The buffer which holds the area's data.
* @return A collection of all parsed objects.
*/
private Collection<StaticObject> parseArea(int area, ByteBuffer buffer) {
List<StaticObject> objects = new ArrayList<StaticObject>();
int x = (area >> 8 & 0xFF) * 64;
int y = (area & 0xFF) * 64;
int id = -1;
int idOffset;
while ((idOffset = ByteBufferUtil.readSmart(buffer)) != 0) {
id += idOffset;
int position = 0;
int positionOffset;
while ((positionOffset = ByteBufferUtil.readSmart(buffer)) != 0) {
position += positionOffset - 1;
int localX = position >> 6 & 0x3F;
int localY = position & 0x3F;
int height = position >> 12;
int info = buffer.get() & 0xFF;
int type = info >> 2;
int rotation = info & 3;
Position pos = new Position(x + localX, y + localY, height);
StaticObject object = new StaticObject(id, pos, type, rotation);
objects.add(object);
}
}
return objects;
}
}
@@ -1,5 +1,5 @@
/**
* Contains classes which parse files within the game's cache.
*/
package org.apollo.fs.parser;
package org.apollo.fs.decoder;
@@ -8,9 +8,7 @@ import org.apollo.game.model.Inventory;
import org.apollo.game.model.Item;
import org.apollo.game.model.Player;
import org.apollo.game.model.Skill;
import org.apollo.game.model.SkillSet;
import org.apollo.game.model.def.EquipmentDefinition;
import org.apollo.game.model.def.ItemDefinition;
import org.apollo.game.model.inv.SynchronizationInventoryListener;
import org.apollo.util.LanguageUtil;
+52
View File
@@ -0,0 +1,52 @@
package org.apollo.game.model;
import org.apollo.game.event.Event;
import org.apollo.game.model.def.NpcDefinition;
/**
* An {@link Npc} is a {@link Character} that is not being controlled by a player.
*
* @author Major
*/
public class Npc extends Character {
/**
* This npc's definition.
*/
private final NpcDefinition definition;
/**
* Creates a new npc with the specified id and {@link Position}.
*
* @param id The id.
* @param position The position.
*/
public Npc(int id, Position position) {
this(NpcDefinition.forId(id), position);
}
/**
* Creates a new npc with the specified {@link NpcDefinition} and {@link Position}.
*
* @param definition The definition.
* @param position The position.
*/
public Npc(NpcDefinition definition, Position position) {
super(position);
this.definition = definition;
}
/**
* Gets this npc's {@link NpcDefinition}
*
* @return The definition.
*/
public NpcDefinition getDefinition() {
return definition;
}
@Override
public void send(Event event) {
}
}
+34 -20
View File
@@ -8,10 +8,16 @@ import java.util.logging.Logger;
import org.apollo.Service;
import org.apollo.fs.IndexedFileSystem;
import org.apollo.fs.parser.ItemDefinitionParser;
import org.apollo.fs.decoder.ItemDefinitionDecoder;
import org.apollo.fs.decoder.NpcDefinitionDecoder;
import org.apollo.fs.decoder.ObjectDefinitionDecoder;
import org.apollo.fs.decoder.StaticObjectDecoder;
import org.apollo.game.command.CommandDispatcher;
import org.apollo.game.model.def.EquipmentDefinition;
import org.apollo.game.model.def.ItemDefinition;
import org.apollo.game.model.def.NpcDefinition;
import org.apollo.game.model.def.ObjectDefinition;
import org.apollo.game.model.obj.StaticObject;
import org.apollo.game.scheduling.ScheduledTask;
import org.apollo.game.scheduling.Scheduler;
import org.apollo.io.EquipmentDefinitionParser;
@@ -102,34 +108,42 @@ public final class World {
*
* @param release The release number.
* @param fs The file system.
* @param mgr The plugin manager. TODO move this.
* @param manager The plugin manager. TODO move this.
* @throws IOException If an I/O error occurs.
*/
public void init(int release, IndexedFileSystem fs, PluginManager mgr) throws IOException {
logger.info("Loading item definitions...");
ItemDefinitionParser itemParser = new ItemDefinitionParser(fs);
ItemDefinition[] itemDefs = itemParser.parse();
ItemDefinition.init(itemDefs);
logger.info("Done (loaded " + itemDefs.length + " item definitions).");
public void init(int release, IndexedFileSystem fs, PluginManager manager) throws IOException {
ItemDefinitionDecoder itemParser = new ItemDefinitionDecoder(fs);
ItemDefinition[] itemDefs = itemParser.decode();
ItemDefinition.init(itemDefs);
logger.info("Loaded " + itemDefs.length + " item definitions.");
logger.info("Loading equipment definitions...");
int nonNull = 0;
InputStream is = new BufferedInputStream(new FileInputStream("data/equipment-" + release + ".dat"));
try {
EquipmentDefinitionParser equipParser = new EquipmentDefinitionParser(is);
EquipmentDefinition[] equipDefs = equipParser.parse();
for (EquipmentDefinition def : equipDefs) {
if (def != null) {
nonNull++;
}
}
EquipmentDefinition.init(equipDefs);
EquipmentDefinitionParser parser = new EquipmentDefinitionParser(is);
EquipmentDefinition[] defs = parser.parse();
EquipmentDefinition.init(defs);
logger.info("Loaded " + defs.length + " equipment definitions.");
} finally {
is.close();
}
logger.info("Done (loaded " + nonNull + " equipment definitions).");
this.pluginManager = mgr; // TODO move!!
NpcDefinitionDecoder parser = new NpcDefinitionDecoder(fs);
NpcDefinition[] npcDefs = parser.decode();
NpcDefinition.init(npcDefs);
logger.info("Loaded " + npcDefs.length + " npc definitions.");
ObjectDefinitionDecoder objParser = new ObjectDefinitionDecoder(fs);
ObjectDefinition[] objDefs = objParser.decode();
ObjectDefinition.init(objDefs);
logger.info("Loaded " + objDefs.length + " object definitions.");
StaticObjectDecoder objectParser = new StaticObjectDecoder(fs);
StaticObject[] objects = objectParser.decode();
StaticObject.init(objects);
logger.info("Loaded " + objects.length + " static objects.");
pluginManager = manager; // TODO move!!
}
/**
@@ -128,6 +128,11 @@ public final class ItemDefinition {
*/
private boolean members;
/**
* This item's team.
*/
private int team;
/**
* The ground actions array.
*/
@@ -387,4 +392,22 @@ public final class ItemDefinition {
return inventoryActions[id];
}
}
/**
* Sets this items team.
*
* @param team The team.
*/
public void setTeam(int team) {
this.team = team;
}
/**
* Gets this item's team.
*
* @return The team.
*/
public int getTeam() {
return team;
}
}
@@ -0,0 +1,362 @@
package org.apollo.game.model.def;
/**
* Represents a type of {@link Npc}.
*
* @author Chris Fletcher
*/
public final class NpcDefinition {
/**
* The NPC definitions.
*/
private static NpcDefinition[] definitions;
/**
* Initialises the class with the specified set of definitions.
*
* @param definitions The definitions.
* @throws RuntimeException If there is an id mismatch.
*/
public static void init(NpcDefinition[] definitions) {
NpcDefinition.definitions = definitions;
for (int id = 0; id < definitions.length; id++) {
NpcDefinition def = definitions[id];
if (def.getId() != id) {
throw new RuntimeException("NPC definition id mismatch");
}
}
}
/**
* Gets the total number of NPCs.
*
* @return The total number of NPCs.
*/
public static int count() {
return definitions.length;
}
/**
* Gets the NPC definition for the specified id.
*
* @param The id.
* @return The definition.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public static NpcDefinition forId(int id) {
if (id < 0 || id >= definitions.length) {
throw new IndexOutOfBoundsException();
}
return definitions[id];
}
/**
* The NPC id.
*/
private final int id;
/**
* The name of the NPC.
*/
private String name;
/**
* The description of the NPC.
*/
private String description;
/**
* The NPC's size, in tiles.
*/
private int size = 1;
/**
* The various animation ids.
*/
private int standAnim = -1, walkAnim = -1, walkBackAnim = -1, walkLeftAnim = -1, walkRightAnim = -1;
/**
* An array of interaction options.
*/
private String[] interactions = new String[5];
/**
* The combat level of the NPC.
*/
private int combatLevel = -1;
/**
* Creates a new NPC definition.
*
* @param id The NPC id.
*/
public NpcDefinition(int id) {
this.id = id;
}
/**
* Gets the NPC id.
*
* @return The NPC id.
*/
public int getId() {
return id;
}
/**
* Gets the name of the NPC.
*
* @return The name of the NPC.
*/
public String getName() {
return name;
}
/**
* Gets the description of the NPC.
*
* @return The description.
*/
public String getDescription() {
return description;
}
/**
* Gets the NPC's size, in tiles.
*
* @return The size.
*/
public int getSize() {
return size;
}
/**
* Gets the id of the NPC's standing animation.
*
* @return The stand animation id, or -1 if it doesn't have one.
*/
public int getStandAnimation() {
return standAnim;
}
/**
* Gets the walking animation of the NPC.
*
* @return The walking animation.
*/
public int getWalkAnimation() {
return walkAnim;
}
/**
* Gets the walk-back animation of the NPC.
*
* @return The walk-back animation.
*/
public int getWalkBackAnimation() {
return walkBackAnim;
}
/**
* Gets the walk-left animation of the NPC.
*
* @return The walk-left animation.
*/
public int getWalkLeftAnimation() {
return walkLeftAnim;
}
/**
* Gets the walk-right animation of the NPC.
*
* @return The walk-right animation.
*/
public int getWalkRightAnimation() {
return walkRightAnim;
}
/**
* Gets an interaction option.
*
* @param slot The slot of the option.
* @return The option, or {@code null} if there isn't any at the specified slot.
* @throws IndexOutOfBoundsException If the slot is out of bounds.
*/
public String getInteraction(int slot) {
if (slot < 0 || slot >= interactions.length) {
throw new IndexOutOfBoundsException();
}
return interactions[slot];
}
/**
* Gets the array of interaction options.
*
* @return The interaction options.
*/
public String[] getInteractions() {
return interactions;
}
/**
* Gets the NPC's combat level.
*
* @return The combat level, or -1 if it doesn't have one.
*/
public int getCombatLevel() {
return combatLevel;
}
/**
* Checks if the NPC has a standing animation id.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasStandAnimation() {
return standAnim != -1;
}
/**
* Checks if the NPC has a walking animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkAnimation() {
return walkAnim != -1;
}
/**
* Checks if the NPC has a walk-back animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkBackAnimation() {
return walkBackAnim != -1;
}
/**
* Checks if the NPC has a walk-left animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkLeftAnimation() {
return walkLeftAnim != -1;
}
/**
* Checks if the NPC has a walk-right animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkRightAnimation() {
return walkRightAnim != -1;
}
/**
* Checks if there is an interaction option present.
*
* @param slot The slot to check.
* @return {@code true} if so, {@code false} if not.
* @throws IndexOutOfBoundsException If the slot is out of bounds.
*/
public boolean hasInteraction(int slot) {
if (slot < 0 || slot >= interactions.length) {
throw new IndexOutOfBoundsException();
}
return interactions[slot] != null;
}
/**
* Checks if the NPC has a combat level.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasCombatLevel() {
return combatLevel != -1;
}
/**
* Sets the name of the NPC.
*
* @param name The name.
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the description of the NPC.
*
* @param description The description.
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Sets the size of the NPC, in tiles.
*
* @param size The size.
*/
public void setSize(int size) {
this.size = size;
}
/**
* Sets the id of the NPC's standing animation.
*
* @param standAnim The stand animation id.
*/
public void setStandAnimation(int standAnim) {
this.standAnim = standAnim;
}
/**
* Sets the walking animation of the NPC.
*
* @param walkAnim The walking animation.
*/
public void setWalkAnimation(int walkAnim) {
this.walkAnim = walkAnim;
}
/**
* Sets the various walking animations of the NPC.
*
* @param walkAnim The walking animation.
* @param walkBackAnim The walk-back animation.
* @param walkLeftAnim The walk-left animation.
* @param walkRightAnim The walk-right animation.
*/
public void setWalkAnimations(int walkAnim, int walkBackAnim, int walkLeftAnim, int walkRightAnim) {
this.walkAnim = walkAnim;
this.walkBackAnim = walkBackAnim;
this.walkLeftAnim = walkLeftAnim;
this.walkRightAnim = walkRightAnim;
}
/**
* Sets an interaction option.
*
* @param slot The slot of the option.
* @param interaction The interaction options.
* @throws IndexOutOfBoundsException If the slot is out of bounds.
*/
public void setInteraction(int slot, String interaction) {
if (slot < 0 || slot >= interactions.length) {
throw new IndexOutOfBoundsException();
}
interactions[slot] = interaction;
}
/**
* Sets the NPC's combat level.
*
* @param combatLevel The combat level.
*/
public void setCombatLevel(int combatLevel) {
this.combatLevel = combatLevel;
}
}
@@ -0,0 +1,272 @@
package org.apollo.game.model.def;
import org.apollo.game.model.obj.GameObject;
/**
* Represents a type of {@link GameObject}.
*
* @author Major
*/
public final class ObjectDefinition {
/**
* Gets the total number of objects.
*
* @return The total number of objects.
*/
public static int count() {
return definitions.length;
}
/**
* Gets the array of object definitions.
*
* @return The definitions.
*/
public static ObjectDefinition[] getDefinitions() {
return definitions;
}
/**
* Initialises the object definitions.
*
* @param definitions The decoded definitions.
*/
public static void init(ObjectDefinition[] definitions) {
ObjectDefinition.definitions = definitions;
for (int id = 0; id < definitions.length; id++) {
// This also verifies that none of the definitions are null.
ObjectDefinition def = definitions[id];
if (def.getId() != id) {
throw new RuntimeException("Item definition id mismatch!");
}
}
}
/**
* Gets the object definition for the specified id.
*
* @param id The id of the object.
* @return The definition.
*/
public static ObjectDefinition lookup(int id) {
if (id < 0 || id > definitions.length) {
throw new IndexOutOfBoundsException(ObjectDefinition.class.getName() + " lookup index " + id
+ " out of bounds.");
}
return definitions[id];
}
/**
* The object's menu actions.
*/
private String[] menuActions;
/**
* The object's description.
*/
private String description;
/**
* Denotes whether this object has actions associated with it or not.
*/
private boolean interactive;
/**
* The object's id.
*/
private final int id;
/**
* The object's name.
*/
private String name;
/**
* Denotes whether this object is impenetrable or not.
*/
private boolean impenetrable;
/**
* Denotes whether the object can be walked over or not.
*/
private boolean solid;
/**
* This object's height.
*/
private int height;
/**
* This object's width.
*/
private int width;
/**
* The array of game object definitions.
*/
private static ObjectDefinition[] definitions;
/**
* Creates a new object definition.
*
* @param id The id of the object.
*/
public ObjectDefinition(int id) {
this.id = id;
}
/**
* Gets the description of this object.
*
* @return The description.
*/
public String getDescription() {
return description;
}
/**
* Gets the height of this object.
*
* @return The height.
*/
public int getHeight() {
return width;
}
/**
* Gets the id of this object.
*
* @return The id.
*/
public int getId() {
return id;
}
/**
* Gets the menu actions of this object.
*
* @return The menu actions.
*/
public String[] getMenuActions() {
return menuActions;
}
/**
* Gets the name of this object.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* Gets the with of this object.
*
* @return The width.
*/
public int getWidth() {
return height;
}
/**
* Indicates the impenetrability of this object.
*
* @return {@code true} if this object is impenetrable, otherwise {@code false}.
*/
public boolean isImpenetrable() {
return impenetrable;
}
/**
* Indicates the interactivity of this object.
*
* @return {@code true} if the object is interactive, otherwise {@code false}.
*/
public boolean isInteractive() {
return interactive;
}
/**
* Indicates the solidity of this object.
*
* @return {@code true} if this object is solid, otherwise {@code false}.
*/
public boolean isSolid() {
return solid;
}
/**
* Sets the description of this object.
*
* @param description The description.
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Sets the height of this object.
*
* @param height The height.
*/
public void setHeight(int height) {
this.height = height;
}
/**
* Sets the impenetrability of this object.
*
* @param impenetrable The impenetrability.
*/
public void setImpenetrable(boolean impenetrable) {
this.impenetrable = impenetrable;
}
/**
* Sets the interactivity of this object.
*
* @param interactive The interactivity.
*/
public void setInteractive(boolean interactive) {
this.interactive = interactive;
}
/**
* Sets the menu actions of this object.
*
* @param menuActions The menu actions.
*/
public void setMenuActions(String[] menuActions) {
this.menuActions = menuActions;
}
/**
* Sets the name of this object.
*
* @param name The name.
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the solidity of this object.
*
* @param solid The solidity.
*/
public void setSolid(boolean solid) {
this.solid = solid;
}
/**
* Sets the width of this object.
*
* @param width The width.
*/
public void setWidth(int width) {
this.width = width;
}
}
@@ -0,0 +1,35 @@
package org.apollo.game.model.obj;
import org.apollo.game.model.def.ObjectDefinition;
/**
* Represents an in-game object.
*
* @author Major
*/
public final class GameObject {
/**
* The object definition.
*/
private final ObjectDefinition definition;
/**
* Creates the game object.
*
* @param definition The object's definition.
*/
public GameObject(ObjectDefinition definition) {
this.definition = definition;
}
/**
* Gets the object's definition.
*
* @return The definition.
*/
public ObjectDefinition getDefinition() {
return definition;
}
}
+92 -15
View File
@@ -1,35 +1,112 @@
package org.apollo.game.model.obj;
import org.apollo.game.model.def.StaticObjectDefinition;
import org.apollo.game.model.Position;
import org.apollo.game.model.def.ObjectDefinition;
/**
* Represents a static object in the game world.
*
* @author Graham
* @author Chris Fletcher
* @author Major
*/
public final class StaticObject {
/**
* The object definition.
* Initialises the static objects.
*/
private final StaticObjectDefinition definition;
/**
* Creates the game object.
*
* @param definition The object's definition.
*/
public StaticObject(StaticObjectDefinition definition) {
this.definition = definition;
public static void init(StaticObject[] objects) {
}
/**
* Gets the object's definition.
*
* @return The definition.
* The object's id.
*/
public StaticObjectDefinition getDefinition() {
private final short id;
/**
* The object's definition.
*/
private final ObjectDefinition definition;
/**
* The object's position.
*/
private final Position position;
/**
* The object type.
*/
private final byte type;
/**
* The object's rotation.
*/
private final byte rotation;
/**
* Creates a new static object.
*
* @param def The object's id.
* @param position The position.
* @param type The type code of the object.
* @param rotation The rotation of the object.
*/
public StaticObject(int id, Position position, int type, int rotation) {
this.id = (short) id;
this.position = position;
this.type = (byte) type;
this.rotation = (byte) rotation;
this.definition = ObjectDefinition.lookup(id);
}
/**
* Gets the definition of this object.
*
* @return The object's definition.
*/
public ObjectDefinition getDefinition() {
return definition;
}
/**
* Gets the id of the object.
*
* @return The object id.
*/
public int getId() {
return id;
}
/**
* Gets the position of this object.
*
* @return The object's position.
*/
public Position getPosition() {
return position;
}
/**
* Gets the object's rotation.
*
* @return The rotation.
*/
public int getRotation() {
return rotation;
}
/**
* Gets the type code of the object.
*
* @return The type.
*/
public int getType() {
return type;
}
@Override
public String toString() {
return StaticObject.class.getName() + " [id= " + id + ", type= " + type + ", rotation= " + rotation + "]";
}
}
+3 -3
View File
@@ -6,7 +6,7 @@ import java.io.File;
import java.io.FileOutputStream;
import org.apollo.fs.IndexedFileSystem;
import org.apollo.fs.parser.ItemDefinitionParser;
import org.apollo.fs.decoder.ItemDefinitionDecoder;
import org.apollo.game.model.def.ItemDefinition;
/**
@@ -36,8 +36,8 @@ public final class EquipmentUpdater {
try {
IndexedFileSystem fs = new IndexedFileSystem(new File("data/fs/" + release), true);
try {
ItemDefinitionParser parser = new ItemDefinitionParser(fs);
ItemDefinition[] defs = parser.parse();
ItemDefinitionDecoder decoder = new ItemDefinitionDecoder(fs);
ItemDefinition[] defs = decoder.decode();
ItemDefinition.init(defs);
os.writeShort(defs.length);
+3 -3
View File
@@ -8,7 +8,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apollo.fs.IndexedFileSystem;
import org.apollo.fs.parser.ItemDefinitionParser;
import org.apollo.fs.decoder.ItemDefinitionDecoder;
import org.apollo.game.model.def.ItemDefinition;
/**
@@ -37,8 +37,8 @@ public final class NoteUpdater {
try {
IndexedFileSystem fs = new IndexedFileSystem(new File("data/fs/" + release), true);
try {
ItemDefinitionParser parser = new ItemDefinitionParser(fs);
ItemDefinition[] defs = parser.parse();
ItemDefinitionDecoder decoder = new ItemDefinitionDecoder(fs);
ItemDefinition[] defs = decoder.decode();
ItemDefinition.init(defs);
os.writeShort(defs.length);
+18 -4
View File
@@ -12,13 +12,17 @@ import org.apollo.net.NetworkConstants;
public final class ByteBufferUtil {
/**
* Reads an unsigned tri byte from the specified buffer.
* Reads a 'smart' (either a {@code byte} or {@code short} depending on the value) from the specified buffer.
*
* @param buffer The buffer.
* @return The tri byte.
* @return The 'smart'.
*/
public static int readUnsignedTriByte(ByteBuffer buffer) {
return ((buffer.get() & 0xFF) << 16) | ((buffer.get() & 0xFF) << 8) | (buffer.get() & 0xFF);
public static int readSmart(ByteBuffer buffer) {
int peek = buffer.get(buffer.position()) & 0xFF;
if (peek < 128) {
return buffer.get() & 0xFF;
}
return (buffer.getShort() & 0xFFFF) - 32768;
}
/**
@@ -36,6 +40,16 @@ public final class ByteBufferUtil {
return bldr.toString();
}
/**
* Reads an unsigned tri byte from the specified buffer.
*
* @param buffer The buffer.
* @return The tri byte.
*/
public static int readUnsignedTriByte(ByteBuffer buffer) {
return ((buffer.get() & 0xFF) << 16) | ((buffer.get() & 0xFF) << 8) | (buffer.get() & 0xFF);
}
/**
* Default private constructor to prevent instantiation.
*/
+35
View File
@@ -4,6 +4,8 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@@ -34,6 +36,39 @@ public final class CompressionUtil {
}
}
/**
* Ungzips the compressed buffer and places the results into the returning array.
*
* @param compressed The compressed buffer.
* @return The uncompressed array.
* @throws IOException If an I/O error occurs.
*/
public static byte[] ungzip(ByteBuffer compressed) throws IOException {
byte[] data = new byte[compressed.remaining()];
compressed.get(data);
InputStream is = new GZIPInputStream(new ByteArrayInputStream(data));
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
while (true) {
byte[] buf = new byte[1024];
int read = is.read(buf, 0, buf.length);
if (read == -1) {
break;
}
os.write(buf, 0, read);
}
} finally {
os.close();
}
return os.toByteArray();
} finally {
is.close();
}
}
/**
* Unbzip2s the compressed array and places the result into the uncompressed array.
*