mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-03 08:39:11 +00:00
Default apollo.
This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
package net.burtleburtle.bob.rand;
|
||||
|
||||
/**
|
||||
* <p>An implementation of the
|
||||
* <a href="http://www.burtleburtle.net/bob/rand/isaacafa.html">ISAAC</a>
|
||||
* psuedorandom number generator.</p>
|
||||
* <pre>
|
||||
* ------------------------------------------------------------------------------
|
||||
* Rand.java: By Bob Jenkins. My random number generator, ISAAC.
|
||||
* rand.init() -- initialize
|
||||
* rand.val() -- get a random value
|
||||
* MODIFIED:
|
||||
* 960327: Creation (addition of randinit, really)
|
||||
* 970719: use context, not global variables, for internal state
|
||||
* 980224: Translate to Java
|
||||
* ------------------------------------------------------------------------------
|
||||
* </pre>
|
||||
* <p>This class has been changed to be more conformant to Java and javadoc
|
||||
* conventions.</p>
|
||||
* @author Bob Jenkins
|
||||
*/
|
||||
public final class IsaacRandom {
|
||||
|
||||
/**
|
||||
* The golden ratio.
|
||||
*/
|
||||
private static final int GOLDEN_RATIO = 0x9e3779b9;
|
||||
|
||||
/**
|
||||
* The log of the size of the result and memory arrays.
|
||||
*/
|
||||
private static final int SIZEL = 8;
|
||||
|
||||
/**
|
||||
* The size of the result and memory arrays.
|
||||
*/
|
||||
private static final int SIZE = 1 << SIZEL;
|
||||
|
||||
/**
|
||||
* A mask for pseudorandom lookup.
|
||||
*/
|
||||
private static int MASK = (SIZE - 1) << 2;
|
||||
|
||||
/**
|
||||
* The count through the results in the results array.
|
||||
*/
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* The results given to the user.
|
||||
*/
|
||||
private int[] rsl;
|
||||
|
||||
/**
|
||||
* The internal state.
|
||||
*/
|
||||
private int[] mem;
|
||||
|
||||
/**
|
||||
* The accumulator.
|
||||
*/
|
||||
private int a;
|
||||
|
||||
/**
|
||||
* The last result.
|
||||
*/
|
||||
private int b;
|
||||
|
||||
/**
|
||||
* The counter.
|
||||
*/
|
||||
private int c;
|
||||
|
||||
/**
|
||||
* Creates the random number generator without an initial seed.
|
||||
*/
|
||||
public IsaacRandom() {
|
||||
mem = new int[SIZE];
|
||||
rsl = new int[SIZE];
|
||||
init(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the random number generator with the specified seed.
|
||||
* @param seed The seed.
|
||||
*/
|
||||
public IsaacRandom(int[] seed) {
|
||||
mem = new int[SIZE];
|
||||
rsl = new int[SIZE];
|
||||
for (int i = 0; i < seed.length; ++i) {
|
||||
rsl[i] = seed[i];
|
||||
}
|
||||
init(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 256 results.
|
||||
*/
|
||||
private void isaac() {
|
||||
int i, j, x, y;
|
||||
|
||||
b += ++c;
|
||||
for (i = 0, j = SIZE / 2; i < SIZE / 2;) {
|
||||
x = mem[i];
|
||||
a ^= a << 13;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 6;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a << 2;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 16;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
}
|
||||
|
||||
for (j = 0; j < SIZE / 2;) {
|
||||
x = mem[i];
|
||||
a ^= a << 13;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 6;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a << 2;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 16;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises this random number generator.
|
||||
* @param flag Set to {@code true} if a seed was passed to the constructor.
|
||||
*/
|
||||
private void init(boolean flag) {
|
||||
int i;
|
||||
int a, b, c, d, e, f, g, h;
|
||||
a = b = c = d = e = f = g = h = GOLDEN_RATIO;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
a ^= b << 11;
|
||||
d += a;
|
||||
b += c;
|
||||
b ^= c >>> 2;
|
||||
e += b;
|
||||
c += d;
|
||||
c ^= d << 8;
|
||||
f += c;
|
||||
d += e;
|
||||
d ^= e >>> 16;
|
||||
g += d;
|
||||
e += f;
|
||||
e ^= f << 10;
|
||||
h += e;
|
||||
f += g;
|
||||
f ^= g >>> 4;
|
||||
a += f;
|
||||
g += h;
|
||||
g ^= h << 8;
|
||||
b += g;
|
||||
h += a;
|
||||
h ^= a >>> 9;
|
||||
c += h;
|
||||
a += b;
|
||||
}
|
||||
|
||||
for (i = 0; i < SIZE; i += 8) { /* fill in mem[] with messy stuff */
|
||||
if (flag) {
|
||||
a += rsl[i];
|
||||
b += rsl[i + 1];
|
||||
c += rsl[i + 2];
|
||||
d += rsl[i + 3];
|
||||
e += rsl[i + 4];
|
||||
f += rsl[i + 5];
|
||||
g += rsl[i + 6];
|
||||
h += rsl[i + 7];
|
||||
}
|
||||
a ^= b << 11;
|
||||
d += a;
|
||||
b += c;
|
||||
b ^= c >>> 2;
|
||||
e += b;
|
||||
c += d;
|
||||
c ^= d << 8;
|
||||
f += c;
|
||||
d += e;
|
||||
d ^= e >>> 16;
|
||||
g += d;
|
||||
e += f;
|
||||
e ^= f << 10;
|
||||
h += e;
|
||||
f += g;
|
||||
f ^= g >>> 4;
|
||||
a += f;
|
||||
g += h;
|
||||
g ^= h << 8;
|
||||
b += g;
|
||||
h += a;
|
||||
h ^= a >>> 9;
|
||||
c += h;
|
||||
a += b;
|
||||
mem[i] = a;
|
||||
mem[i + 1] = b;
|
||||
mem[i + 2] = c;
|
||||
mem[i + 3] = d;
|
||||
mem[i + 4] = e;
|
||||
mem[i + 5] = f;
|
||||
mem[i + 6] = g;
|
||||
mem[i + 7] = h;
|
||||
}
|
||||
|
||||
if (flag) { /* second pass makes all of seed affect all of mem */
|
||||
for (i = 0; i < SIZE; i += 8) {
|
||||
a += mem[i];
|
||||
b += mem[i + 1];
|
||||
c += mem[i + 2];
|
||||
d += mem[i + 3];
|
||||
e += mem[i + 4];
|
||||
f += mem[i + 5];
|
||||
g += mem[i + 6];
|
||||
h += mem[i + 7];
|
||||
a ^= b << 11;
|
||||
d += a;
|
||||
b += c;
|
||||
b ^= c >>> 2;
|
||||
e += b;
|
||||
c += d;
|
||||
c ^= d << 8;
|
||||
f += c;
|
||||
d += e;
|
||||
d ^= e >>> 16;
|
||||
g += d;
|
||||
e += f;
|
||||
e ^= f << 10;
|
||||
h += e;
|
||||
f += g;
|
||||
f ^= g >>> 4;
|
||||
a += f;
|
||||
g += h;
|
||||
g ^= h << 8;
|
||||
b += g;
|
||||
h += a;
|
||||
h ^= a >>> 9;
|
||||
c += h;
|
||||
a += b;
|
||||
mem[i] = a;
|
||||
mem[i + 1] = b;
|
||||
mem[i + 2] = c;
|
||||
mem[i + 3] = d;
|
||||
mem[i + 4] = e;
|
||||
mem[i + 5] = f;
|
||||
mem[i + 6] = g;
|
||||
mem[i + 7] = h;
|
||||
}
|
||||
}
|
||||
|
||||
isaac();
|
||||
count = SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next random value.
|
||||
* @return The next random value.
|
||||
*/
|
||||
public int nextInt() {
|
||||
if (0 == count--) {
|
||||
isaac();
|
||||
count = SIZE - 1;
|
||||
}
|
||||
return rsl[count];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains a Java implementation of Bob Jenkins' ISAAC algorithm.
|
||||
*/
|
||||
package net.burtleburtle.bob.rand;
|
||||
@@ -0,0 +1,177 @@
|
||||
package org.apollo;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apollo.fs.IndexedFileSystem;
|
||||
import org.apollo.game.model.World;
|
||||
import org.apollo.net.ApolloHandler;
|
||||
import org.apollo.net.HttpPipelineFactory;
|
||||
import org.apollo.net.JagGrabPipelineFactory;
|
||||
import org.apollo.net.ServicePipelineFactory;
|
||||
import org.apollo.net.NetworkConstants;
|
||||
import org.apollo.net.release.Release;
|
||||
import org.apollo.net.release.r317.Release317;
|
||||
import org.apollo.util.plugin.PluginContext;
|
||||
import org.apollo.util.plugin.PluginManager;
|
||||
import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.ChannelPipelineFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import org.jboss.netty.util.HashedWheelTimer;
|
||||
import org.jboss.netty.util.Timer;
|
||||
|
||||
/**
|
||||
* The core class of the Apollo server.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Server {
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(Server.class.getName());
|
||||
|
||||
/**
|
||||
* The entry point of the Apollo server application.
|
||||
* @param args The command-line arguments passed to the application.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Server server = null;
|
||||
try {
|
||||
server = new Server();
|
||||
server.init(args.length == 1 ? args[0] : Release317.class.getName());
|
||||
|
||||
SocketAddress service = new InetSocketAddress(NetworkConstants.SERVICE_PORT);
|
||||
SocketAddress http = new InetSocketAddress(NetworkConstants.HTTP_PORT);
|
||||
SocketAddress jaggrab = new InetSocketAddress(NetworkConstants.JAGGRAB_PORT);
|
||||
|
||||
server.start();
|
||||
server.bind(service, http, jaggrab);
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "Error whilst starting server.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ServerBootstrap} for the service listener.
|
||||
*/
|
||||
private final ServerBootstrap serviceBootstrap = new ServerBootstrap();
|
||||
|
||||
/**
|
||||
* The {@link ServerBootstrap} for the HTTP listener.
|
||||
*/
|
||||
private final ServerBootstrap httpBootstrap = new ServerBootstrap();
|
||||
|
||||
/**
|
||||
* The {@link ServerBootstrap} for the JAGGRAB listener.
|
||||
*/
|
||||
private final ServerBootstrap jagGrabBootstrap = new ServerBootstrap();
|
||||
|
||||
/**
|
||||
* The {@link ExecutorService} used for network events. The named thread
|
||||
* factory is unused as Netty names threads itself.
|
||||
*/
|
||||
private final ExecutorService networkExecutor = Executors.newCachedThreadPool();
|
||||
|
||||
/**
|
||||
* The service manager.
|
||||
*/
|
||||
private final ServiceManager serviceManager;
|
||||
|
||||
/**
|
||||
* The timer used for idle checking.
|
||||
*/
|
||||
private final Timer timer = new HashedWheelTimer();
|
||||
|
||||
/**
|
||||
* The server's context.
|
||||
*/
|
||||
private ServerContext context;
|
||||
|
||||
/**
|
||||
* Creates the Apollo server.
|
||||
* @throws Exception if an error occurs whilst creating services.
|
||||
*/
|
||||
public Server() throws Exception {
|
||||
logger.info("Starting Apollo...");
|
||||
serviceManager = new ServiceManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the server.
|
||||
* @param releaseClassName The class name of the current active
|
||||
* {@link Release}.
|
||||
* @throws ClassNotFoundException if the release class could not be found.
|
||||
* @throws IllegalAccessException if the release class could not be accessed.
|
||||
* @throws InstantiationException if the release class could not be instantiated.
|
||||
*/
|
||||
public void init(String releaseClassName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||
Class<?> clazz = Class.forName(releaseClassName);
|
||||
Release release = (Release) clazz.newInstance();
|
||||
|
||||
logger.info("Initialized release #" + release.getReleaseNumber() + ".");
|
||||
|
||||
ChannelFactory factory = new NioServerSocketChannelFactory(networkExecutor, networkExecutor);
|
||||
serviceBootstrap.setFactory(factory);
|
||||
httpBootstrap.setFactory(factory);
|
||||
jagGrabBootstrap.setFactory(factory);
|
||||
|
||||
context = new ServerContext(release, serviceManager);
|
||||
ApolloHandler handler = new ApolloHandler(context);
|
||||
|
||||
ChannelPipelineFactory servicePipelineFactory = new ServicePipelineFactory(handler, timer);
|
||||
serviceBootstrap.setPipelineFactory(servicePipelineFactory);
|
||||
|
||||
ChannelPipelineFactory httpPipelineFactory = new HttpPipelineFactory(handler, timer);
|
||||
httpBootstrap.setPipelineFactory(httpPipelineFactory);
|
||||
|
||||
ChannelPipelineFactory jagGrabPipelineFactory = new JagGrabPipelineFactory(handler, timer);
|
||||
jagGrabBootstrap.setPipelineFactory(jagGrabPipelineFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the server to the specified address.
|
||||
* @param serviceAddress The service address to bind to.
|
||||
* @param httpAddress The HTTP address to bind to.
|
||||
* @param jagGrabAddress The JAGGRAB address to bind to.
|
||||
*/
|
||||
public void bind(SocketAddress serviceAddress, SocketAddress httpAddress, SocketAddress jagGrabAddress) {
|
||||
logger.info("Binding service listener to address: " + serviceAddress + "...");
|
||||
serviceBootstrap.bind(serviceAddress);
|
||||
|
||||
logger.info("Binding HTTP listener to address: " + httpAddress + "...");
|
||||
try {
|
||||
httpBootstrap.bind(httpAddress);
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.WARNING, "Binding to HTTP failed: client will use JAGGRAB as a fallback (not reccomended)!", t);
|
||||
}
|
||||
|
||||
logger.info("Binding JAGGRAB listener to address: " + jagGrabAddress + "...");
|
||||
jagGrabBootstrap.bind(jagGrabAddress);
|
||||
|
||||
logger.info("Ready for connections.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
public void start() throws Exception {
|
||||
PluginManager mgr = new PluginManager(new PluginContext(context));
|
||||
mgr.start(); // TODO move this?
|
||||
|
||||
serviceManager.startAll();
|
||||
|
||||
// TODO move this?
|
||||
int releaseNo = context.getRelease().getReleaseNumber();
|
||||
IndexedFileSystem fs = new IndexedFileSystem(new File("data/fs/" + releaseNo), true);
|
||||
World.getWorld().init(releaseNo, fs, mgr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.apollo;
|
||||
|
||||
import org.apollo.net.release.Release;
|
||||
import org.jboss.netty.channel.group.ChannelGroup;
|
||||
import org.jboss.netty.channel.group.DefaultChannelGroup;
|
||||
|
||||
/**
|
||||
* A {@link ServerContext} is created along with the {@link Server} object. The
|
||||
* primary difference is that a reference to the current context should be
|
||||
* passed around within the server. The {@link Server} should not be as it
|
||||
* allows access to some methods such as
|
||||
* {@link Server#bind(java.net.SocketAddress, java.net.SocketAddress, java.net.SocketAddress)}
|
||||
* which user scripts/code should not be able to access.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ServerContext {
|
||||
|
||||
/**
|
||||
* The current release.
|
||||
*/
|
||||
private final Release release;
|
||||
|
||||
/**
|
||||
* The service manager.
|
||||
*/
|
||||
private final ServiceManager serviceManager;
|
||||
|
||||
/**
|
||||
* The channel group.
|
||||
*/
|
||||
private final ChannelGroup group = new DefaultChannelGroup();
|
||||
|
||||
/**
|
||||
* Creates a new server context.
|
||||
* @param release The current release.
|
||||
* @param serviceManager The service manager.
|
||||
*/
|
||||
ServerContext(Release release, ServiceManager serviceManager) {
|
||||
this.release = release;
|
||||
this.serviceManager = serviceManager;
|
||||
this.serviceManager.setContext(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the channel group.
|
||||
* @return The channel group.
|
||||
*/
|
||||
public ChannelGroup getChannelGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current release.
|
||||
* @return The current release.
|
||||
*/
|
||||
public Release getRelease() {
|
||||
return release;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the service manager.
|
||||
* @return The service manager.
|
||||
*/
|
||||
public ServiceManager getServiceManager() {
|
||||
return serviceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a service. This method is shorthand for
|
||||
* {@code getServiceManager().getService(...)}.
|
||||
* @param <S> The type of service.
|
||||
* @param clazz The service class.
|
||||
* @return The service, or {@code null} if it could not be found.
|
||||
*/
|
||||
public <S extends Service> S getService(Class<S> clazz) {
|
||||
return serviceManager.getService(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.apollo;
|
||||
|
||||
/**
|
||||
* Represents a service that the server provides.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class Service {
|
||||
|
||||
/**
|
||||
* The server context.
|
||||
*/
|
||||
private ServerContext ctx;
|
||||
|
||||
/**
|
||||
* Gets the server context.
|
||||
* @return The context.
|
||||
*/
|
||||
public final ServerContext getContext() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server context.
|
||||
* @param ctx The context.
|
||||
*/
|
||||
public final void setContext(ServerContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the service.
|
||||
*/
|
||||
public abstract void start();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.apollo;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apollo.util.xml.XmlNode;
|
||||
import org.apollo.util.xml.XmlParser;
|
||||
|
||||
/**
|
||||
* A class which manages {@link Service}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ServiceManager {
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(ServiceManager.class.getName());
|
||||
|
||||
/**
|
||||
* The service map.
|
||||
*/
|
||||
private Map<Class<? extends Service>, Service> services = new HashMap<Class<? extends Service>, Service>();
|
||||
|
||||
/**
|
||||
* Creates and initializes the {@link ServiceManager}.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
public ServiceManager() throws Exception {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this service manager.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void init() throws Exception {
|
||||
logger.info("Registering services...");
|
||||
|
||||
XmlParser parser = new XmlParser();
|
||||
XmlNode rootNode;
|
||||
|
||||
InputStream is = new FileInputStream("data/services.xml");
|
||||
try {
|
||||
rootNode = parser.parse(is);
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
|
||||
if (!rootNode.getName().equals("services")) {
|
||||
throw new Exception("unexpected name of root node");
|
||||
}
|
||||
|
||||
for (XmlNode childNode : rootNode) {
|
||||
if (!childNode.getName().equals("service")) {
|
||||
throw new Exception("unexpected name of child node");
|
||||
}
|
||||
|
||||
if (!childNode.hasValue()) {
|
||||
throw new Exception("child node must have a value!");
|
||||
}
|
||||
|
||||
Class<? extends Service> clazz = (Class<? extends Service>) Class.forName(childNode.getValue());
|
||||
register((Class<Service>) clazz, clazz.newInstance());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a service.
|
||||
* @param <S> The type of service.
|
||||
* @param clazz The service's class.
|
||||
* @param service The service.
|
||||
*/
|
||||
private <S extends Service> void register(Class<S> clazz, S service) {
|
||||
logger.fine("Registering service: " + clazz + "...");
|
||||
services.put(clazz, service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a service.
|
||||
* @param <S> The type of service.
|
||||
* @param clazz The service class.
|
||||
* @return The service.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends Service> S getService(Class<S> clazz) {
|
||||
return (S) services.get(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts all the services.
|
||||
*/
|
||||
public void startAll() {
|
||||
logger.info("Starting services...");
|
||||
for (Service service : services.values()) {
|
||||
logger.fine("Starting service: " + service.getClass().getName() + "...");
|
||||
service.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of all services.
|
||||
* @param ctx The server context.
|
||||
*/
|
||||
public void setContext(ServerContext ctx) {
|
||||
for (Service s : services.values()) {
|
||||
s.setContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
/**
|
||||
* A class which points to a file in the cache.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FileDescriptor {
|
||||
|
||||
/**
|
||||
* The file type.
|
||||
*/
|
||||
private final int type;
|
||||
|
||||
/**
|
||||
* The file id.
|
||||
*/
|
||||
private final int file;
|
||||
|
||||
/**
|
||||
* Creates the file descriptor.
|
||||
* @param type The file type.
|
||||
* @param file The file id.
|
||||
*/
|
||||
public FileDescriptor(int type, int file) {
|
||||
this.type = type;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file type.
|
||||
* @return The file type.
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file id.
|
||||
* @return The file id.
|
||||
*/
|
||||
public int getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
/**
|
||||
* Holds file system related constants.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FileSystemConstants {
|
||||
|
||||
/**
|
||||
* The number of caches.
|
||||
*/
|
||||
public static final int CACHE_COUNT = 5;
|
||||
|
||||
/**
|
||||
* The number of archives in cache 0.
|
||||
*/
|
||||
public static final int ARCHIVE_COUNT = 9;
|
||||
|
||||
/**
|
||||
* The size of an index.
|
||||
*/
|
||||
public static final int INDEX_SIZE = 6;
|
||||
|
||||
/**
|
||||
* The size of a header.
|
||||
*/
|
||||
public static final int HEADER_SIZE = 8;
|
||||
|
||||
/**
|
||||
* The size of a chunk.
|
||||
*/
|
||||
public static final int CHUNK_SIZE = 512;
|
||||
|
||||
/**
|
||||
* The size of a block.
|
||||
*/
|
||||
public static final int BLOCK_SIZE = HEADER_SIZE + CHUNK_SIZE;
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation.
|
||||
*/
|
||||
private FileSystemConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
/**
|
||||
* An {@link Index} points to a file in the {@code main_file_cache.dat} file.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Index {
|
||||
|
||||
/**
|
||||
* Decodes a buffer into an index.
|
||||
* @param buffer The buffer.
|
||||
* @return The decoded {@link Index}.
|
||||
* @throws IllegalArgumentException if the buffer length is invalid.
|
||||
*/
|
||||
public static Index decode(byte[] buffer) {
|
||||
if (buffer.length != FileSystemConstants.INDEX_SIZE) {
|
||||
throw new IllegalArgumentException("Incorrect buffer length.");
|
||||
}
|
||||
|
||||
int size = ((buffer[0] & 0xFF) << 16) | ((buffer[1] & 0xFF) << 8) | (buffer[2] & 0xFF);
|
||||
int block = ((buffer[3] & 0xFF) << 16) | ((buffer[4] & 0xFF) << 8) | (buffer[5] & 0xFF);
|
||||
|
||||
return new Index(size, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the file.
|
||||
*/
|
||||
private final int size;
|
||||
|
||||
/**
|
||||
* The first block of the file.
|
||||
*/
|
||||
private final int block;
|
||||
|
||||
/**
|
||||
* Creates the index.
|
||||
* @param size The size of the file.
|
||||
* @param block The first block of the file.
|
||||
*/
|
||||
public Index(int size, int block) {
|
||||
this.size = size;
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the file.
|
||||
* @return The size of the file.
|
||||
*/
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first block of the file.
|
||||
* @return The first block of the file.
|
||||
*/
|
||||
public int getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
/**
|
||||
* A file system based on top of the operating system's file system. It
|
||||
* consists of a data file and index files. Index files point to blocks in the
|
||||
* data file, which contains the actual data.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class IndexedFileSystem implements Closeable {
|
||||
|
||||
/**
|
||||
* Read only flag.
|
||||
*/
|
||||
private final boolean readOnly;
|
||||
|
||||
/**
|
||||
* The index files.
|
||||
*/
|
||||
private RandomAccessFile[] indices = new RandomAccessFile[256];
|
||||
|
||||
/**
|
||||
* The data file.
|
||||
*/
|
||||
private RandomAccessFile data;
|
||||
|
||||
/**
|
||||
* The cached CRC table.
|
||||
*/
|
||||
private ByteBuffer crcTable;
|
||||
|
||||
/**
|
||||
* Creates the file system with the specified base directory.
|
||||
* @param base The base directory.
|
||||
* @param readOnly A flag indicating if the file system will be read only.
|
||||
* @throws Exception if the file system is invalid.
|
||||
*/
|
||||
public IndexedFileSystem(File base, boolean readOnly) throws Exception {
|
||||
this.readOnly = readOnly;
|
||||
detectLayout(base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this {@link IndexedFileSystem} is read only.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically detect the layout of the specified directory.
|
||||
* @param base The base directory.
|
||||
* @throws Exception if the file system is invalid.
|
||||
*/
|
||||
private void detectLayout(File base) throws Exception {
|
||||
int indexCount = 0;
|
||||
for (int index = 0; index < indices.length; index++) {
|
||||
File f = new File(base.getAbsolutePath() + "/main_file_cache.idx" + index);
|
||||
if (f.exists() && !f.isDirectory()) {
|
||||
indexCount++;
|
||||
indices[index] = new RandomAccessFile(f, readOnly ? "r" : "rw");
|
||||
}
|
||||
}
|
||||
if (indexCount <= 0) {
|
||||
throw new Exception("No index file(s) present");
|
||||
}
|
||||
|
||||
File oldEngineData = new File(base.getAbsolutePath() + "/main_file_cache.dat");
|
||||
File newEngineData = new File(base.getAbsolutePath() + "/main_file_cache.dat2");
|
||||
if (oldEngineData.exists() && !oldEngineData.isDirectory()) {
|
||||
data = new RandomAccessFile(oldEngineData, readOnly ? "r" : "rw");
|
||||
} else if (newEngineData.exists() && !oldEngineData.isDirectory()) {
|
||||
data = new RandomAccessFile(newEngineData, readOnly ? "r" : "rw");
|
||||
} else {
|
||||
throw new Exception("No data file present");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a file.
|
||||
* @param fd The {@link FileDescriptor} which points to the file.
|
||||
* @return The {@link Index}.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private Index getIndex(FileDescriptor fd) throws IOException {
|
||||
int index = fd.getType();
|
||||
if (index < 0 || index >= indices.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[FileSystemConstants.INDEX_SIZE];
|
||||
RandomAccessFile indexFile = indices[index];
|
||||
synchronized (indexFile) {
|
||||
long ptr = (long) fd.getFile() * (long) FileSystemConstants.INDEX_SIZE;
|
||||
if (ptr >= 0 && indexFile.length() >= (ptr + FileSystemConstants.INDEX_SIZE)) {
|
||||
indexFile.seek(ptr);
|
||||
indexFile.readFully(buffer);
|
||||
} else {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
return Index.decode(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of files with the specified type.
|
||||
* @param type The type.
|
||||
* @return The number of files.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private int getFileCount(int type) throws IOException {
|
||||
if (type < 0 || type >= indices.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
RandomAccessFile indexFile = indices[type];
|
||||
synchronized (indexFile) {
|
||||
return (int) (indexFile.length() / FileSystemConstants.INDEX_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CRC table.
|
||||
* @return The CRC table.
|
||||
* @throws IOException if an I/O erorr occurs.
|
||||
*/
|
||||
public ByteBuffer getCrcTable() throws IOException {
|
||||
if (readOnly) {
|
||||
synchronized (this) {
|
||||
if (crcTable != null) {
|
||||
return crcTable.duplicate();
|
||||
}
|
||||
}
|
||||
|
||||
// the number of archives
|
||||
int archives = getFileCount(0);
|
||||
|
||||
// the hash
|
||||
int hash = 1234;
|
||||
|
||||
// the CRCs
|
||||
int[] crcs = new int[archives];
|
||||
|
||||
// calculate the CRCs
|
||||
CRC32 crc32 = new CRC32();
|
||||
for (int i = 1; i < crcs.length; i++) {
|
||||
crc32.reset();
|
||||
|
||||
ByteBuffer bb = getFile(0, i);
|
||||
byte[] bytes = new byte[bb.remaining()];
|
||||
bb.get(bytes, 0, bytes.length);
|
||||
crc32.update(bytes, 0, bytes.length);
|
||||
|
||||
crcs[i] = (int) crc32.getValue();
|
||||
}
|
||||
|
||||
// hash the CRCs and place them in the buffer
|
||||
ByteBuffer buf = ByteBuffer.allocate(crcs.length * 4 + 4);
|
||||
for (int i = 0; i < crcs.length; i++) {
|
||||
hash = (hash << 1) + crcs[i];
|
||||
buf.putInt(crcs[i]);
|
||||
}
|
||||
|
||||
// place the hash into the buffer
|
||||
buf.putInt(hash);
|
||||
buf.flip();
|
||||
|
||||
synchronized (this) {
|
||||
crcTable = buf.asReadOnlyBuffer();
|
||||
return crcTable.duplicate();
|
||||
}
|
||||
} else {
|
||||
throw new IOException("cannot get CRC table from a writable file system");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file.
|
||||
* @param type The file type.
|
||||
* @param file The file id.
|
||||
* @return A {@link ByteBuffer} which contains the contents of the file.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public ByteBuffer getFile(int type, int file) throws IOException {
|
||||
return getFile(new FileDescriptor(type, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file.
|
||||
* @param fd The {@link FileDescriptor} which points to the file.
|
||||
* @return A {@link ByteBuffer} which contains the contents of the file.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public ByteBuffer getFile(FileDescriptor fd) throws IOException {
|
||||
Index index = getIndex(fd);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(index.getSize());
|
||||
|
||||
// calculate some initial values
|
||||
long ptr = (long) index.getBlock() * (long) FileSystemConstants.BLOCK_SIZE;
|
||||
int read = 0;
|
||||
int size = index.getSize();
|
||||
int blocks = size / FileSystemConstants.CHUNK_SIZE;
|
||||
if (size % FileSystemConstants.CHUNK_SIZE != 0) {
|
||||
blocks++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < blocks; i++) {
|
||||
|
||||
// read header
|
||||
byte[] header = new byte[FileSystemConstants.HEADER_SIZE];
|
||||
synchronized (data) {
|
||||
data.seek(ptr);
|
||||
data.readFully(header);
|
||||
}
|
||||
|
||||
// increment pointers
|
||||
ptr += FileSystemConstants.HEADER_SIZE;
|
||||
|
||||
// parse header
|
||||
int nextFile = ((header[0] & 0xFF) << 8) | (header[1] & 0xFF);
|
||||
int curChunk = ((header[2] & 0xFF) << 8) | (header[3] & 0xFF);
|
||||
int nextBlock = ((header[4] & 0xFF) << 16) | ((header[5] & 0xFF) << 8) | (header[6] & 0xFF);
|
||||
int nextType = header[7] & 0xFF;
|
||||
|
||||
// check expected chunk id is correct
|
||||
if (i != curChunk) {
|
||||
throw new IOException("Chunk id mismatch.");
|
||||
}
|
||||
|
||||
// calculate how much we can read
|
||||
int chunkSize = size - read;
|
||||
if (chunkSize > FileSystemConstants.CHUNK_SIZE) {
|
||||
chunkSize = FileSystemConstants.CHUNK_SIZE;
|
||||
}
|
||||
|
||||
// read the next chunk and put it in the buffer
|
||||
byte[] chunk = new byte[chunkSize];
|
||||
synchronized (data) {
|
||||
data.seek(ptr);
|
||||
data.readFully(chunk);
|
||||
}
|
||||
buffer.put(chunk);
|
||||
|
||||
// increment pointers
|
||||
read += chunkSize;
|
||||
ptr = (long) nextBlock * (long) FileSystemConstants.BLOCK_SIZE;
|
||||
|
||||
// if we still have more data to read, check the validity of the
|
||||
// header
|
||||
if (size > read) {
|
||||
if (nextType != (fd.getType() + 1)) {
|
||||
throw new IOException("File type mismatch.");
|
||||
}
|
||||
|
||||
if (nextFile != fd.getFile()) {
|
||||
throw new IOException("File id mismatch.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (data != null) {
|
||||
synchronized (data) {
|
||||
data.close();
|
||||
}
|
||||
}
|
||||
|
||||
for (RandomAccessFile index : indices) {
|
||||
if (index != null) {
|
||||
synchronized (index) {
|
||||
index.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.apollo.fs.archive;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.apollo.util.ByteBufferUtil;
|
||||
import org.apollo.util.CompressionUtil;
|
||||
|
||||
/**
|
||||
* Represents an archive.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Archive {
|
||||
|
||||
/**
|
||||
* Decodes the archive in the specified buffer.
|
||||
* @param buffer The buffer.
|
||||
* @return The archive.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public static Archive decode(ByteBuffer buffer) throws IOException {
|
||||
int extractedSize = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
int size = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
boolean extracted = false;
|
||||
|
||||
if (size != extractedSize) {
|
||||
byte[] compressed = new byte[size];
|
||||
byte[] uncompressed = new byte[extractedSize];
|
||||
buffer.get(compressed);
|
||||
CompressionUtil.unbzip2(compressed, uncompressed);
|
||||
buffer = ByteBuffer.wrap(uncompressed);
|
||||
extracted = true;
|
||||
}
|
||||
|
||||
int entries = buffer.getShort() & 0xFFFF;
|
||||
int[] identifiers = new int[entries];
|
||||
int[] extractedSizes = new int[entries];
|
||||
int[] sizes = new int[entries];
|
||||
|
||||
for (int i = 0; i < entries; i++) {
|
||||
identifiers[i] = buffer.getInt();
|
||||
extractedSizes[i] = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
sizes[i] = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
}
|
||||
|
||||
ArchiveEntry[] entry = new ArchiveEntry[entries];
|
||||
|
||||
for (int i = 0; i < entries; i++) {
|
||||
ByteBuffer entryBuffer = ByteBuffer.allocate(extractedSizes[i]);
|
||||
if (!extracted) {
|
||||
byte[] compressed = new byte[sizes[i]];
|
||||
byte[] uncompressed = new byte[extractedSizes[i]];
|
||||
buffer.get(compressed);
|
||||
CompressionUtil.unbzip2(compressed, uncompressed);
|
||||
entryBuffer = ByteBuffer.wrap(uncompressed);
|
||||
}
|
||||
entry[i] = new ArchiveEntry(identifiers[i], entryBuffer);
|
||||
}
|
||||
|
||||
return new Archive(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* The entries in this archive.
|
||||
*/
|
||||
private final ArchiveEntry[] entries;
|
||||
|
||||
/**
|
||||
* Creates a new archive.
|
||||
* @param entries The entries in this archive.
|
||||
*/
|
||||
public Archive(ArchiveEntry[] entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entry by its name.
|
||||
* @param name The name.
|
||||
* @return The entry.
|
||||
* @throws FileNotFoundException if the file could not be found.
|
||||
*/
|
||||
public ArchiveEntry getEntry(String name) throws FileNotFoundException {
|
||||
int hash = 0;
|
||||
name = name.toUpperCase();
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
hash = (hash * 61 + name.charAt(i)) - 32;
|
||||
}
|
||||
for (ArchiveEntry entry : entries) {
|
||||
if (entry.getIdentifier() == hash) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.apollo.fs.archive;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents a single entry in an {@link Archive}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ArchiveEntry {
|
||||
|
||||
/**
|
||||
* The identifier of this entry.
|
||||
*/
|
||||
private final int identifier;
|
||||
|
||||
/**
|
||||
* The buffer of this entry.
|
||||
*/
|
||||
private final ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* Creates a new archive entry.
|
||||
* @param identifier The identifier.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
public ArchiveEntry(int identifier, ByteBuffer buffer) {
|
||||
this.identifier = identifier;
|
||||
this.buffer = buffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identifier of this entry.
|
||||
* @return The identifier of this entry.
|
||||
*/
|
||||
public int getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the buffer of this entry.
|
||||
* @return This buffer of this entry.
|
||||
*/
|
||||
public ByteBuffer getBuffer() {
|
||||
return buffer.duplicate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes which deal with archives.
|
||||
*/
|
||||
package org.apollo.fs.archive;
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Contains classes which deal with the file system that the client uses to
|
||||
* store game data files.
|
||||
*/
|
||||
package org.apollo.fs;
|
||||
@@ -0,0 +1,186 @@
|
||||
package org.apollo.fs.parser;
|
||||
|
||||
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.ItemDefinition;
|
||||
import org.apollo.util.ByteBufferUtil;
|
||||
|
||||
/**
|
||||
* A class which parses item definitions.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ItemDefinitionParser {
|
||||
|
||||
/**
|
||||
* The indexed file system.
|
||||
*/
|
||||
private final IndexedFileSystem fs;
|
||||
|
||||
/**
|
||||
* Creates the item definition parser.
|
||||
* @param fs The indexed file system.
|
||||
*/
|
||||
public ItemDefinitionParser(IndexedFileSystem fs) {
|
||||
this.fs = fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the item definitions.
|
||||
* @return The item definitions.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public ItemDefinition[] parse() throws IOException {
|
||||
Archive config = Archive.decode(fs.getFile(0, 2));
|
||||
ByteBuffer dat = config.getEntry("obj.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("obj.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();
|
||||
}
|
||||
|
||||
ItemDefinition[] defs = new ItemDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
dat.position(indices[i]);
|
||||
defs[i] = parseDefinition(i, dat);
|
||||
}
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single definition.
|
||||
* @param id The item's id.
|
||||
* @param buffer The buffer.
|
||||
* @return The definition.
|
||||
*/
|
||||
private ItemDefinition parseDefinition(int id, ByteBuffer buffer) {
|
||||
ItemDefinition def = new ItemDefinition(id);
|
||||
|
||||
while (true) {
|
||||
int code = buffer.get() & 0xFF;
|
||||
|
||||
if (code == 0) {
|
||||
return def;
|
||||
} else if (code == 1) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelId = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 2) {
|
||||
def.setName(ByteBufferUtil.readString(buffer));
|
||||
} else if (code == 3) {
|
||||
def.setDescription(ByteBufferUtil.readString(buffer));
|
||||
} else if (code == 4) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelScale = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 5) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelRotationX = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 6) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelRotationY = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 7) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelTransformationX = buffer.getShort();
|
||||
} else if (code == 8) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelTransformationY = buffer.getShort();
|
||||
} else if (code == 10) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 11) {
|
||||
def.setStackable(true);
|
||||
} else if (code == 12) {
|
||||
def.setValue(buffer.getInt());
|
||||
} else if (code == 16) {
|
||||
def.setMembersOnly(true);
|
||||
} else if (code == 23) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int unknownByte = buffer.get();
|
||||
} else if (code == 24) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 25) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int unknownByte = buffer.get();
|
||||
} else if (code == 26) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
} else if (code >= 30 && code < 35) {
|
||||
String str = ByteBufferUtil.readString(buffer);
|
||||
if (str.equalsIgnoreCase("hidden")) {
|
||||
str = null;
|
||||
}
|
||||
def.setGroundAction(code - 30, str);
|
||||
} else if (code >= 35 && code < 40) {
|
||||
String str = ByteBufferUtil.readString(buffer);
|
||||
def.setInventoryAction(code - 35, str);
|
||||
} else if (code == 40) {
|
||||
int colorCount = buffer.get() & 0xFF;
|
||||
for (int i = 0; i < colorCount; i++) {
|
||||
@SuppressWarnings("unused")
|
||||
int oldColor = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int newColor = buffer.getShort() & 0xFFFF;
|
||||
}
|
||||
} else if (code == 78) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 79) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 90) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 91) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 92) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 93) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 95) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 97) {
|
||||
int noteInfoId = buffer.getShort() & 0xFFFF;
|
||||
def.setNoteInfoId(noteInfoId);
|
||||
} else if (code == 98) {
|
||||
int noteGraphicId = buffer.getShort() & 0xFFFF;
|
||||
def.setNoteGraphicId(noteGraphicId);
|
||||
} else if (code >= 100 && code < 110) {
|
||||
@SuppressWarnings("unused")
|
||||
int stackId = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int stackAmount = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 110) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 111) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 112) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 114) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() * 5;
|
||||
} else if (code == 115) {
|
||||
@SuppressWarnings("unused")
|
||||
int team = buffer.get() & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes which parse files within the game's cache.
|
||||
*/
|
||||
package org.apollo.fs.parser;
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.apollo.game;
|
||||
|
||||
/**
|
||||
* Contains game-related constants.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class GameConstants {
|
||||
|
||||
/**
|
||||
* The delay between consecutive pulses, in milliseconds.
|
||||
*/
|
||||
public static final int PULSE_DELAY = 600;
|
||||
|
||||
/**
|
||||
* The maximum events per pulse per session.
|
||||
*/
|
||||
public static final int EVENTS_PER_PULSE = 10;
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation by other classes.
|
||||
*/
|
||||
private GameConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.apollo.game;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A class which handles the logic for each pulse of the {@link GameService}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class GamePulseHandler implements Runnable {
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(GamePulseHandler.class.getName());
|
||||
|
||||
/**
|
||||
* The {@link GameService}.
|
||||
*/
|
||||
private final GameService service;
|
||||
|
||||
/**
|
||||
* Creates the game pulse handler object.
|
||||
* @param service The {@link GameService}.
|
||||
*/
|
||||
GamePulseHandler(GameService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
service.pulse();
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "Exception during pulse.", t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package org.apollo.game;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apollo.Service;
|
||||
import org.apollo.game.event.handler.chain.EventHandlerChainGroup;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.World;
|
||||
import org.apollo.game.model.World.RegistrationStatus;
|
||||
import org.apollo.game.sync.ClientSynchronizer;
|
||||
import org.apollo.io.EventHandlerChainParser;
|
||||
import org.apollo.login.LoginService;
|
||||
import org.apollo.net.session.GameSession;
|
||||
import org.apollo.util.NamedThreadFactory;
|
||||
import org.apollo.util.xml.XmlNode;
|
||||
import org.apollo.util.xml.XmlParser;
|
||||
|
||||
/**
|
||||
* The {@link GameService} class schedules and manages the execution of the
|
||||
* {@link GamePulseHandler} class.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class GameService extends Service {
|
||||
|
||||
/**
|
||||
* The number of times to unregister players per cycle. This is to ensure
|
||||
* the saving threads don't get swamped with requests and slow everything
|
||||
* down.
|
||||
*/
|
||||
private static final int UNREGISTERS_PER_CYCLE = 50;
|
||||
|
||||
/**
|
||||
* The scheduled executor service.
|
||||
*/
|
||||
private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("GameService"));
|
||||
|
||||
/**
|
||||
* A queue of players to remove.
|
||||
*/
|
||||
private final Queue<Player> oldPlayers = new ConcurrentLinkedQueue<Player>();
|
||||
|
||||
/**
|
||||
* The {@link EventHandlerChainGroup}.
|
||||
*/
|
||||
private EventHandlerChainGroup chainGroup;
|
||||
|
||||
/**
|
||||
* The {@link ClientSynchronizer}.
|
||||
*/
|
||||
private ClientSynchronizer synchronizer;
|
||||
|
||||
/**
|
||||
* Creates the game service.
|
||||
* @throws Exception if an error occurs during initialization.
|
||||
*/
|
||||
public GameService() throws Exception {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event handler chains.
|
||||
* @return The event handler chains.
|
||||
*/
|
||||
public EventHandlerChainGroup getEventHandlerChains() {
|
||||
return chainGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the game service.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
private void init() throws Exception {
|
||||
InputStream is = new FileInputStream("data/events.xml");
|
||||
try {
|
||||
EventHandlerChainParser chainGroupParser = new EventHandlerChainParser(is);
|
||||
chainGroup = chainGroupParser.parse();
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
|
||||
is = new FileInputStream("data/synchronizer.xml");
|
||||
try {
|
||||
XmlParser parser = new XmlParser();
|
||||
XmlNode rootNode = parser.parse(is);
|
||||
|
||||
if (!rootNode.getName().equals("synchronizer")) {
|
||||
throw new Exception("Invalid root node name.");
|
||||
}
|
||||
|
||||
XmlNode activeNode = rootNode.getChild("active");
|
||||
if (activeNode == null || !activeNode.hasValue()) {
|
||||
throw new Exception("No active node/value.");
|
||||
}
|
||||
|
||||
Class<?> clazz = Class.forName(activeNode.getValue());
|
||||
synchronizer = (ClientSynchronizer) clazz.newInstance();
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the game service.
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
scheduledExecutor.scheduleAtFixedRate(new GamePulseHandler(this), GameConstants.PULSE_DELAY, GameConstants.PULSE_DELAY, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every pulse.
|
||||
*/
|
||||
public void pulse() {
|
||||
synchronized (this) {
|
||||
LoginService loginService = getContext().getService(LoginService.class);
|
||||
World world = World.getWorld();
|
||||
|
||||
int unregistered = 0;
|
||||
Player old;
|
||||
while (unregistered < UNREGISTERS_PER_CYCLE && (old = oldPlayers.poll()) != null) {
|
||||
loginService.submitSaveRequest(old.getSession(), old);
|
||||
unregistered++;
|
||||
}
|
||||
|
||||
for (Player p : world.getPlayerRepository()) {
|
||||
GameSession session = p.getSession();
|
||||
if (session != null) {
|
||||
session.handlePendingEvents(chainGroup);
|
||||
}
|
||||
}
|
||||
|
||||
world.pulse();
|
||||
|
||||
synchronizer.synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a player (may block!).
|
||||
* @param player The player.
|
||||
* @return A {@link RegistrationStatus}.
|
||||
*/
|
||||
public RegistrationStatus registerPlayer(Player player) {
|
||||
synchronized (this) {
|
||||
return World.getWorld().register(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a player. Returns immediately. The player is unregistered
|
||||
* at the start of the next cycle.
|
||||
* @param player The player.
|
||||
*/
|
||||
public void unregisterPlayer(Player player) {
|
||||
oldPlayers.add(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the unregistration of a player.
|
||||
* @param player The player.
|
||||
*/
|
||||
public void finalizePlayerUnregistration(Player player) {
|
||||
synchronized (this) {
|
||||
World.getWorld().unregister(player);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.apollo.game.action;
|
||||
|
||||
import org.apollo.game.model.Character;
|
||||
import org.apollo.game.scheduling.ScheduledTask;
|
||||
|
||||
/**
|
||||
* An action is a specialised {@link ScheduledTask} which is specific to a
|
||||
* character.
|
||||
* <p>
|
||||
* <strong>ALL</strong> actions <strong>MUST</strong> implement the
|
||||
* {@link #equals(Object)} method. This is to check if two actions are
|
||||
* identical: if they are, then the new action does not replace the old one (so
|
||||
* spam/accidental clicking won't cancel your action, and start another from
|
||||
* scratch).
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class Action<T extends Character> extends ScheduledTask {
|
||||
|
||||
/**
|
||||
* The character performing the action.
|
||||
*/
|
||||
private final T character;
|
||||
|
||||
/**
|
||||
* A flag indicating if this action is stopping.
|
||||
*/
|
||||
private boolean stopping = false;
|
||||
|
||||
/**
|
||||
* Creates a new action.
|
||||
* @param delay The delay in pulses.
|
||||
* @param immediate A flag indicating if the action should happen
|
||||
* immediately.
|
||||
* @param character The character performing the action.
|
||||
*/
|
||||
public Action(int delay, boolean immediate, T character) {
|
||||
super(delay, immediate);
|
||||
this.character = character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character which performed the action.
|
||||
* @return The character.
|
||||
*/
|
||||
public T getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
if (!stopping) {
|
||||
stopping = true;
|
||||
character.stopAction();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.apollo.game.action;
|
||||
|
||||
import org.apollo.game.model.Character;
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An @{link Action} which fires when a distance requirement is met.
|
||||
* @author Blake
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class DistancedAction<T extends Character> extends Action<T> {
|
||||
|
||||
/**
|
||||
* The position to distance check with.
|
||||
*/
|
||||
private final Position position;
|
||||
|
||||
/**
|
||||
* The minimum distance before the action fires.
|
||||
*/
|
||||
private final int distance;
|
||||
|
||||
/**
|
||||
* The delay once the threshold is reached.
|
||||
*/
|
||||
private final int delay;
|
||||
|
||||
/**
|
||||
* A flag indicating if this action fires immediately after the threshold
|
||||
* is reached.
|
||||
*/
|
||||
private final boolean immediate;
|
||||
|
||||
/**
|
||||
* A flag indicating if the distance has been reached yet.
|
||||
*/
|
||||
private boolean reached = false;
|
||||
|
||||
/**
|
||||
* Creates a new DistancedAction.
|
||||
* @param delay The delay between executions once the distance threshold is
|
||||
* reached.
|
||||
* @param immediate Whether or not this action fires immediately after the
|
||||
* distance threshold is reached.
|
||||
* @param character The character.
|
||||
* @param position The position.
|
||||
* @param distance The distance.
|
||||
*/
|
||||
public DistancedAction(int delay, boolean immediate, T character, Position position, int distance) {
|
||||
super(0, true, character);
|
||||
this.position = position;
|
||||
this.distance = distance;
|
||||
this.delay = delay;
|
||||
this.immediate = immediate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (reached) {
|
||||
// some actions (e.g. agility) will cause the player to move away again
|
||||
// so we don't check once the player got close enough once
|
||||
executeAction();
|
||||
} else if (getCharacter().getPosition().getDistance(position) <= distance) {
|
||||
reached = true;
|
||||
setDelay(delay);
|
||||
if (immediate) { // TODO: required?
|
||||
executeAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the actual action. Called when the distance requirement is met.
|
||||
*/
|
||||
public abstract void executeAction();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Contains classes related to actions, specialised scheduled tasks which
|
||||
* characters perform.
|
||||
*/
|
||||
package org.apollo.game.action;
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
/**
|
||||
* Represents a command.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Command {
|
||||
|
||||
/**
|
||||
* The name of the command.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* The command's arguments.
|
||||
*/
|
||||
private final String[] arguments;
|
||||
|
||||
/**
|
||||
* Creates the command.
|
||||
* @param name The name of the command.
|
||||
* @param arguments The command's arguments.
|
||||
*/
|
||||
public Command(String name, String[] arguments) {
|
||||
this.name = name;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command.
|
||||
* @return The name of the command.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command's arguments.
|
||||
* @return The command's arguments.
|
||||
*/
|
||||
public String[] getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A class which dispatches {@link Command}s to {@link CommandListener}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CommandDispatcher {
|
||||
|
||||
/**
|
||||
* A map of event listeners.
|
||||
*/
|
||||
private final Map<String, CommandListener> listeners = new HashMap<String, CommandListener>();
|
||||
|
||||
/**
|
||||
* Creates the command dispatcher and registers a listener for the credits
|
||||
* command.
|
||||
*/
|
||||
public CommandDispatcher() {
|
||||
// not in a plugin so it is harder for people to remove!
|
||||
listeners.put("credits", new CreditsCommandListener());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener with the
|
||||
* @param command The command's name.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void register(String command, CommandListener listener) {
|
||||
listeners.put(command.toLowerCase(), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a command to the appropriate listener.
|
||||
* @param player The player.
|
||||
* @param command The command.
|
||||
*/
|
||||
public void dispatch(Player player, Command command) {
|
||||
CommandListener listener = listeners.get(command.getName().toLowerCase());
|
||||
if (listener != null) {
|
||||
listener.execute(player, command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An interface which should be implemented by classes to listen to
|
||||
* {@link Command}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public interface CommandListener {
|
||||
|
||||
/**
|
||||
* Executes the action for this command.
|
||||
* @param player The player.
|
||||
* @param command The command.
|
||||
*/
|
||||
public void execute(Player player, Command command);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apollo.game.event.impl.SetInterfaceTextEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.World;
|
||||
import org.apollo.game.model.inter.quest.QuestConstants;
|
||||
import org.apollo.util.plugin.PluginManager;
|
||||
|
||||
/**
|
||||
* Implements a {@code ::credits} command that lists the authors of all plugins
|
||||
* used in the server.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CreditsCommandListener implements CommandListener {
|
||||
|
||||
/*
|
||||
* If you are considering removing this command, please bear in mind that
|
||||
* Apollo took several people thousands of hours to create. We released it
|
||||
* to the world for free and it isn't much to ask to leave this command in.
|
||||
* It isn't very obtrusive and gives us some well-deserved recognition for
|
||||
* the work we have done. Thank you!
|
||||
*
|
||||
* The list of authors is generated from the plugin manager. If you create
|
||||
* a custom plugin, make sure you add your name to the plugin.xml file and
|
||||
* it'll appear here automatically!
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void execute(Player player, Command command) {
|
||||
PluginManager mgr = World.getWorld().getPluginManager();
|
||||
Iterator<String> it = mgr.createAuthorsIterator();
|
||||
|
||||
int pos = 0;
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dre@Apollo"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dre@Introduction"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "This server is based on Apollo, a lightweight, fast, secure"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "and open-source RuneScape emulator. For more"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "information about Apollo, visit the website at:"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dbl@https://github.com/apollo-rsps/apollo"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "Apollo is released under the terms of the ISC"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "license, details can be found in the root folder of the "));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "Apollo distribution."));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dre@Credits"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
|
||||
for (; pos < QuestConstants.QUEST_TEXT.length; pos++) {
|
||||
String text = it.hasNext() ? it.next() : "";
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos], text));
|
||||
}
|
||||
|
||||
player.getInterfaceSet().openWindow(QuestConstants.QUEST_INTERFACE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.Player.PrivilegeLevel;
|
||||
|
||||
/**
|
||||
* A {@link CommandListener} which checks the {@link PrivilegeLevel} of the
|
||||
* {@link Player} executing the command.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class PrivilegedCommandListener implements CommandListener {
|
||||
|
||||
/**
|
||||
* The minimum privilege level.
|
||||
*/
|
||||
private final PrivilegeLevel level;
|
||||
|
||||
/**
|
||||
* Creates the privileged command listener with the specified minimum level.
|
||||
* @param level The minimum privilege level.
|
||||
*/
|
||||
public PrivilegedCommandListener(PrivilegeLevel level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a privileged command.
|
||||
* @param player The player.
|
||||
* @param command The command.
|
||||
*/
|
||||
public abstract void executePrivileged(Player player, Command command);
|
||||
|
||||
@Override
|
||||
public final void execute(Player player, Command command) {
|
||||
if (player.getPrivilegeLevel().toInteger() >= level.toInteger()) {
|
||||
executePrivileged(player, command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes related to in-game commands.
|
||||
*/
|
||||
package org.apollo.game.command;
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.apollo.game.event;
|
||||
|
||||
/**
|
||||
* Represents an event that can occur in the game world.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.apollo.game.event.handler;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A class which handles events.
|
||||
* @author Graham
|
||||
* @param <E> The type of event this class handles.
|
||||
*/
|
||||
public abstract class EventHandler<E extends Event> {
|
||||
|
||||
/**
|
||||
* Handles an event.
|
||||
* @param ctx The context.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
public abstract void handle(EventHandlerContext ctx, Player player, E event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.apollo.game.event.handler;
|
||||
|
||||
import org.apollo.game.event.handler.chain.EventHandlerChain;
|
||||
|
||||
/**
|
||||
* Provides operations specific to an {@link EventHandler} in an
|
||||
* {@link EventHandlerChain}.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class EventHandlerContext {
|
||||
|
||||
/**
|
||||
* Breaks the handler chain.
|
||||
*/
|
||||
public abstract void breakHandlerChain();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.apollo.game.event.handler.chain;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A chain of event handlers.
|
||||
* @author Graham
|
||||
* @param <E> The type of event the handlers in this chain handle.
|
||||
*/
|
||||
public final class EventHandlerChain<E extends Event> {
|
||||
|
||||
/**
|
||||
* The handlers.
|
||||
*/
|
||||
private EventHandler<E>[] handlers;
|
||||
|
||||
/**
|
||||
* Creates the event handler chain.
|
||||
* @param handlers The handlers.
|
||||
*/
|
||||
public EventHandlerChain(EventHandler<E>... handlers) {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically adds an event handler to the end of the chain.
|
||||
* @param handler The handler.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addLast(EventHandler<E> handler) {
|
||||
EventHandler<E>[] old = handlers;
|
||||
handlers = new EventHandler[old.length + 1];
|
||||
System.arraycopy(old, 0, handlers, 0, old.length);
|
||||
handlers[old.length] = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event, passing it down the chain until the chain is broken
|
||||
* or the event reaches the end of the chain.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
public void handle(Player player, E event) {
|
||||
final boolean[] running = new boolean[1];
|
||||
running[0] = true;
|
||||
|
||||
EventHandlerContext ctx = new EventHandlerContext() {
|
||||
|
||||
@Override
|
||||
public void breakHandlerChain() {
|
||||
running[0] = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
for (EventHandler<E> handler : handlers) {
|
||||
handler.handle(ctx, player, event);
|
||||
if (!running[0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.apollo.game.event.handler.chain;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* A group of {@link EventHandlerChain}s classified by the {@link Event} type.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EventHandlerChainGroup {
|
||||
|
||||
/**
|
||||
* The map of event classes to event handler chains.
|
||||
*/
|
||||
private final Map<Class<? extends Event>, EventHandlerChain<?>> chains;
|
||||
|
||||
/**
|
||||
* Creates the event handler chain group.
|
||||
* @param chains The chains map.
|
||||
*/
|
||||
public EventHandlerChainGroup(Map<Class<? extends Event>, EventHandlerChain<?>> chains) {
|
||||
this.chains = chains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link EventHandlerChain} from this group.
|
||||
* @param <E> The type of event.
|
||||
* @param clazz The event class.
|
||||
* @return The {@link EventHandlerChain} if one was found, {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends Event> EventHandlerChain<E> getChain(Class<E> clazz) {
|
||||
return (EventHandlerChain<E>) chains.get(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes related to the chaining of event handlers.
|
||||
*/
|
||||
package org.apollo.game.event.handler.chain;
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ButtonEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} which responds to {@link ButtonEvent}s for
|
||||
* withdrawing items as notes.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class BankButtonEventHandler extends EventHandler<ButtonEvent> {
|
||||
|
||||
/**
|
||||
* The withdraw as item button id.
|
||||
*/
|
||||
private static final int WITHDRAW_AS_ITEM = 5387;
|
||||
|
||||
/**
|
||||
* The withdraw as note button id.
|
||||
*/
|
||||
private static final int WITHDRAW_AS_NOTE = 5386;
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ButtonEvent event) {
|
||||
if (event.getInterfaceId() == WITHDRAW_AS_ITEM) {
|
||||
player.setWithdrawingNotes(false);
|
||||
} else if (event.getInterfaceId() == WITHDRAW_AS_NOTE) {
|
||||
player.setWithdrawingNotes(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ItemActionEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.inter.bank.BankConstants;
|
||||
import org.apollo.game.model.inter.bank.BankDepositEnterAmountListener;
|
||||
import org.apollo.game.model.inter.bank.BankUtils;
|
||||
import org.apollo.game.model.inter.bank.BankWithdrawEnterAmountListener;
|
||||
|
||||
/**
|
||||
* An event handler which handles withdrawing and depositing items from/to a
|
||||
* player's bank.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class BankEventHandler extends EventHandler<ItemActionEvent> {
|
||||
|
||||
/**
|
||||
* Converts an option to an amount.
|
||||
* @param option The option.
|
||||
* @return The amount.
|
||||
* @throws IllegalArgumentException if the option is not legal.
|
||||
*/
|
||||
private static final int optionToAmount(int option) {
|
||||
switch (option) {
|
||||
case 1:
|
||||
return 1;
|
||||
case 2:
|
||||
return 5;
|
||||
case 3:
|
||||
return 10;
|
||||
case 4:
|
||||
return Integer.MAX_VALUE;
|
||||
case 5:
|
||||
return -1;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
if (!player.getInterfaceSet().contains(BankConstants.BANK_WINDOW_ID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getInterfaceId() == BankConstants.SIDEBAR_INVENTORY_ID) {
|
||||
deposit(ctx, player, event);
|
||||
} else if (event.getInterfaceId() == BankConstants.BANK_INVENTORY_ID) {
|
||||
withdraw(ctx, player, event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a withdraw action.
|
||||
* @param ctx The event handler context.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
private void withdraw(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
int amount = optionToAmount(event.getOption());
|
||||
if (amount == -1) {
|
||||
player.getInterfaceSet().openEnterAmountDialog(new BankWithdrawEnterAmountListener(player, event.getSlot(), event.getId()));
|
||||
} else {
|
||||
if (!BankUtils.withdraw(player, event.getSlot(), event.getId(), amount)) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a deposit action.
|
||||
* @param ctx The event handler context.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
private void deposit(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
int amount = optionToAmount(event.getOption());
|
||||
if (amount == -1) {
|
||||
player.getInterfaceSet().openEnterAmountDialog(new BankDepositEnterAmountListener(player, event.getSlot(), event.getId()));
|
||||
} else {
|
||||
if (!BankUtils.deposit(player, event.getSlot(), event.getId(), amount)) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.CharacterDesignEvent;
|
||||
import org.apollo.game.event.impl.CloseInterfaceEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A handler which handles {@link CharacterDesignEvent}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CharacterDesignEventHandler extends EventHandler<CharacterDesignEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, CharacterDesignEvent event) {
|
||||
player.setAppearance(event.getAppearance());
|
||||
player.setDesignedCharacter(true);
|
||||
player.send(new CloseInterfaceEvent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.CharacterDesignEvent;
|
||||
import org.apollo.game.model.Appearance;
|
||||
import org.apollo.game.model.Gender;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A handler which verifies {@link CharacterDesignEvent}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CharacterDesignVerificationHandler extends EventHandler<CharacterDesignEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, CharacterDesignEvent event) {
|
||||
if (!valid(event.getAppearance()) || player.hasDesignedCharacter()) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an appearance combination is valid.
|
||||
* @param appearance The appearance combination.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
private boolean valid(Appearance appearance) {
|
||||
int[] colors = appearance.getColors();
|
||||
int[] maxColors = new int[] { 11, 15, 15, 5, 7 };
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
if (colors[i] < 0 || colors[i] > maxColors[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Gender gender = appearance.getGender();
|
||||
if (gender == Gender.MALE) {
|
||||
return validMaleStyle(appearance);
|
||||
} else if (gender == Gender.FEMALE) {
|
||||
return validFemaleStyle(appearance);
|
||||
} else {
|
||||
return false; // maybe null?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a {@link Gender#MALE} style combination is valid.
|
||||
* @param appearance The appearance combination.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
private boolean validMaleStyle(Appearance appearance) {
|
||||
int[] styles = appearance.getStyle();
|
||||
int[] minStyles = new int[] { 0, 10, 18, 26, 33, 36, 42 };
|
||||
int[] maxStyles = new int[] { 8, 17, 25, 31, 34, 40, 43 };
|
||||
for (int i = 0; i < styles.length; i++) {
|
||||
if (styles[i] < minStyles[i] || styles[i] > maxStyles[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a {@link Gender#FEMALE} style combination is valid.
|
||||
* @param appearance The appearance combination.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
private boolean validFemaleStyle(Appearance appearance) {
|
||||
int[] styles = appearance.getStyle();
|
||||
int[] minStyles = new int[] { 45, 255, 56, 61, 67, 70, 79 };
|
||||
int[] maxStyles = new int[] { 54, 255, 60, 65, 68, 77, 80 };
|
||||
for (int i = 0; i < styles.length; i++) {
|
||||
if (styles[i] < minStyles[i] || styles[i] > maxStyles[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ChatEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.sync.block.SynchronizationBlock;
|
||||
|
||||
/**
|
||||
* An event handler which broadcasts public chat messages.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ChatEventHandler extends EventHandler<ChatEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ChatEvent event) {
|
||||
player.getBlockSet().add(SynchronizationBlock.createChatBlock(player, event));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ChatEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An event handler which verifies chat events.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ChatVerificationHandler extends EventHandler<ChatEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ChatEvent event) {
|
||||
int color = event.getTextColor();
|
||||
int effects = event.getTextEffects();
|
||||
if (color < 0 || color > 11 || effects < 0 || effects > 5) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ClosedInterfaceEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} for the {@link ClosedInterfaceEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ClosedInterfaceEventHandler extends EventHandler<ClosedInterfaceEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ClosedInterfaceEvent event) {
|
||||
player.getInterfaceSet().interfaceClosed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.command.Command;
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.CommandEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.World;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} which dispatches {@link CommandEvent}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CommandEventHandler extends EventHandler<CommandEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, CommandEvent event) {
|
||||
String str = event.getCommand();
|
||||
String[] components = str.split(" ");
|
||||
|
||||
String name = components[0];
|
||||
String[] arguments = new String[components.length - 1];
|
||||
|
||||
System.arraycopy(components, 1, arguments, 0, arguments.length);
|
||||
|
||||
Command command = new Command(name, arguments);
|
||||
|
||||
World.getWorld().getCommandDispatcher().dispatch(player, command);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.EnteredAmountEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} for the {@link EnteredAmountEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EnteredAmountEventHandler extends EventHandler<EnteredAmountEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, EnteredAmountEvent event) {
|
||||
player.getInterfaceSet().enteredAmount(event.getAmount());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.EquipEvent;
|
||||
import org.apollo.game.model.EquipmentConstants;
|
||||
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;
|
||||
|
||||
/**
|
||||
* An event handler which equips items.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EquipEventHandler extends EventHandler<EquipEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, EquipEvent event) {
|
||||
if (event.getInterfaceId() == SynchronizationInventoryListener.INVENTORY_ID) {
|
||||
int slot = event.getSlot();
|
||||
if (slot < 0 || slot >= player.getInventory().capacity()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = player.getInventory().get(slot);
|
||||
if (item == null || item.getId() != event.getId()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
ItemDefinition itemDef = item.getDefinition();
|
||||
EquipmentDefinition equipDef = EquipmentDefinition.forId(item.getId());
|
||||
if (equipDef == null) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
SkillSet skillSet = player.getSkillSet();
|
||||
if (skillSet.getSkill(Skill.ATTACK).getMaximumLevel() < equipDef.getAttackLevel()) {
|
||||
player.sendMessage("You need an Attack level of " + equipDef.getAttackLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.STRENGTH).getMaximumLevel() < equipDef.getStrengthLevel()) {
|
||||
player.sendMessage("You need a Strength level of " + equipDef.getStrengthLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.DEFENCE).getMaximumLevel() < equipDef.getDefenceLevel()) {
|
||||
player.sendMessage("You need a Defence level of " + equipDef.getDefenceLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.RANGED).getMaximumLevel() < equipDef.getRangedLevel()) {
|
||||
player.sendMessage("You need a Ranged level of " + equipDef.getRangedLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.MAGIC).getMaximumLevel() < equipDef.getMagicLevel()) {
|
||||
player.sendMessage("You need a Magic level of " + equipDef.getMagicLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory inventory = player.getInventory();
|
||||
Inventory equipment = player.getEquipment();
|
||||
|
||||
int equipmentSlot = equipDef.getSlot();
|
||||
|
||||
// TODO: equip event decoder for 317, and remove event decoder for both
|
||||
// TODO: put all this into another method somewhere
|
||||
|
||||
// check if there is enough space for a two handed weapon
|
||||
if (equipDef.isTwoHanded()) {
|
||||
Item currentShield = equipment.get(EquipmentConstants.SHIELD);
|
||||
if (currentShield != null) {
|
||||
if (inventory.freeSlots() < 1) {
|
||||
inventory.forceCapacityExceeded();
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if a shield is being added with a two handed weapon
|
||||
boolean removeWeapon = false;
|
||||
if (equipmentSlot == EquipmentConstants.SHIELD) {
|
||||
Item currentWeapon = equipment.get(EquipmentConstants.WEAPON);
|
||||
if (currentWeapon != null) {
|
||||
EquipmentDefinition weaponDef = EquipmentDefinition.forId(currentWeapon.getId());
|
||||
if (weaponDef.isTwoHanded()) {
|
||||
if (inventory.freeSlots() < 1) {
|
||||
inventory.forceCapacityExceeded();
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
removeWeapon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item previous = equipment.get(equipmentSlot);
|
||||
if (itemDef.isStackable() && previous != null && previous.getId() == item.getId()) {
|
||||
// we know the item is there, so we can let the inventory class do its stacking magic
|
||||
inventory.remove(item);
|
||||
Item tmp = equipment.add(item);
|
||||
if (tmp != null) {
|
||||
inventory.add(tmp);
|
||||
}
|
||||
} else {
|
||||
// swap the weapons around
|
||||
Item tmp = equipment.reset(equipmentSlot);
|
||||
equipment.set(equipmentSlot, item);
|
||||
inventory.reset(slot);
|
||||
if (tmp != null) {
|
||||
inventory.add(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
// remove the shield if this weapon is two handed
|
||||
if (equipDef.isTwoHanded()) {
|
||||
Item tmp = equipment.reset(EquipmentConstants.SHIELD);
|
||||
// we know tmp will not be null from the check above
|
||||
inventory.add(tmp);
|
||||
}
|
||||
|
||||
if (removeWeapon) {
|
||||
Item tmp = equipment.reset(EquipmentConstants.WEAPON);
|
||||
// we know tmp will not be null from the check about
|
||||
inventory.add(tmp);
|
||||
}
|
||||
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ItemActionEvent;
|
||||
import org.apollo.game.model.Inventory;
|
||||
import org.apollo.game.model.Item;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.inv.SynchronizationInventoryListener;
|
||||
|
||||
/**
|
||||
* An event handler which removes equipped items.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class RemoveEventHandler extends EventHandler<ItemActionEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
if (event.getOption() == 1 && event.getInterfaceId() == SynchronizationInventoryListener.EQUIPMENT_ID) {
|
||||
Inventory inventory = player.getInventory();
|
||||
Inventory equipment = player.getEquipment();
|
||||
|
||||
int slot = event.getSlot();
|
||||
if (slot < 0 || slot >= equipment.capacity()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = equipment.get(slot);
|
||||
if (item == null || item.getId() != event.getId()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean removed = true;
|
||||
|
||||
inventory.stopFiringEvents();
|
||||
equipment.stopFiringEvents();
|
||||
|
||||
try {
|
||||
equipment.set(slot, null);
|
||||
Item tmp = inventory.add(item);
|
||||
if (tmp != null) {
|
||||
removed = false;
|
||||
equipment.set(slot, tmp);
|
||||
}
|
||||
} finally {
|
||||
inventory.startFiringEvents();
|
||||
equipment.startFiringEvents();
|
||||
}
|
||||
|
||||
if (removed) {
|
||||
inventory.forceRefresh(); // TODO find out the specific slot that got used?
|
||||
equipment.forceRefresh(slot);
|
||||
} else {
|
||||
inventory.forceCapacityExceeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.SwitchItemEvent;
|
||||
import org.apollo.game.model.Inventory;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.inter.bank.BankConstants;
|
||||
import org.apollo.game.model.inv.SynchronizationInventoryListener;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} which updates an {@link Inventory} when the client
|
||||
* sends a {@link SwitchItemEvent} to the server.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class SwitchItemEventHandler extends EventHandler<SwitchItemEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, SwitchItemEvent event) {
|
||||
Inventory inventory;
|
||||
boolean insertPermitted = false;
|
||||
|
||||
// TODO is there a better way of doing this??
|
||||
switch (event.getInterfaceId()) {
|
||||
case SynchronizationInventoryListener.INVENTORY_ID:
|
||||
case BankConstants.SIDEBAR_INVENTORY_ID:
|
||||
inventory = player.getInventory();
|
||||
break;
|
||||
case SynchronizationInventoryListener.EQUIPMENT_ID:
|
||||
inventory = player.getEquipment();
|
||||
break;
|
||||
case BankConstants.BANK_INVENTORY_ID:
|
||||
inventory = player.getBank();
|
||||
insertPermitted = true;
|
||||
break;
|
||||
default:
|
||||
return; // not a known inventory, ignore
|
||||
}
|
||||
|
||||
if (event.getOldSlot() >= 0 && event.getNewSlot() >= 0 && event.getOldSlot() < inventory.capacity() && event.getNewSlot() < inventory.capacity()) {
|
||||
// events must be fired for it to work if a sidebar inv overlay is used
|
||||
inventory.swap(insertPermitted ? event.isInserting() : false, event.getOldSlot(), event.getNewSlot());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.WalkEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.model.WalkingQueue;
|
||||
|
||||
/**
|
||||
* A handler for the {@link WalkEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class WalkEventHandler extends EventHandler<WalkEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, WalkEvent event) {
|
||||
WalkingQueue queue = player.getWalkingQueue();
|
||||
|
||||
Position[] steps = event.getSteps();
|
||||
for (int i = 0; i < steps.length; i++) {
|
||||
Position step = steps[i];
|
||||
if (i == 0) {
|
||||
if (!queue.addFirstStep(step)) {
|
||||
return; /* ignore packet */
|
||||
}
|
||||
} else {
|
||||
queue.addStep(step);
|
||||
}
|
||||
}
|
||||
|
||||
queue.setRunningQueue(event.isRunning());
|
||||
|
||||
if (queue.size() > 0) {
|
||||
player.stopAction();
|
||||
player.getInterfaceSet().close(); // TODO: should this be done if size == 0?
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains event handler implementations.
|
||||
*/
|
||||
package org.apollo.game.event.handler.impl;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes which define abstract event handlers.
|
||||
*/
|
||||
package org.apollo.game.event.handler;
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent when the client clicks a button.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ButtonEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* Creates the button event.
|
||||
* @param interfaceId The interface id.
|
||||
*/
|
||||
public ButtonEvent(int interfaceId) {
|
||||
this.interfaceId = interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Appearance;
|
||||
|
||||
/**
|
||||
* An event sent by the client when the player modifies their character's
|
||||
* design.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CharacterDesignEvent extends Event {
|
||||
|
||||
/**
|
||||
* The appearance.
|
||||
*/
|
||||
private final Appearance appearance;
|
||||
|
||||
/**
|
||||
* Creates the character design event.
|
||||
* @param appearance The appearance.
|
||||
*/
|
||||
public CharacterDesignEvent(Appearance appearance) {
|
||||
this.appearance = appearance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appearance.
|
||||
* @return The appearance.
|
||||
*/
|
||||
public Appearance getAppearance() {
|
||||
return appearance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent by the client to send a public chat message to other players.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ChatEvent extends Event {
|
||||
|
||||
/**
|
||||
* The message.
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* The compressed message.
|
||||
*/
|
||||
private final byte[] compressedMessage;
|
||||
|
||||
/**
|
||||
* The text color.
|
||||
*/
|
||||
private final int color;
|
||||
|
||||
/**
|
||||
* The text effects.
|
||||
*/
|
||||
private final int effects;
|
||||
|
||||
/**
|
||||
* Creates a new chat event.
|
||||
* @param message The message.
|
||||
* @param compressedMessage The compressed message.
|
||||
* @param color The text color.
|
||||
* @param effects The text effects.
|
||||
*/
|
||||
public ChatEvent(String message, byte[] compressedMessage, int color, int effects) {
|
||||
this.message = message;
|
||||
this.compressedMessage = compressedMessage;
|
||||
this.color = color;
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message.
|
||||
* @return The message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text color.
|
||||
* @return The text color.
|
||||
*/
|
||||
public int getTextColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text effects.
|
||||
* @return The text effects.
|
||||
*/
|
||||
public int getTextEffects() {
|
||||
return effects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compressed message.
|
||||
* @return The compressed message.
|
||||
*/
|
||||
public byte[] getCompressedMessage() {
|
||||
return compressedMessage;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event which closes the open interface.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CloseInterfaceEvent extends Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* Sent by the client when the current interface is closed.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ClosedInterfaceEvent extends Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event issued by the client to send a {@code ::} command/
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CommandEvent extends Event {
|
||||
|
||||
/**
|
||||
* The command.
|
||||
*/
|
||||
private final String command;
|
||||
|
||||
/**
|
||||
* Creates the command event.
|
||||
* @param command The command.
|
||||
*/
|
||||
public CommandEvent(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command.
|
||||
* @return The command.
|
||||
*/
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An {@link Event} which is sent to the client to open up the enter amount
|
||||
* interface.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EnterAmountEvent extends Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent by the client when the player has entered an amount.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EnteredAmountEvent extends Event {
|
||||
|
||||
/**
|
||||
* The amount.
|
||||
*/
|
||||
private final int amount;
|
||||
|
||||
/**
|
||||
* Creates the entered amount event.
|
||||
* @param amount The amount.
|
||||
*/
|
||||
public EnteredAmountEvent(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount.
|
||||
* @return The amount.
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent by the client to request that an item is equipped.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EquipEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* The item id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The item's slot.
|
||||
*/
|
||||
private final int slot;
|
||||
|
||||
/**
|
||||
* Creates the equip event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param id The id.
|
||||
* @param slot The slot.
|
||||
*/
|
||||
public EquipEvent(int interfaceId, int id, int slot) {
|
||||
this.interfaceId = interfaceId;
|
||||
this.id = id;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item id.
|
||||
* @return The item id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slot.
|
||||
* @return The slot.
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
/**
|
||||
* The fifth {@link ItemActionEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FifthItemActionEvent extends ItemActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the fifth item action event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param id The item id.
|
||||
* @param slot The item slot.
|
||||
*/
|
||||
public FifthItemActionEvent(int interfaceId, int id, int slot) {
|
||||
super(5, interfaceId, id, slot);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
/**
|
||||
* The first {@link ItemActionEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FirstItemActionEvent extends ItemActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the first item action event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param id The item id.
|
||||
* @param slot The item slot.
|
||||
*/
|
||||
public FirstItemActionEvent(int interfaceId, int id, int slot) {
|
||||
super(1, interfaceId, id, slot);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An event sent when the first option at an object is used.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FirstObjectActionEvent extends ObjectActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the first object action event.
|
||||
* @param id The id.
|
||||
* @param position The position.
|
||||
*/
|
||||
public FirstObjectActionEvent(int id, Position position) {
|
||||
super(1, id, position);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
/**
|
||||
* The fourth {@link ItemActionEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FourthItemActionEvent extends ItemActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the fourth item action event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param id The item id.
|
||||
* @param slot The item slot.
|
||||
*/
|
||||
public FourthItemActionEvent(int interfaceId, int id, int slot) {
|
||||
super(4, interfaceId, id, slot);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event which specifies the local id and membership status of the current
|
||||
* player.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class IdAssignmentEvent extends Event {
|
||||
|
||||
/**
|
||||
* The id of this player.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The membership flag.
|
||||
*/
|
||||
private final boolean members;
|
||||
|
||||
/**
|
||||
* Creates the local id event.
|
||||
* @param id The id.
|
||||
* @param members The membership flag.
|
||||
*/
|
||||
public IdAssignmentEvent(int id, boolean members) {
|
||||
this.id = id;
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id.
|
||||
* @return The id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the membership flag.
|
||||
* @return The membership flag.
|
||||
*/
|
||||
public boolean isMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An {@link Event} which represents some sort of action on an item.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class ItemActionEvent extends Event {
|
||||
|
||||
/**
|
||||
* The option number (1-5).
|
||||
*/
|
||||
private final int option;
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* The item id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The item's slot.
|
||||
*/
|
||||
private final int slot;
|
||||
|
||||
/**
|
||||
* Creates the item action event.
|
||||
* @param option The option number.
|
||||
* @param interfaceId The interface id.
|
||||
* @param id The id.
|
||||
* @param slot The slot.
|
||||
*/
|
||||
public ItemActionEvent(int option, int interfaceId, int id, int slot) {
|
||||
this.option = option;
|
||||
this.interfaceId = interfaceId;
|
||||
this.id = id;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option number.
|
||||
* @return The option number.
|
||||
*/
|
||||
public int getOption() {
|
||||
return option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item id.
|
||||
* @return The item id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slot.
|
||||
* @return The slot.
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event which is periodically sent by the client to keep a connection
|
||||
* alive.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class KeepAliveEvent extends Event {
|
||||
|
||||
/**
|
||||
* The time this event was created.
|
||||
*/
|
||||
private final long createdAt;
|
||||
|
||||
/**
|
||||
* Creates the keep alive event.
|
||||
*/
|
||||
public KeepAliveEvent() {
|
||||
createdAt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time when this event was created.
|
||||
* @return The time when this event was created.
|
||||
*/
|
||||
public long getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent to the client which logs it out cleanly.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class LogoutEvent extends Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An {@link Event} which represents some sort of action at an object.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class ObjectActionEvent extends Event {
|
||||
|
||||
/**
|
||||
* The option number (1-3).
|
||||
*/
|
||||
private final int option;
|
||||
|
||||
/**
|
||||
* The object's id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The object's position.
|
||||
*/
|
||||
private final Position position;
|
||||
|
||||
/**
|
||||
* Creates a new object action event.
|
||||
* @param option The option number.
|
||||
* @param id The id of the object.
|
||||
* @param position The position of the object.
|
||||
*/
|
||||
public ObjectActionEvent(int option, int id, Position position) {
|
||||
this.option = option;
|
||||
this.id = id;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option number.
|
||||
* @return The option number.
|
||||
*/
|
||||
public int getOption() {
|
||||
return option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of the object.
|
||||
* @return The id of the object.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the object.
|
||||
* @return The position of the object.
|
||||
*/
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event which opens an interface.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class OpenInterfaceEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* Creates the event with the specified interface id.
|
||||
* @param id The interface id.
|
||||
*/
|
||||
public OpenInterfaceEvent(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An {@link Event} sent to open an interface and temporary sidebar overlay.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class OpenInterfaceSidebarEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* The sidebar id.
|
||||
*/
|
||||
private final int sidebarId;
|
||||
|
||||
/**
|
||||
* Creates the open interface sidebar event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param sidebarId The sidebar id.
|
||||
*/
|
||||
public OpenInterfaceSidebarEvent(int interfaceId, int sidebarId) {
|
||||
this.interfaceId = interfaceId;
|
||||
this.sidebarId = sidebarId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sidebar id.
|
||||
* @return The sidebar id.
|
||||
*/
|
||||
public int getSidebarId() {
|
||||
return sidebarId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.sync.seg.SynchronizationSegment;
|
||||
|
||||
/**
|
||||
* An event which is sent to synchronize the players.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class PlayerSynchronizationEvent extends Event {
|
||||
|
||||
/**
|
||||
* The last known region.
|
||||
*/
|
||||
private final Position lastKnownRegion;
|
||||
|
||||
/**
|
||||
* The player's position.
|
||||
*/
|
||||
private final Position position;
|
||||
|
||||
/**
|
||||
* A flag indicating if the region has changed.
|
||||
*/
|
||||
private final boolean regionChanged;
|
||||
|
||||
/**
|
||||
* The current player's synchronization segment.
|
||||
*/
|
||||
private final SynchronizationSegment segment;
|
||||
|
||||
/**
|
||||
* The number of local players.
|
||||
*/
|
||||
private final int localPlayers;
|
||||
|
||||
/**
|
||||
* A list of segments.
|
||||
*/
|
||||
private final List<SynchronizationSegment> segments;
|
||||
|
||||
/**
|
||||
* Creates the player synchronization event.
|
||||
* @param lastKnownRegion The last known region.
|
||||
* @param position The player's current position.
|
||||
* @param regionChanged A flag indicating if the region has changed.
|
||||
* @param segment The current player's synchronization segment.
|
||||
* @param localPlayers The number of local players.
|
||||
* @param segments A list of segments.
|
||||
*/
|
||||
public PlayerSynchronizationEvent(Position lastKnownRegion, Position position, boolean regionChanged, SynchronizationSegment segment, int localPlayers, List<SynchronizationSegment> segments) {
|
||||
this.lastKnownRegion = lastKnownRegion;
|
||||
this.position = position;
|
||||
this.regionChanged = regionChanged;
|
||||
this.segment = segment;
|
||||
this.localPlayers = localPlayers;
|
||||
this.segments = segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last known region.
|
||||
* @return The last known region.
|
||||
*/
|
||||
public Position getLastKnownRegion() {
|
||||
return lastKnownRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's position.
|
||||
* @return The player's position.
|
||||
*/
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the region has changed.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean hasRegionChanged() {
|
||||
return regionChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current player's segment.
|
||||
* @return The current player's segment.
|
||||
*/
|
||||
public SynchronizationSegment getSegment() {
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of local players.
|
||||
* @return The number of local players.
|
||||
*/
|
||||
public int getLocalPlayers() {
|
||||
return localPlayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the synchronization segments.
|
||||
* @return The segments.
|
||||
*/
|
||||
public List<SynchronizationSegment> getSegments() {
|
||||
return segments;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An event which indicates that the client should load the specified region.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class RegionChangeEvent extends Event {
|
||||
|
||||
/**
|
||||
* The position of the region to load.
|
||||
*/
|
||||
private final Position position;
|
||||
|
||||
/**
|
||||
* Creates the region changed event.
|
||||
* @param position The position of the region.
|
||||
*/
|
||||
public RegionChangeEvent(Position position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the region to load.
|
||||
* @return The position of the region to load.
|
||||
*/
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
/**
|
||||
* The second {@link ItemActionEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class SecondItemActionEvent extends ItemActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the second item action event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param id The item id.
|
||||
* @param slot The item slot.
|
||||
*/
|
||||
public SecondItemActionEvent(int interfaceId, int id, int slot) {
|
||||
super(2, interfaceId, id, slot);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An event sent when the second option at an object is used.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class SecondObjectActionEvent extends ObjectActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the second object action event.
|
||||
* @param id The id.
|
||||
* @param position The position.
|
||||
*/
|
||||
public SecondObjectActionEvent(int id, Position position) {
|
||||
super(2, id, position);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event which is sent to the client with a server-side message.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ServerMessageEvent extends Event {
|
||||
|
||||
/**
|
||||
* The message.
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* Creates the {@link ServerMessageEvent}.
|
||||
* @param message The message.
|
||||
*/
|
||||
public ServerMessageEvent(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message.
|
||||
* @return The message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent to the client to update an interface's text.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class SetInterfaceTextEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface's id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* The text.
|
||||
*/
|
||||
private final String text;
|
||||
|
||||
/**
|
||||
* Creates the set interface text event.
|
||||
* @param interfaceId The interface's id.
|
||||
* @param text The interface's text.
|
||||
*/
|
||||
public SetInterfaceTextEvent(int interfaceId, String text) {
|
||||
this.interfaceId = interfaceId;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface's text.
|
||||
* @return The interface's text.
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent by the client when two items are switched.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class SwitchItemEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* A flag indicating if insertion mode is enabled.
|
||||
*/
|
||||
private final boolean inserting;
|
||||
|
||||
/**
|
||||
* The old slot.
|
||||
*/
|
||||
private final int oldSlot;
|
||||
|
||||
/**
|
||||
* The new slot.
|
||||
*/
|
||||
private final int newSlot;
|
||||
|
||||
/**
|
||||
* Creates a new switch item event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param inserting A flag indicating if the interface is in 'insert' mode
|
||||
* instead of swap mode.
|
||||
* @param oldSlot The old slot.
|
||||
* @param newSlot The new slot.
|
||||
*/
|
||||
public SwitchItemEvent(int interfaceId, boolean inserting, int oldSlot, int newSlot) {
|
||||
this.interfaceId = interfaceId;
|
||||
this.inserting = inserting;
|
||||
this.oldSlot = oldSlot;
|
||||
this.newSlot = newSlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this event is in insertion mode.
|
||||
* @return The insertion flag.
|
||||
*/
|
||||
public boolean isInserting() {
|
||||
return inserting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this event is in swap mode.
|
||||
* @return The swap flag.
|
||||
*/
|
||||
public boolean isSwapping() {
|
||||
return !inserting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the old slot.
|
||||
* @return The old slot.
|
||||
*/
|
||||
public int getOldSlot() {
|
||||
return oldSlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new slot.
|
||||
* @return The new slot.
|
||||
*/
|
||||
public int getNewSlot() {
|
||||
return newSlot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent to the client to change the interface of a tab.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class SwitchTabInterfaceEvent extends Event {
|
||||
|
||||
/**
|
||||
* The tab id.
|
||||
*/
|
||||
private final int tab;
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* Creates the switch interface event.
|
||||
* @param tab The tab id.
|
||||
* @param interfaceId The interface id.
|
||||
*/
|
||||
public SwitchTabInterfaceEvent(int tab, int interfaceId) {
|
||||
this.tab = tab;
|
||||
this.interfaceId = interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tab id.
|
||||
* @return The tab id.
|
||||
*/
|
||||
public int getTabId() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
/**
|
||||
* The third {@link ItemActionEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ThirdItemActionEvent extends ItemActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the third item action event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param id The item id.
|
||||
* @param slot The item slot.
|
||||
*/
|
||||
public ThirdItemActionEvent(int interfaceId, int id, int slot) {
|
||||
super(3, interfaceId, id, slot);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An event sent when the third option at an object is used.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ThirdObjectActionEvent extends ObjectActionEvent {
|
||||
|
||||
/**
|
||||
* Creates the third object action event.
|
||||
* @param id The id.
|
||||
* @param position The position.
|
||||
*/
|
||||
public ThirdObjectActionEvent(int id, Position position) {
|
||||
super(3, id, position);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Item;
|
||||
|
||||
/**
|
||||
* An event which updates all the items in an interface.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class UpdateItemsEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* The items.
|
||||
*/
|
||||
private final Item[] items;
|
||||
|
||||
/**
|
||||
* Creates the update inventory interface event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param items The items.
|
||||
*/
|
||||
public UpdateItemsEvent(int interfaceId, Item[] items) {
|
||||
this.interfaceId = interfaceId;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the items.
|
||||
* @return The items.
|
||||
*/
|
||||
public Item[] getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Skill;
|
||||
|
||||
/**
|
||||
* An {@link Event} sent to the client to update a player's skill level.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class UpdateSkillEvent extends Event {
|
||||
|
||||
/**
|
||||
* The skill's id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The skill.
|
||||
*/
|
||||
private final Skill skill;
|
||||
|
||||
/**
|
||||
* Creates an update skill event.
|
||||
* @param id The id.
|
||||
* @param skill The skill.
|
||||
*/
|
||||
public UpdateSkillEvent(int id, Skill skill) {
|
||||
this.id = id;
|
||||
this.skill = skill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the skill's id.
|
||||
* @return The skill's id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the skill.
|
||||
* @return The skill.
|
||||
*/
|
||||
public Skill getSkill() {
|
||||
return skill;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.SlottedItem;
|
||||
|
||||
/**
|
||||
* An event which updates a single item in an interface.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class UpdateSlottedItemsEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* The slotted items.
|
||||
*/
|
||||
private final SlottedItem[] items;
|
||||
|
||||
/**
|
||||
* Creates the update item in interface event.
|
||||
* @param interfaceId The interface id.
|
||||
* @param items The slotted items.
|
||||
*/
|
||||
public UpdateSlottedItemsEvent(int interfaceId, SlottedItem... items) {
|
||||
this.interfaceId = interfaceId;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of slotted items.
|
||||
* @return The slotted items.
|
||||
*/
|
||||
public SlottedItem[] getSlottedItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An event which the client sends to request that the player walks somewhere.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class WalkEvent extends Event {
|
||||
|
||||
/**
|
||||
* The steps.
|
||||
*/
|
||||
private final Position[] steps;
|
||||
|
||||
/**
|
||||
* The running flag.
|
||||
*/
|
||||
private boolean run;
|
||||
|
||||
/**
|
||||
* Creates the event.
|
||||
* @param steps The steps array.
|
||||
* @param run The run flag.
|
||||
*/
|
||||
public WalkEvent(Position[] steps, boolean run) {
|
||||
if (steps.length < 0) {
|
||||
throw new IllegalArgumentException("number of steps must not be negative");
|
||||
}
|
||||
this.steps = steps;
|
||||
this.run = run;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the steps array.
|
||||
* @return An array of steps.
|
||||
*/
|
||||
public Position[] getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the steps should be ran (ctrl+click).
|
||||
* @return {@code true} if so, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return run;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains event implementations.
|
||||
*/
|
||||
package org.apollo.game.event.impl;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes related to the event management in the game.
|
||||
*/
|
||||
package org.apollo.game.event;
|
||||
@@ -0,0 +1,198 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Represents an animation.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Animation {
|
||||
|
||||
/**
|
||||
* A special animation which stops the current animation.
|
||||
*/
|
||||
public static final Animation STOP_ANIMATION = new Animation(-1);
|
||||
|
||||
/**
|
||||
* The yes animation.
|
||||
*/
|
||||
public static final Animation YES = new Animation(855);
|
||||
|
||||
/**
|
||||
* The no animation.
|
||||
*/
|
||||
public static final Animation NO = new Animation(856);
|
||||
|
||||
/**
|
||||
* The thinking animation.
|
||||
*/
|
||||
public static final Animation THINKING = new Animation(857);
|
||||
|
||||
/**
|
||||
* The bow animation.
|
||||
*/
|
||||
public static final Animation BOW = new Animation(858);
|
||||
|
||||
/**
|
||||
* The angry animation.
|
||||
*/
|
||||
public static final Animation ANGRY = new Animation(859);
|
||||
|
||||
/**
|
||||
* The cry animation.
|
||||
*/
|
||||
public static final Animation CRY = new Animation(860);
|
||||
|
||||
/**
|
||||
* The laugh animation.
|
||||
*/
|
||||
public static final Animation LAUGH = new Animation(861);
|
||||
|
||||
/**
|
||||
* The cheer animation.
|
||||
*/
|
||||
public static final Animation CHEER = new Animation(862);
|
||||
|
||||
/**
|
||||
* The wave animation.
|
||||
*/
|
||||
public static final Animation WAVE = new Animation(863);
|
||||
|
||||
/**
|
||||
* The beckon animation.
|
||||
*/
|
||||
public static final Animation BECKON = new Animation(864);
|
||||
|
||||
/**
|
||||
* The clap animation.
|
||||
*/
|
||||
public static final Animation CLAP = new Animation(865);
|
||||
|
||||
/**
|
||||
* The dance animation.
|
||||
*/
|
||||
public static final Animation DANCE = new Animation(866);
|
||||
|
||||
/**
|
||||
* The panic animation.
|
||||
*/
|
||||
public static final Animation PANIC = new Animation(2105);
|
||||
|
||||
/**
|
||||
* The jig animation.
|
||||
*/
|
||||
public static final Animation JIG = new Animation(2106);
|
||||
|
||||
/**
|
||||
* The spin animation.
|
||||
*/
|
||||
public static final Animation SPIN = new Animation(2107);
|
||||
|
||||
/**
|
||||
* The head bang animation.
|
||||
*/
|
||||
public static final Animation HEAD_BANG = new Animation(2108);
|
||||
|
||||
/**
|
||||
* The joy jump animation.
|
||||
*/
|
||||
public static final Animation JOY_JUMP = new Animation(2109);
|
||||
|
||||
/**
|
||||
* The raspberry animation.
|
||||
*/
|
||||
public static final Animation RASPBERRY = new Animation(2110);
|
||||
|
||||
/**
|
||||
* The yawn animation.
|
||||
*/
|
||||
public static final Animation YAWN = new Animation(2111);
|
||||
|
||||
/**
|
||||
* The salute animation.
|
||||
*/
|
||||
public static final Animation SALUTE = new Animation(2112);
|
||||
|
||||
/**
|
||||
* The shrug animation.
|
||||
*/
|
||||
public static final Animation SHRUG = new Animation(2113);
|
||||
|
||||
/**
|
||||
* The blow kiss animation.
|
||||
*/
|
||||
public static final Animation BLOW_KISS = new Animation(1368);
|
||||
|
||||
/**
|
||||
* The glass wall animation.
|
||||
*/
|
||||
public static final Animation GLASS_WALL = new Animation(1128);
|
||||
|
||||
/**
|
||||
* The lean animation.
|
||||
*/
|
||||
public static final Animation LEAN = new Animation(1129);
|
||||
|
||||
/**
|
||||
* The climb rope animation.
|
||||
*/
|
||||
public static final Animation CLIMB_ROPE = new Animation(1130);
|
||||
|
||||
/**
|
||||
* The glass box animation.
|
||||
*/
|
||||
public static final Animation GLASS_BOX = new Animation(1131);
|
||||
|
||||
/**
|
||||
* The goblin bow animation.
|
||||
*/
|
||||
public static final Animation GOBLIN_BOW = new Animation(2127);
|
||||
|
||||
/**
|
||||
* The goblin dance animation.
|
||||
*/
|
||||
public static final Animation GOBLIN_DANCE = new Animation(2128);
|
||||
|
||||
/**
|
||||
* The id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The delay.
|
||||
*/
|
||||
private final int delay;
|
||||
|
||||
/**
|
||||
* Creates a new animation with no delay.
|
||||
* @param id The id.
|
||||
*/
|
||||
public Animation(int id) {
|
||||
this(id, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new animation.
|
||||
* @param id The id.
|
||||
* @param delay The delay.
|
||||
*/
|
||||
public Animation(int id, int delay) {
|
||||
this.id = id;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the animation's id.
|
||||
* @return The animation's id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the animation's delay.
|
||||
* @return The animation's delay.
|
||||
*/
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Represents the appearance of a player.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Appearance {
|
||||
|
||||
/**
|
||||
* The default appearance.
|
||||
*/
|
||||
public static final Appearance DEFAULT_APPEARANCE = new Appearance(Gender.MALE, new int[] { 0, 10, 18, 26, 33, 36, 42 }, new int[5]);
|
||||
|
||||
/**
|
||||
* The player's gender.
|
||||
*/
|
||||
private final Gender gender;
|
||||
|
||||
/**
|
||||
* The array of clothing/characteristic styles.
|
||||
*/
|
||||
private final int[] style;
|
||||
|
||||
/**
|
||||
* The array of clothing/skin colours.
|
||||
*/
|
||||
private final int[] colors;
|
||||
|
||||
/**
|
||||
* Creates the appearance with the specified gender, style and colors.
|
||||
* @param gender The gender.
|
||||
* @param style The style.
|
||||
* @param colors The colors.
|
||||
*/
|
||||
public Appearance(Gender gender, int[] style, int[] colors) {
|
||||
if (gender == null || style == null || colors == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (style.length != 7) {
|
||||
throw new IllegalArgumentException("the style array must have 7 elements");
|
||||
}
|
||||
if (colors.length != 5) {
|
||||
throw new IllegalArgumentException("the colors array must have 5 elements");
|
||||
}
|
||||
this.gender = gender;
|
||||
this.style = style;
|
||||
this.colors = colors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the gender of the player.
|
||||
* @return The gender of the player.
|
||||
*/
|
||||
public Gender getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player is male.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isMale() {
|
||||
return gender == Gender.MALE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player is female.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isFemale() {
|
||||
return gender == Gender.FEMALE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's styles.
|
||||
* @return The player's styles.
|
||||
*/
|
||||
public int[] getStyle() {
|
||||
/*
|
||||
* Info on the elements of the array itself:
|
||||
*
|
||||
* 0 = head
|
||||
* 1 = chin/beard
|
||||
* 2 = chest
|
||||
* 3 = arms
|
||||
* 4 = hands
|
||||
* 5 = legs
|
||||
* 6 = feet
|
||||
*/
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's colors.
|
||||
* @return The player's colors.
|
||||
*/
|
||||
public int[] getColors() {
|
||||
return colors;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apollo.game.action.Action;
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.event.impl.ServerMessageEvent;
|
||||
import org.apollo.game.model.Inventory.StackMode;
|
||||
import org.apollo.game.scheduling.impl.SkillNormalizationTask;
|
||||
import org.apollo.game.sync.block.SynchronizationBlock;
|
||||
import org.apollo.game.sync.block.SynchronizationBlockSet;
|
||||
import org.apollo.util.CharacterRepository;
|
||||
|
||||
/**
|
||||
* A {@link Character} is a living creature in the world, such as a player or
|
||||
* NPC.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class Character {
|
||||
|
||||
/**
|
||||
* The index of this character in the {@link CharacterRepository} it
|
||||
* belongs to.
|
||||
*/
|
||||
private int index = -1;
|
||||
|
||||
/**
|
||||
* Teleportation flag.
|
||||
*/
|
||||
private boolean teleporting = false;
|
||||
|
||||
/**
|
||||
* The walking queue.
|
||||
*/
|
||||
private final WalkingQueue walkingQueue = new WalkingQueue(this);
|
||||
|
||||
/**
|
||||
* The first direction.
|
||||
*/
|
||||
private Direction firstDirection = Direction.NONE;
|
||||
|
||||
/**
|
||||
* The second direction.
|
||||
*/
|
||||
private Direction secondDirection = Direction.NONE;
|
||||
|
||||
/**
|
||||
* The current position of this character.
|
||||
*/
|
||||
private Position position;
|
||||
|
||||
/**
|
||||
* A list of local players.
|
||||
*/
|
||||
private final List<Player> localPlayers = new ArrayList<Player>(); // TODO make a specialized collection?
|
||||
|
||||
/**
|
||||
* A set of {@link SynchronizationBlock}s.
|
||||
*/
|
||||
private SynchronizationBlockSet blockSet = new SynchronizationBlockSet();
|
||||
|
||||
/**
|
||||
* The character's current action.
|
||||
*/
|
||||
private Action<?> action; // TODO
|
||||
|
||||
/**
|
||||
* The character's inventory.
|
||||
*/
|
||||
private final Inventory inventory = new Inventory(InventoryConstants.INVENTORY_CAPACITY);
|
||||
|
||||
/**
|
||||
* The character's equipment.
|
||||
*/
|
||||
private final Inventory equipment = new Inventory(InventoryConstants.EQUIPMENT_CAPACITY, StackMode.STACK_ALWAYS);
|
||||
|
||||
/**
|
||||
* The character's bank.
|
||||
*/
|
||||
private final Inventory bank = new Inventory(InventoryConstants.BANK_CAPACITY, StackMode.STACK_ALWAYS);
|
||||
|
||||
/**
|
||||
* The character's skill set.
|
||||
*/
|
||||
private final SkillSet skillSet = new SkillSet();
|
||||
|
||||
/**
|
||||
* Creates a new character with the specified initial position.
|
||||
* @param position The initial position of this character.
|
||||
*/
|
||||
public Character(Position position) {
|
||||
this.position = position;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises this character.
|
||||
*/
|
||||
private void init() {
|
||||
World.getWorld().schedule(new SkillNormalizationTask(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character's inventory.
|
||||
* @return The character's inventory.
|
||||
*/
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character's equipment.
|
||||
* @return The character's equipment.
|
||||
*/
|
||||
public Inventory getEquipment() {
|
||||
return equipment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character's bank.
|
||||
* @return The character's bank.
|
||||
*/
|
||||
public Inventory getBank() {
|
||||
return bank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local player list.
|
||||
* @return The local player list.
|
||||
*/
|
||||
public List<Player> getLocalPlayerList() {
|
||||
return localPlayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this player is currently teleporting.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isTeleporting() {
|
||||
return teleporting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the teleporting flag.
|
||||
* @param teleporting {@code true} if the player is teleporting,
|
||||
* {@code false} if not.
|
||||
*/
|
||||
public void setTeleporting(boolean teleporting) {
|
||||
this.teleporting = teleporting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the walking queue.
|
||||
* @return The walking queue.
|
||||
*/
|
||||
public WalkingQueue getWalkingQueue() {
|
||||
return walkingQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the next directions for this character.
|
||||
* @param first The first direction.
|
||||
* @param second The second direction.
|
||||
*/
|
||||
public void setDirections(Direction first, Direction second) {
|
||||
this.firstDirection = first;
|
||||
this.secondDirection = second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first direction.
|
||||
* @return The first direction.
|
||||
*/
|
||||
public Direction getFirstDirection() {
|
||||
return firstDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the second direction.
|
||||
* @return The second direction.
|
||||
*/
|
||||
public Direction getSecondDirection() {
|
||||
return secondDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the directions as an array.
|
||||
* @return A zero, one or two element array containing the directions (in
|
||||
* order).
|
||||
*/
|
||||
public Direction[] getDirections() {
|
||||
if (firstDirection != Direction.NONE) {
|
||||
if (secondDirection != Direction.NONE) {
|
||||
return new Direction[] { firstDirection, secondDirection };
|
||||
} else {
|
||||
return new Direction[] { firstDirection };
|
||||
}
|
||||
} else {
|
||||
return Direction.EMPTY_DIRECTION_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of this character.
|
||||
* @return The position of this character.
|
||||
*/
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of this character.
|
||||
* @param position The position of this character.
|
||||
*/
|
||||
public void setPosition(Position position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this character is active.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return index != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of this character.
|
||||
* @return The index of this character.
|
||||
*/
|
||||
public int getIndex() {
|
||||
synchronized (this) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of this character.
|
||||
* @param index The index of this character.
|
||||
*/
|
||||
public void setIndex(int index) {
|
||||
synchronized (this) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link SynchronizationBlockSet}.
|
||||
* @return The block set.
|
||||
*/
|
||||
public SynchronizationBlockSet getBlockSet() {
|
||||
return blockSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the block set.
|
||||
*/
|
||||
public void resetBlockSet() {
|
||||
blockSet = new SynchronizationBlockSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an {@link Event} to either:
|
||||
* <ul>
|
||||
* <li>The client if this {@link Character} is a {@link Player}.</li>
|
||||
* <li>The AI routines if this {@link Character} is an NPC</li>
|
||||
* </ul>
|
||||
* @param event The event.
|
||||
*/
|
||||
public abstract void send(Event event);
|
||||
|
||||
/**
|
||||
* Teleports this character to the specified position, setting the
|
||||
* appropriate flags and clearing the walking queue.
|
||||
* @param position The position.
|
||||
*/
|
||||
public void teleport(Position position) {
|
||||
this.teleporting = true;
|
||||
this.position = position;
|
||||
this.walkingQueue.clear();
|
||||
this.stopAction(); // TODO do it on any movement is a must.. walking queue perhaps?
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the specified animation.
|
||||
* @param animation The animation.
|
||||
*/
|
||||
public void playAnimation(Animation animation) {
|
||||
blockSet.add(SynchronizationBlock.createAnimationBlock(animation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current animation.
|
||||
*/
|
||||
public void stopAnimation() {
|
||||
playAnimation(Animation.STOP_ANIMATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the specified graphic.
|
||||
* @param graphic The graphic.
|
||||
*/
|
||||
public void playGraphic(Graphic graphic) {
|
||||
blockSet.add(SynchronizationBlock.createGraphicBlock(graphic));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current graphic.
|
||||
*/
|
||||
public void stopGraphic() {
|
||||
playGraphic(Graphic.STOP_GRAPHIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character's skill set.
|
||||
* @return The character's skill set.
|
||||
*/
|
||||
public SkillSet getSkillSet() {
|
||||
return skillSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new action, stopping the current one if it exists.
|
||||
* @param action The new action.
|
||||
* @return A flag indicating if the action was started.
|
||||
*/
|
||||
public boolean startAction(Action<?> action) {
|
||||
if (this.action != null) {
|
||||
if (this.action.equals(action)) {
|
||||
return false;
|
||||
}
|
||||
stopAction();
|
||||
}
|
||||
this.action = action;
|
||||
World.getWorld().schedule(action);
|
||||
return true; // TODO maybe this should be incorporated into the action class itself?
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current action.
|
||||
*/
|
||||
public void stopAction() {
|
||||
if (action != null) {
|
||||
action.stop();
|
||||
action = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the character to face the specified position.
|
||||
* @param position The position to face.
|
||||
*/
|
||||
public void turnTo(Position position) {
|
||||
blockSet.add(SynchronizationBlock.createTurnToPositionBlock(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the character.
|
||||
* @param message The message.
|
||||
*/
|
||||
public void sendMessage(String message) {
|
||||
send(new ServerMessageEvent(message));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Represents a single movement direction.
|
||||
* @author Graham
|
||||
*/
|
||||
public enum Direction {
|
||||
|
||||
/**
|
||||
* North movement.
|
||||
*/
|
||||
NORTH(1),
|
||||
|
||||
/**
|
||||
* North east movement.
|
||||
*/
|
||||
NORTH_EAST(2),
|
||||
|
||||
/**
|
||||
* East movement.
|
||||
*/
|
||||
EAST(4),
|
||||
|
||||
/**
|
||||
* South east movement.
|
||||
*/
|
||||
SOUTH_EAST(7),
|
||||
|
||||
/**
|
||||
* South movement.
|
||||
*/
|
||||
SOUTH(6),
|
||||
|
||||
/**
|
||||
* South west movement.
|
||||
*/
|
||||
SOUTH_WEST(5),
|
||||
|
||||
/**
|
||||
* West movement.
|
||||
*/
|
||||
WEST(3),
|
||||
|
||||
/**
|
||||
* North west movement.
|
||||
*/
|
||||
NORTH_WEST(0),
|
||||
|
||||
/**
|
||||
* No movement.
|
||||
*/
|
||||
NONE(-1);
|
||||
|
||||
/**
|
||||
* An empty direction array.
|
||||
*/
|
||||
public static final Direction[] EMPTY_DIRECTION_ARRAY = new Direction[0];
|
||||
|
||||
/**
|
||||
* Checks if the direction represented by the two delta values can connect
|
||||
* two points together in a single direction.
|
||||
* @param deltaX The difference in X coordinates.
|
||||
* @param deltaY The difference in X coordinates.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public static boolean isConnectable(int deltaX, int deltaY) {
|
||||
return Math.abs(deltaX) == Math.abs(deltaY) || deltaX == 0 || deltaY == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a direction from the differences between X and Y.
|
||||
* @param deltaX The difference between two X coordinates.
|
||||
* @param deltaY The difference between two Y coordinates.
|
||||
* @return The direction.
|
||||
*/
|
||||
public static Direction fromDeltas(int deltaX, int deltaY) {
|
||||
if (deltaY == 1) {
|
||||
if (deltaX == 1) {
|
||||
return Direction.NORTH_EAST;
|
||||
} else if (deltaX == 0) {
|
||||
return Direction.NORTH;
|
||||
} else {
|
||||
return Direction.NORTH_WEST;
|
||||
}
|
||||
} else if (deltaY == -1) {
|
||||
if (deltaX == 1) {
|
||||
return Direction.SOUTH_EAST;
|
||||
} else if (deltaX == 0) {
|
||||
return Direction.SOUTH;
|
||||
} else {
|
||||
return Direction.SOUTH_WEST;
|
||||
}
|
||||
} else {
|
||||
if (deltaX == 1) {
|
||||
return Direction.EAST;
|
||||
} else if (deltaX == -1) {
|
||||
return Direction.WEST;
|
||||
}
|
||||
}
|
||||
return Direction.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The direction as an integer.
|
||||
*/
|
||||
private final int intValue;
|
||||
|
||||
/**
|
||||
* Creates the direction.
|
||||
* @param intValue The direction as an integer.
|
||||
*/
|
||||
private Direction(int intValue) {
|
||||
this.intValue = intValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the direction as an integer which the client can understand.
|
||||
* @return The movement as an integer.
|
||||
*/
|
||||
public int toInteger() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Contains equipment-related constants.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EquipmentConstants {
|
||||
|
||||
/**
|
||||
* The hat slot.
|
||||
*/
|
||||
public static final int HAT = 0;
|
||||
|
||||
/**
|
||||
* The cape slot.
|
||||
*/
|
||||
public static final int CAPE = 1;
|
||||
|
||||
/**
|
||||
* The amulet slot.
|
||||
*/
|
||||
public static final int AMULET = 2;
|
||||
|
||||
/**
|
||||
* The weapon slot.
|
||||
*/
|
||||
public static final int WEAPON = 3;
|
||||
|
||||
/**
|
||||
* The chest slot.
|
||||
*/
|
||||
public static final int CHEST = 4;
|
||||
|
||||
/**
|
||||
* The shield slot.
|
||||
*/
|
||||
public static final int SHIELD = 5;
|
||||
|
||||
/**
|
||||
* The legs slot.
|
||||
*/
|
||||
public static final int LEGS = 7;
|
||||
|
||||
/**
|
||||
* The hands slot.
|
||||
*/
|
||||
public static final int HANDS = 9;
|
||||
|
||||
/**
|
||||
* The feet slot.
|
||||
*/
|
||||
public static final int FEET = 10;
|
||||
|
||||
/**
|
||||
* The ring slot.
|
||||
*/
|
||||
public static final int RING = 12;
|
||||
|
||||
/**
|
||||
* The arrows slot.
|
||||
*/
|
||||
public static final int ARROWS = 13;
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation;
|
||||
*/
|
||||
private EquipmentConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* An enumeration containing the two genders (male and female).
|
||||
* @author Graham
|
||||
*/
|
||||
public enum Gender {
|
||||
|
||||
/**
|
||||
* The male gender.
|
||||
*/
|
||||
MALE(0),
|
||||
|
||||
/**
|
||||
* The female gender.
|
||||
*/
|
||||
FEMALE(1);
|
||||
|
||||
/**
|
||||
* An integer representation used by the client.
|
||||
*/
|
||||
private final int intValue;
|
||||
|
||||
/**
|
||||
* Creates the gender.
|
||||
* @param intValue The integer representation.
|
||||
*/
|
||||
private Gender(int intValue) {
|
||||
this.intValue = intValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this gender to an integer.
|
||||
* @return The integer representation used by the client.
|
||||
*/
|
||||
public int toInteger() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Represents a 'still graphic'.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Graphic {
|
||||
|
||||
/**
|
||||
* A special graphic which stops the current graphic.
|
||||
*/
|
||||
public static final Graphic STOP_GRAPHIC = new Graphic(-1);
|
||||
|
||||
/**
|
||||
* The id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The delay.
|
||||
*/
|
||||
private final int delay;
|
||||
|
||||
/**
|
||||
* The height.
|
||||
*/
|
||||
private final int height;
|
||||
|
||||
/**
|
||||
* Creates a new graphic with no delay and a height of zero.
|
||||
* @param id The id.
|
||||
*/
|
||||
public Graphic(int id) {
|
||||
this(id, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new graphic with a height of zero.
|
||||
* @param id The id.
|
||||
* @param delay The delay.
|
||||
*/
|
||||
public Graphic(int id, int delay) {
|
||||
this(id, delay, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new graphic.
|
||||
* @param id The id.
|
||||
* @param delay The delay.
|
||||
* @param height The height.
|
||||
*/
|
||||
public Graphic(int id, int delay, int height) {
|
||||
this.id = id;
|
||||
this.delay = delay;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graphic's id.
|
||||
* @return The graphic's id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graphic's delay.
|
||||
* @return The graphic's delay.
|
||||
*/
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graphic's height.
|
||||
* @return The graphic's height.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apollo.game.event.impl.CloseInterfaceEvent;
|
||||
import org.apollo.game.event.impl.EnterAmountEvent;
|
||||
import org.apollo.game.event.impl.OpenInterfaceEvent;
|
||||
import org.apollo.game.event.impl.OpenInterfaceSidebarEvent;
|
||||
import org.apollo.game.model.inter.EnterAmountListener;
|
||||
import org.apollo.game.model.inter.InterfaceListener;
|
||||
|
||||
/**
|
||||
* Represents the set of interfaces the player has open.
|
||||
* <p>
|
||||
* This class manages all six distinct types of interface (the last two are not
|
||||
* present on 317 servers).
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li><strong>Windows:</strong> the ones people mostly associate with the
|
||||
* word interfaces. Things like your bank, the wildy warning screen, the
|
||||
* trade screen, etc.</li>
|
||||
* <li><strong>Overlays:</strong> display in the same place as windows, but
|
||||
* don't prevent you from moving. For example, the wilderness level
|
||||
* indicator.</li>
|
||||
* <li><strong>Dialogues:</strong> interfaces which are displayed over the
|
||||
* chat box.</li>
|
||||
* <li><strong>Sidebars:</strong> an interface which displays over the
|
||||
* inventory area.</li>
|
||||
* <li><strong>Fullscreen windows:</strong> a window which displays over the
|
||||
* whole screen e.g. the 377 welcome screen.</li>
|
||||
* <li><strong>Fullscreen background:</strong> an interface displayed behind
|
||||
* the fullscreen window, typically a blank, black screen.</li>
|
||||
* </ul>
|
||||
* @author Graham
|
||||
*/
|
||||
public final class InterfaceSet {
|
||||
|
||||
/**
|
||||
* The player whose interfaces are being managed.
|
||||
*/
|
||||
private final Player player; // TODO: maybe switch to a listener system like the inventory?
|
||||
|
||||
// TODO: maybe store the current inventory tab ids here??
|
||||
/**
|
||||
* A map of open interfaces.
|
||||
*/
|
||||
private Map<InterfaceType, Integer> interfaces = new HashMap<InterfaceType, Integer>();
|
||||
|
||||
/**
|
||||
* The current listener.
|
||||
*/
|
||||
private InterfaceListener listener;
|
||||
|
||||
/**
|
||||
* The current enter amount listener.
|
||||
*/
|
||||
private EnterAmountListener amountListener;
|
||||
|
||||
/**
|
||||
* Creates an interface set.
|
||||
* @param player The player.
|
||||
*/
|
||||
public InterfaceSet(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current open interface(s).
|
||||
*/
|
||||
public void close() {
|
||||
closeAndNotify();
|
||||
|
||||
player.send(new CloseInterfaceEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent by the client when it has closed an interface.
|
||||
*/
|
||||
public void interfaceClosed() {
|
||||
closeAndNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the enter amount dialog.
|
||||
* @param listener The enter amount listener.
|
||||
*/
|
||||
public void openEnterAmountDialog(EnterAmountListener listener) {
|
||||
this.amountListener = listener;
|
||||
|
||||
player.send(new EnterAmountEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a window and inventory sidebar.
|
||||
* @param windowId The window's id.
|
||||
* @param sidebarId The sidebar's id.
|
||||
*/
|
||||
public void openWindowWithSidebar(int windowId, int sidebarId) {
|
||||
openWindowWithSidebar(null, windowId, sidebarId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a window and inventory sidebar with the specified listener.
|
||||
* @param listener The listener for this interface.
|
||||
* @param windowId The window's id.
|
||||
* @param sidebarId The sidebar's id.
|
||||
*/
|
||||
public void openWindowWithSidebar(InterfaceListener listener, int windowId, int sidebarId) {
|
||||
closeAndNotify();
|
||||
this.listener = listener;
|
||||
|
||||
interfaces.put(InterfaceType.WINDOW, windowId);
|
||||
interfaces.put(InterfaceType.SIDEBAR, sidebarId);
|
||||
|
||||
player.send(new OpenInterfaceSidebarEvent(windowId, sidebarId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a window.
|
||||
* @param windowId The window's id.
|
||||
*/
|
||||
public void openWindow(int windowId) {
|
||||
openWindow(null, windowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a window with the specified listener.
|
||||
* @param listener The listener for this interface.
|
||||
* @param windowId The window's id.
|
||||
*/
|
||||
public void openWindow(InterfaceListener listener, int windowId) {
|
||||
closeAndNotify();
|
||||
this.listener = listener;
|
||||
|
||||
interfaces.put(InterfaceType.WINDOW, windowId);
|
||||
|
||||
player.send(new OpenInterfaceEvent(windowId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this interface sets contains the specified interface.
|
||||
* @param id The interface's id.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean contains(int id) {
|
||||
return interfaces.containsValue(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal method for closing the interface, notifying the listener if
|
||||
* appropriate, but not sending any events.
|
||||
*/
|
||||
private void closeAndNotify() {
|
||||
amountListener = null; // TODO should we notify??
|
||||
|
||||
interfaces.clear();
|
||||
if (listener != null) {
|
||||
listener.interfaceClosed();
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the client has entered the specified amount. Notifies the
|
||||
* current listener.
|
||||
* @param amount The amount.
|
||||
*/
|
||||
public void enteredAmount(int amount) {
|
||||
if (amountListener != null) {
|
||||
amountListener.amountEntered(amount);
|
||||
amountListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Represents the different types of interfaces.
|
||||
* @author Graham
|
||||
*/
|
||||
public enum InterfaceType {
|
||||
|
||||
/**
|
||||
* A window is an interface which occupies the game screen.
|
||||
*/
|
||||
WINDOW,
|
||||
|
||||
/**
|
||||
* An overlay is an interface which occupies the game screen like a window,
|
||||
* however, you can walk around and perform actions still.
|
||||
*/
|
||||
OVERLAY,
|
||||
|
||||
/**
|
||||
* A dialogue is an interface which appears in the chat box.
|
||||
*/
|
||||
DIALOGUE,
|
||||
|
||||
/**
|
||||
* An interface which displays over the inventory area.
|
||||
*/
|
||||
SIDEBAR,
|
||||
|
||||
/**
|
||||
* An interface which is shown in full screen mode.
|
||||
*/
|
||||
FULLSCREEN_WINDOW,
|
||||
|
||||
/**
|
||||
* An interface which is shown behind a fullscreen window.
|
||||
*/
|
||||
FULLSCREEN_BACKGROUND;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apollo.game.model.def.ItemDefinition;
|
||||
import org.apollo.game.model.inv.InventoryListener;
|
||||
|
||||
/**
|
||||
* Represents an inventory - a collection of {@link Item}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Inventory implements Cloneable {
|
||||
|
||||
/**
|
||||
* An enumeration containing the different 'stacking modes' of an
|
||||
* {@link Inventory}.
|
||||
* @author Graham
|
||||
*/
|
||||
public enum StackMode {
|
||||
|
||||
/**
|
||||
* When in {@link #STACK_ALWAYS} mode, an {@link Inventory} will stack
|
||||
* every single item, regardless of the settings of individual items.
|
||||
*/
|
||||
STACK_ALWAYS,
|
||||
|
||||
/**
|
||||
* When in {@link #STACK_STACKABLE_ITEMS} mode, an {@link Inventory}
|
||||
* will stack items depending on their settings.
|
||||
*/
|
||||
STACK_STACKABLE_ITEMS,
|
||||
|
||||
/**
|
||||
* When in {@link #STACK_NEVER} mode, an {@link Inventory} will never
|
||||
* stack items.
|
||||
*/
|
||||
STACK_NEVER;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of inventory listeners.
|
||||
*/
|
||||
private final List<InventoryListener> listeners = new ArrayList<InventoryListener>();
|
||||
|
||||
/**
|
||||
* The capacity of this inventory.
|
||||
*/
|
||||
private final int capacity;
|
||||
|
||||
/**
|
||||
* The items in this inventory.
|
||||
*/
|
||||
private Item[] items;
|
||||
|
||||
/**
|
||||
* The stacking mode.
|
||||
*/
|
||||
private final StackMode mode;
|
||||
|
||||
/**
|
||||
* The size of this inventory - the number of 'used slots'.
|
||||
*/
|
||||
private int size = 0;
|
||||
|
||||
/**
|
||||
* A flag indicating if events are being fired.
|
||||
*/
|
||||
private boolean firingEvents = true; // TODO: make this reentrant
|
||||
|
||||
/**
|
||||
* Creates an inventory.
|
||||
* @param capacity The capacity.
|
||||
* @throws IllegalArgumentException if the capacity is negative.
|
||||
*/
|
||||
public Inventory(int capacity) {
|
||||
this(capacity, StackMode.STACK_STACKABLE_ITEMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an inventory.
|
||||
* @param capacity The capacity.
|
||||
* @param mode The stacking mode.
|
||||
* @throws IllegalArgumentException if the capacity is negative.
|
||||
* @throws NullPointerException if the mode is {@code null}.
|
||||
*/
|
||||
public Inventory(int capacity, StackMode mode) {
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("capacity cannot be negative");
|
||||
}
|
||||
if (mode == null) {
|
||||
throw new NullPointerException("mode");
|
||||
}
|
||||
this.capacity = capacity;
|
||||
this.items = new Item[capacity];
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this inventory. Listeners are not copied, they must be
|
||||
* added again yourself! This is so cloned copies don't send updates to
|
||||
* their counterparts.
|
||||
*/
|
||||
@Override
|
||||
public Inventory clone() {
|
||||
Inventory copy = new Inventory(capacity, mode);
|
||||
System.arraycopy(items, 0, copy.items, 0, capacity);
|
||||
copy.size = size;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this inventory contains an item with the specified id.
|
||||
* @param id The item's id.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean contains(int id) {
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Item item = items[i];
|
||||
if (item != null && item.getId() == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of free slots.
|
||||
* @return The number of free slots.
|
||||
*/
|
||||
public int freeSlots() {
|
||||
return capacity - size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the inventory.
|
||||
*/
|
||||
public void clear() {
|
||||
items = new Item[capacity];
|
||||
size = 0;
|
||||
notifyItemsUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the capacity of this inventory.
|
||||
* @return The capacity.
|
||||
*/
|
||||
public int capacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of this inventory - the number of used slots.
|
||||
* @return The size.
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item in the specified slot.
|
||||
* @param slot The slot.
|
||||
* @return The item, or {@code null} if the slot is empty.
|
||||
* @throws IndexOutOfBoundsException if the slot is out of bounds.
|
||||
*/
|
||||
public Item get(int slot) {
|
||||
checkBounds(slot);
|
||||
return items[slot];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item that is in the specified slot.
|
||||
* @param slot The slot.
|
||||
* @param item The item, or {@code null} to remove the item that is in the
|
||||
* slot.
|
||||
* @return The item that was in the slot.
|
||||
* @throws IndexOutOfBoundsException if the slot is out of bounds.
|
||||
*/
|
||||
public Item set(int slot, Item item) {
|
||||
if (item == null) {
|
||||
return reset(slot);
|
||||
}
|
||||
checkBounds(slot);
|
||||
|
||||
Item old = items[slot];
|
||||
if (old == null) {
|
||||
size++;
|
||||
}
|
||||
items[slot] = item;
|
||||
notifyItemUpdated(slot);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the item (if any) that is in the specified slot.
|
||||
* @param slot
|
||||
* @return The item that was in the slot.
|
||||
* @throws IndexOutOfBoundsException if the slot is out of bounds.
|
||||
*/
|
||||
public Item reset(int slot) {
|
||||
checkBounds(slot);
|
||||
|
||||
Item old = items[slot];
|
||||
if (old != null) {
|
||||
size--;
|
||||
}
|
||||
items[slot] = null;
|
||||
notifyItemUpdated(slot);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* An alias for {@code add(id, 1)}.
|
||||
* @param id The id.
|
||||
* @return {@code true} if the item was added, {@code false} if there was
|
||||
* not enough room.
|
||||
*/
|
||||
public boolean add(int id) {
|
||||
return add(id, 1) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* An alias for {@code add(new Item(id, amount)}.
|
||||
* @param id The id.
|
||||
* @param amount The amount.
|
||||
* @return The amount that remains.
|
||||
*/
|
||||
public int add(int id, int amount) {
|
||||
Item item = add(new Item(id, amount));
|
||||
if (item != null) {
|
||||
return item.getAmount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to this inventory. This will attempt to add as much of the
|
||||
* item that is possible. If the item remains, it will be returned (in the
|
||||
* case of stackable items, any quantity that remains in the stack is
|
||||
* returned). If nothing remains, the method will return {@code null}. If
|
||||
* something remains, the listener will also be notified which could be
|
||||
* used, for example, to send a message to the player.
|
||||
* @param item The item to add to this inventory.
|
||||
* @return The item that remains if there is not enough room in the
|
||||
* inventory. If nothing remains, {@code null}.
|
||||
*/
|
||||
public Item add(Item item) {
|
||||
int id = item.getId();
|
||||
boolean stackable = isStackable(item.getDefinition());
|
||||
if (stackable) {
|
||||
for (int slot = 0; slot < capacity; slot++) {
|
||||
Item other = items[slot];
|
||||
if (other != null && other.getId() == id) {
|
||||
long total = item.getAmount() + other.getAmount();
|
||||
int amount;
|
||||
int remaining;
|
||||
if (total > Integer.MAX_VALUE) {
|
||||
amount = (int) (total - Integer.MAX_VALUE);
|
||||
remaining = (int) (total - amount);
|
||||
notifyCapacityExceeded();
|
||||
} else {
|
||||
amount = (int) total;
|
||||
remaining = 0;
|
||||
}
|
||||
set(slot, new Item(id, amount));
|
||||
return remaining > 0 ? new Item(id, remaining): null;
|
||||
}
|
||||
}
|
||||
for (int slot = 0; slot < capacity; slot++) {
|
||||
Item other = items[slot];
|
||||
if (other == null) {
|
||||
set(slot, item);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
notifyCapacityExceeded();
|
||||
return item;
|
||||
}
|
||||
|
||||
int remaining = item.getAmount();
|
||||
|
||||
stopFiringEvents();
|
||||
try {
|
||||
Item single = new Item(item.getId(), 1);
|
||||
for (int slot = 0; slot < capacity; slot++) {
|
||||
if (items[slot] == null) {
|
||||
remaining--;
|
||||
set(slot, single); // share the instances
|
||||
if (remaining <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
startFiringEvents();
|
||||
}
|
||||
|
||||
if (remaining != item.getAmount()) {
|
||||
notifyItemsUpdated();
|
||||
}
|
||||
if (remaining > 0) {
|
||||
notifyCapacityExceeded();
|
||||
}
|
||||
|
||||
return new Item(item.getId(), remaining);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one item with the specified id.
|
||||
* @param id The id.
|
||||
* @return {@code true} if the item was removed, {@code false} otherwise.
|
||||
*/
|
||||
public boolean remove(int id) {
|
||||
return remove(id, 1) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* An alias for {@code remove(item.getId(), item.getAmount())}.
|
||||
* @param item The item to remove.
|
||||
* @return The amount that was removed.
|
||||
*/
|
||||
public int remove(Item item) {
|
||||
return remove(item.getId(), item.getAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@code amount} of the item with the specified {@code id}. If the
|
||||
* item is stackable, it will remove it from the stack. If not, it'll
|
||||
* remove {@code amount} items.
|
||||
* @param id The id.
|
||||
* @param amount The amount.
|
||||
* @return The amount that was removed.
|
||||
*/
|
||||
public int remove(int id, int amount) {
|
||||
ItemDefinition def = ItemDefinition.forId(id);
|
||||
boolean stackable = isStackable(def);
|
||||
if (stackable) {
|
||||
for (int slot = 0; slot < capacity; slot++) {
|
||||
Item item = items[slot];
|
||||
if (item != null && item.getId() == id) {
|
||||
if (amount >= item.getAmount()) {
|
||||
set(slot, null);
|
||||
return item.getAmount();
|
||||
} else {
|
||||
int newAmount = item.getAmount() - amount;
|
||||
set(slot, new Item(item.getId(), newAmount));
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int removed = 0;
|
||||
for (int slot = 0; slot < capacity; slot++) {
|
||||
Item item = items[slot];
|
||||
if (item != null && item.getId() == id) {
|
||||
set(slot, null);
|
||||
removed++;
|
||||
}
|
||||
if (removed >= amount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts all items to the top left of the container, leaving no gaps.
|
||||
*/
|
||||
public void shift() {
|
||||
Item[] old = items;
|
||||
items = new Item[capacity];
|
||||
for (int i = 0, pos = 0; i < items.length; i++) {
|
||||
if (old[i] != null) {
|
||||
items[pos++] = old[i];
|
||||
}
|
||||
}
|
||||
if (firingEvents) {
|
||||
notifyItemsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the two items at the specified slots.
|
||||
* @param oldSlot The old slot.
|
||||
* @param newSlot The new slot.
|
||||
* @throws IndexOutOufBoundsException if the slot is out of bounds.
|
||||
*/
|
||||
public void swap(int oldSlot, int newSlot) {
|
||||
swap(false, oldSlot, newSlot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the two items at the specified slots.
|
||||
* @param insert If the swap should be done in insertion mode.
|
||||
* @param oldSlot The old slot.
|
||||
* @param newSlot The new slot.
|
||||
* @throws IndexOutOfBoundsException if the slot is out of bounds.
|
||||
*/
|
||||
public void swap(boolean insert, int oldSlot, int newSlot) {
|
||||
checkBounds(oldSlot);
|
||||
checkBounds(newSlot);
|
||||
if (insert) {
|
||||
if (newSlot > oldSlot) {
|
||||
for (int slot = oldSlot; slot < newSlot; slot++) {
|
||||
swap(slot, slot + 1);
|
||||
}
|
||||
} else if (oldSlot > newSlot) {
|
||||
for (int slot = oldSlot; slot > newSlot; slot--) {
|
||||
swap(slot, slot - 1);
|
||||
}
|
||||
} // else no change is required - aren't we lucky?
|
||||
forceRefresh();
|
||||
} else {
|
||||
Item temp = items[oldSlot];
|
||||
items[oldSlot] = items[newSlot];
|
||||
items[newSlot] = temp;
|
||||
notifyItemsUpdated(); // TODO can we just fire for the two slots?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener.
|
||||
* @param listener The listener to add.
|
||||
*/
|
||||
public void addListener(InventoryListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener.
|
||||
* @param listener The listener to remove.
|
||||
*/
|
||||
public void removeListener(InventoryListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the listeners.
|
||||
*/
|
||||
public void removeAllListeners() {
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners that the capacity of this inventory has been
|
||||
* exceeded.
|
||||
*/
|
||||
private void notifyCapacityExceeded() {
|
||||
if (firingEvents) {
|
||||
for (InventoryListener listener : listeners) {
|
||||
listener.capacityExceeded(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners that all the items have been updated.
|
||||
*/
|
||||
private void notifyItemsUpdated() {
|
||||
if (firingEvents) {
|
||||
for (InventoryListener listener : listeners) {
|
||||
listener.itemsUpdated(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners that the specified slot has been updated.
|
||||
* @param slot The slot.
|
||||
*/
|
||||
private void notifyItemUpdated(int slot) {
|
||||
if (firingEvents) {
|
||||
Item item = items[slot];
|
||||
for (InventoryListener listener : listeners) {
|
||||
listener.itemUpdated(this, slot, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the bounds of the specified slot.
|
||||
* @param slot The slot.
|
||||
* @throws IndexOutOfBoundsException if the slot is out of bounds.
|
||||
*/
|
||||
private void checkBounds(int slot) {
|
||||
if (slot < 0 || slot >= capacity) {
|
||||
throw new IndexOutOfBoundsException("slot out of bounds");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item specified by the definition should be stacked.
|
||||
* @param def The definition.
|
||||
* @return {@code true} if the item should be stacked, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
private boolean isStackable(ItemDefinition def) {
|
||||
if (mode == StackMode.STACK_ALWAYS) {
|
||||
return true;
|
||||
} else if (mode == StackMode.STACK_STACKABLE_ITEMS) {
|
||||
return def.isStackable();
|
||||
} else { // will be STACK_NEVER
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a clone of the items array.
|
||||
* @return A clone of the items array.
|
||||
*/
|
||||
public Item[] getItems() {
|
||||
return items.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the firing of events.
|
||||
*/
|
||||
public void stopFiringEvents() {
|
||||
firingEvents = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the firing of events.
|
||||
*/
|
||||
public void startFiringEvents() {
|
||||
firingEvents = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the refresh of this inventory.
|
||||
*/
|
||||
public void forceRefresh() {
|
||||
notifyItemsUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a refresh of a specific slot.
|
||||
* @param slot The slot.
|
||||
*/
|
||||
public void forceRefresh(int slot) {
|
||||
notifyItemUpdated(slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the capacity to exceeded event to be fired.
|
||||
*/
|
||||
public void forceCapacityExceeded() {
|
||||
notifyCapacityExceeded();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Holds {@link Inventory}-related constants.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class InventoryConstants {
|
||||
|
||||
/**
|
||||
* The capacity of the bank.
|
||||
*/
|
||||
public static final int BANK_CAPACITY = 352;
|
||||
|
||||
/**
|
||||
* The capacity of the inventory.
|
||||
*/
|
||||
public static final int INVENTORY_CAPACITY = 28;
|
||||
|
||||
/**
|
||||
* The capacity of the equipment inventory.
|
||||
*/
|
||||
public static final int EQUIPMENT_CAPACITY = 14;
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation.
|
||||
*/
|
||||
private InventoryConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
import org.apollo.game.model.def.ItemDefinition;
|
||||
|
||||
/**
|
||||
* Represents a single item.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Item {
|
||||
|
||||
/**
|
||||
* The item's id.
|
||||
*/
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* The amount of items in the stack.
|
||||
*/
|
||||
private final int amount;
|
||||
|
||||
/**
|
||||
* Creates an item with an amount of {@code 1}.
|
||||
* @param id The item's id.
|
||||
*/
|
||||
public Item(int id) {
|
||||
this(id, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an item with the specified the amount.
|
||||
* @param id The item's id.
|
||||
* @param amount The amount.
|
||||
* @throws IllegalArgumentException if the amount is negative.
|
||||
*/
|
||||
public Item(int id, int amount) {
|
||||
if (amount < 0) {
|
||||
throw new IllegalArgumentException("negative amount");
|
||||
}
|
||||
this.id = id;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id.
|
||||
* @return The id.
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount.
|
||||
* @return The amount.
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link ItemDefinition} which describes this item.
|
||||
* @return The definition.
|
||||
*/
|
||||
public ItemDefinition getDefinition() {
|
||||
return ItemDefinition.forId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Item.class.getName() + " [id=" + id + ", amount=" + amount + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.event.impl.IdAssignmentEvent;
|
||||
import org.apollo.game.event.impl.LogoutEvent;
|
||||
import org.apollo.game.event.impl.SwitchTabInterfaceEvent;
|
||||
import org.apollo.game.model.inter.bank.BankConstants;
|
||||
import org.apollo.game.model.inv.AppearanceInventoryListener;
|
||||
import org.apollo.game.model.inv.FullInventoryListener;
|
||||
import org.apollo.game.model.inv.InventoryListener;
|
||||
import org.apollo.game.model.inv.SynchronizationInventoryListener;
|
||||
import org.apollo.game.model.skill.LevelUpSkillListener;
|
||||
import org.apollo.game.model.skill.SkillListener;
|
||||
import org.apollo.game.model.skill.SynchronizationSkillListener;
|
||||
import org.apollo.game.sync.block.SynchronizationBlock;
|
||||
import org.apollo.net.session.GameSession;
|
||||
import org.apollo.security.PlayerCredentials;
|
||||
|
||||
/**
|
||||
* A {@link Player} is a {@link Character} that a user is controlling.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Player extends Character {
|
||||
|
||||
/**
|
||||
* An enumeration with the different privilege levels a player can have.
|
||||
* @author Graham
|
||||
*/
|
||||
public enum PrivilegeLevel {
|
||||
|
||||
/**
|
||||
* A standard (rights 0) account.
|
||||
*/
|
||||
STANDARD(0),
|
||||
|
||||
/**
|
||||
* A player moderator (rights 1) account.
|
||||
*/
|
||||
MODERATOR(1),
|
||||
|
||||
/**
|
||||
* An administrator (rights 2) account.
|
||||
*/
|
||||
ADMINISTRATOR(2);
|
||||
|
||||
/**
|
||||
* Gets the privilege level for the specified numerical level.
|
||||
* @param numericalLevel The numerical level.
|
||||
* @return The privilege level.
|
||||
* @throws IllegalArgumentException if the numerical level is invalid.
|
||||
*/
|
||||
public static PrivilegeLevel valueOf(int numericalLevel) {
|
||||
for (PrivilegeLevel level : values()) {
|
||||
if (level.numericalLevel == numericalLevel) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("invalid numerical level");
|
||||
}
|
||||
|
||||
/**
|
||||
* The numerical level used in the protocol.
|
||||
*/
|
||||
private final int numericalLevel;
|
||||
|
||||
/**
|
||||
* Creates a privilege level.
|
||||
* @param numericalLevel The numerical level.
|
||||
*/
|
||||
private PrivilegeLevel(int numericalLevel) {
|
||||
this.numericalLevel = numericalLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the numerical level.
|
||||
* @return The numerical level used in the protocol.
|
||||
*/
|
||||
public int toInteger() {
|
||||
return numericalLevel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary queue of events sent during the login process.
|
||||
*/
|
||||
private final Queue<Event> queuedEvents = new ArrayDeque<Event>();
|
||||
|
||||
/**
|
||||
* The player's credentials.
|
||||
*/
|
||||
private PlayerCredentials credentials;
|
||||
|
||||
/**
|
||||
* The privilege level.
|
||||
*/
|
||||
private PrivilegeLevel privilegeLevel = PrivilegeLevel.STANDARD;
|
||||
|
||||
/**
|
||||
* The membership flag.
|
||||
*/
|
||||
private boolean members = false;
|
||||
|
||||
/**
|
||||
* A flag indicating if the player has designed their character.
|
||||
*/
|
||||
private boolean designedCharacter = false;
|
||||
|
||||
/**
|
||||
* The {@link GameSession} currently attached to this {@link Player}.
|
||||
*/
|
||||
private GameSession session;
|
||||
|
||||
/**
|
||||
* The centre of the last region the client has loaded.
|
||||
*/
|
||||
private Position lastKnownRegion;
|
||||
|
||||
/**
|
||||
* A flag indicating if the region changed in the last cycle.
|
||||
*/
|
||||
private boolean regionChanged = false;
|
||||
|
||||
/**
|
||||
* The player's appearance.
|
||||
*/
|
||||
private Appearance appearance = Appearance.DEFAULT_APPEARANCE;
|
||||
|
||||
/**
|
||||
* The current maximum viewing distance of this player.
|
||||
*/
|
||||
private int viewingDistance = 1;
|
||||
|
||||
/**
|
||||
* A flag which indicates there are players that couldn't be added.
|
||||
*/
|
||||
private boolean excessivePlayers = false;
|
||||
|
||||
/**
|
||||
* This player's interface set.
|
||||
*/
|
||||
private final InterfaceSet interfaceSet = new InterfaceSet(this);
|
||||
|
||||
/**
|
||||
* A flag indicating if the player is withdrawing items as notes.
|
||||
*/
|
||||
private boolean withdrawingNotes = false; // TODO find a better place!
|
||||
|
||||
/**
|
||||
* Creates the {@link Player}.
|
||||
* @param credentials The player's credentials.
|
||||
* @param position The initial position.
|
||||
*/
|
||||
public Player(PlayerCredentials credentials, Position position) {
|
||||
super(position);
|
||||
init();
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this player's interface set.
|
||||
* @return The interface set for this player.
|
||||
*/
|
||||
public InterfaceSet getInterfaceSet() {
|
||||
return interfaceSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are excessive players.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isExcessivePlayersSet() {
|
||||
return excessivePlayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the excessive players flag.
|
||||
*/
|
||||
public void flagExcessivePlayers() {
|
||||
excessivePlayers = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the excessive players flag.
|
||||
*/
|
||||
public void resetExcessivePlayers() {
|
||||
excessivePlayers = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this player's viewing distance.
|
||||
*/
|
||||
public void resetViewingDistance() {
|
||||
viewingDistance = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this player's viewing distance.
|
||||
* @return The viewing distance.
|
||||
*/
|
||||
public int getViewingDistance() {
|
||||
return viewingDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments this player's viewing distance if it is less than the maximum
|
||||
* viewing distance.
|
||||
*/
|
||||
public void incrementViewingDistance() {
|
||||
if (viewingDistance < Position.MAX_DISTANCE) {
|
||||
viewingDistance++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements this player's viewing distance if it is greater than 1.
|
||||
*/
|
||||
public void decrementViewingDistance() {
|
||||
if (viewingDistance > 1) { // TODO should it be 0?
|
||||
viewingDistance--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this player has ever known a region.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean hasLastKnownRegion() {
|
||||
return lastKnownRegion != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last known region.
|
||||
* @return The last known region, or {@code null} if the player has never
|
||||
* known a region.
|
||||
*/
|
||||
public Position getLastKnownRegion() {
|
||||
return lastKnownRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last known region.
|
||||
* @param lastKnownRegion The last known region.
|
||||
*/
|
||||
public void setLastKnownRegion(Position lastKnownRegion) {
|
||||
this.lastKnownRegion = lastKnownRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the privilege level.
|
||||
* @return The privilege level.
|
||||
*/
|
||||
public PrivilegeLevel getPrivilegeLevel() {
|
||||
return privilegeLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the privilege level.
|
||||
* @param privilegeLevel The privilege level.
|
||||
*/
|
||||
public void setPrivilegeLevel(PrivilegeLevel privilegeLevel) {
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this player account has membership.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the membership status of this player.
|
||||
* @param members The new membership flag.
|
||||
*/
|
||||
public void setMembers(boolean members) {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the player's {@link GameSession}.
|
||||
* @param session The player's {@link GameSession}.
|
||||
* @param reconnecting The reconnecting flag.
|
||||
*/
|
||||
public void setSession(GameSession session, boolean reconnecting) {
|
||||
this.session = session;
|
||||
if (!reconnecting) {
|
||||
sendInitialEvents();
|
||||
}
|
||||
getBlockSet().add(SynchronizationBlock.createAppearanceBlock(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's credentials.
|
||||
* @return The player's credentials.
|
||||
*/
|
||||
public PlayerCredentials getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Event event) {
|
||||
if (isActive()) {
|
||||
if (!queuedEvents.isEmpty()) {
|
||||
for (Event queuedEvent : queuedEvents) {
|
||||
session.dispatchEvent(queuedEvent);
|
||||
}
|
||||
queuedEvents.clear();
|
||||
}
|
||||
session.dispatchEvent(event);
|
||||
} else {
|
||||
queuedEvents.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises this player.
|
||||
*/
|
||||
private void init() {
|
||||
initInventories();
|
||||
initSkills();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the player's skills.
|
||||
*/
|
||||
private void initSkills() {
|
||||
SkillSet skills = getSkillSet();
|
||||
|
||||
// synchronization listener
|
||||
SkillListener syncListener = new SynchronizationSkillListener(this);
|
||||
|
||||
// level up listener
|
||||
SkillListener levelUpListener = new LevelUpSkillListener(this);
|
||||
|
||||
// add the listeners
|
||||
skills.addListener(syncListener);
|
||||
skills.addListener(levelUpListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the player's inventories.
|
||||
*/
|
||||
private void initInventories() {
|
||||
Inventory inventory = getInventory();
|
||||
Inventory bank = getBank();
|
||||
Inventory equipment = getEquipment();
|
||||
|
||||
// TODO only add bank listener when it is open? (like Hyperion)
|
||||
|
||||
// inventory full listeners
|
||||
InventoryListener fullInventoryListener = new FullInventoryListener(this, FullInventoryListener.FULL_INVENTORY_MESSAGE);
|
||||
InventoryListener fullBankListener = new FullInventoryListener(this, FullInventoryListener.FULL_BANK_MESSAGE);
|
||||
InventoryListener fullEquipmentListener = new FullInventoryListener(this, FullInventoryListener.FULL_EQUIPMENT_MESSAGE);
|
||||
|
||||
// equipment appearance listener
|
||||
InventoryListener appearanceListener = new AppearanceInventoryListener(this);
|
||||
|
||||
// synchronization listeners
|
||||
InventoryListener syncInventoryListener = new SynchronizationInventoryListener(this, SynchronizationInventoryListener.INVENTORY_ID);
|
||||
InventoryListener syncBankListener = new SynchronizationInventoryListener(this, BankConstants.BANK_INVENTORY_ID);
|
||||
InventoryListener syncEquipmentListener = new SynchronizationInventoryListener(this, SynchronizationInventoryListener.EQUIPMENT_ID);
|
||||
|
||||
// add the listeners
|
||||
inventory.addListener(syncInventoryListener);
|
||||
inventory.addListener(fullInventoryListener);
|
||||
bank.addListener(syncBankListener);
|
||||
bank.addListener(fullBankListener);
|
||||
equipment.addListener(syncEquipmentListener);
|
||||
equipment.addListener(appearanceListener);
|
||||
equipment.addListener(fullEquipmentListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the initial events.
|
||||
*/
|
||||
private void sendInitialEvents() {
|
||||
// vital initial stuff
|
||||
send(new IdAssignmentEvent(getIndex(), members)); // TODO should this be sent when we reconnect?
|
||||
sendMessage("Welcome to RuneScape.");
|
||||
|
||||
// character design screen
|
||||
if (!designedCharacter) {
|
||||
interfaceSet.openWindow(3559); // TODO make the interface id a constant or something?
|
||||
}
|
||||
|
||||
// tabs TODO make a constant? look at player settings
|
||||
int[] tabs = {
|
||||
// 6299 = music tab, music disabled
|
||||
// 4445 = settings tab, music disabled
|
||||
// 12855 = ancients magic
|
||||
2423, 3917, 638, 3213, 1644, 5608, 1151, -1, 5065, 5715, 2449, 904, 147, 962,
|
||||
};
|
||||
for (int i = 0; i < tabs.length; i++) {
|
||||
send(new SwitchTabInterfaceEvent(i, tabs[i]));
|
||||
}
|
||||
|
||||
// force inventories to update
|
||||
getInventory().forceRefresh();
|
||||
getEquipment().forceRefresh();
|
||||
getBank().forceRefresh();
|
||||
|
||||
// force skills to update
|
||||
getSkillSet().forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Player.class.getName() + " [username=" + credentials.getUsername() + ", privilegeLevel=" + privilegeLevel +"]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the region changed flag.
|
||||
* @param regionChanged A flag indicating if the region has changed.
|
||||
*/
|
||||
public void setRegionChanged(boolean regionChanged) {
|
||||
this.regionChanged = regionChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the region has changed.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean hasRegionChanged() {
|
||||
return regionChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's appearance.
|
||||
* @return The appearance.
|
||||
*/
|
||||
public Appearance getAppearance() {
|
||||
return appearance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the player's appearance.
|
||||
* @param appearance The new appearance.
|
||||
*/
|
||||
public void setAppearance(Appearance appearance) {
|
||||
this.appearance = appearance;
|
||||
this.getBlockSet().add(SynchronizationBlock.createAppearanceBlock(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's name.
|
||||
* @return The player's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return credentials.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's name, encoded as a long.
|
||||
* @return The encoded player name.
|
||||
*/
|
||||
public long getEncodedName() {
|
||||
return credentials.getEncodedUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the player out, if possible.
|
||||
*/
|
||||
public void logout() {
|
||||
send(new LogoutEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the game session.
|
||||
* @return The game session.
|
||||
*/
|
||||
public GameSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the character design flag.
|
||||
* @param designedCharacter A flag indicating if the character has been
|
||||
* designed.
|
||||
*/
|
||||
public void setDesignedCharacter(boolean designedCharacter) {
|
||||
this.designedCharacter = designedCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has designed their character.
|
||||
* @return A flag indicating if the player has designed their character.
|
||||
*/
|
||||
public boolean hasDesignedCharacter() {
|
||||
return designedCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the withdrawing notes flag.
|
||||
* @return The flag.
|
||||
*/
|
||||
public boolean isWithdrawingNotes() {
|
||||
return withdrawingNotes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the withdrawing notes flag.
|
||||
* @param withdrawingNotes The flag.
|
||||
*/
|
||||
public void setWithdrawingNotes(boolean withdrawingNotes) {
|
||||
this.withdrawingNotes = withdrawingNotes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(Position position) {
|
||||
super.teleport(position); // TODO put this in the same place as Character#teleport and WalkEventHandler!!
|
||||
interfaceSet.close(); // TODO: should this be done if size == 0?
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Represents a position in the world.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Position {
|
||||
|
||||
/**
|
||||
* The number of height levels.
|
||||
*/
|
||||
public static final int HEIGHT_LEVELS = 4;
|
||||
|
||||
/**
|
||||
* The maximum distance players/NPCs can 'see'.
|
||||
*/
|
||||
public static final int MAX_DISTANCE = 15;
|
||||
|
||||
/**
|
||||
* The x coordinate.
|
||||
*/
|
||||
private final int x;
|
||||
|
||||
/**
|
||||
* The y coordinate.
|
||||
*/
|
||||
private final int y;
|
||||
|
||||
/**
|
||||
* The height level.
|
||||
*/
|
||||
private final int height;
|
||||
|
||||
/**
|
||||
* Creates a position at the default height.
|
||||
* @param x The x coordinate.
|
||||
* @param y The y coordinate.
|
||||
*/
|
||||
public Position(int x, int y) {
|
||||
this(x, y, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a position with the specified height.
|
||||
* @param x The x coordinate.
|
||||
* @param y The y coordinate.
|
||||
* @param height The height.
|
||||
*/
|
||||
public Position(int x, int y, int height) {
|
||||
if (height < 0 || height >= HEIGHT_LEVELS) {
|
||||
throw new IllegalArgumentException("Height out of bounds");
|
||||
}
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x coordinate.
|
||||
* @return The x coordinate.
|
||||
*/
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y coordinate.
|
||||
* @return The y coordinate.
|
||||
*/
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the height level.
|
||||
* @return The height level.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x coordinate of the region.
|
||||
* @return The region x coordinate.
|
||||
*/
|
||||
public int getTopLeftRegionX() {
|
||||
return (x / 8) - 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y coordinate of the region.
|
||||
* @return The region y coordinate.
|
||||
*/
|
||||
public int getTopLeftRegionY() {
|
||||
return (y / 8) - 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x coordinate of the central region.
|
||||
* @return The x coordinate of the central region.
|
||||
*/
|
||||
public int getCentralRegionX() {
|
||||
return x / 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y coordinate of the central region.
|
||||
* @return The y coordinate of the central region.
|
||||
*/
|
||||
public int getCentralRegionY() {
|
||||
return y / 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x coordinate inside the region of this position.
|
||||
* @return The local x coordinate.
|
||||
*/
|
||||
public int getLocalX() {
|
||||
return getLocalX(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y coordinate inside the region of this position.
|
||||
* @return The local y coordinate.
|
||||
*/
|
||||
public int getLocalY() {
|
||||
return getLocalY(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local x coordinate inside the region of the {@code base}
|
||||
* position.
|
||||
* @param base The base position.
|
||||
* @return The local x coordinate.
|
||||
*/
|
||||
public int getLocalX(Position base) {
|
||||
return x - (base.getTopLeftRegionX() * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local y coordinate inside the region of the {@code base}
|
||||
* position.
|
||||
* @param base The base position.
|
||||
* @return The local y coordinate.
|
||||
*/
|
||||
public int getLocalY(Position base) {
|
||||
return y - (base.getTopLeftRegionY() * 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ((height << 30) & 0xC0000000) | ((y << 15) & 0x3FFF8000) | (x & 0x7FFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the distance between this position and another position. Only X and
|
||||
* Y are considered (i.e. 2 dimensions).
|
||||
* @param other The other position.
|
||||
* @return The distance.
|
||||
*/
|
||||
public int getDistance(Position other) {
|
||||
int deltaX = x - other.x;
|
||||
int deltaY = y - other.y;
|
||||
// TODO will rounding up interfere with other stuff?
|
||||
return (int) Math.ceil(Math.sqrt(deltaX * deltaX + deltaY * deltaY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the longest horizontal or vertical delta between the two positions.
|
||||
* @param other The other position.
|
||||
* @return The longest horizontal or vertical delta.
|
||||
*/
|
||||
public int getLongestDelta(Position other) {
|
||||
int deltaX = x - other.x;
|
||||
int deltaY = y - other.y;
|
||||
return Math.max(deltaX, deltaY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the position is within distance of another.
|
||||
* @param other The other position.
|
||||
* @param distance The distance.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isWithinDistance(Position other, int distance) {
|
||||
int deltaX = Math.abs(x - other.x);
|
||||
int deltaY = Math.abs(y - other.y);
|
||||
return deltaX <= distance && deltaY <= distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Position other = (Position) obj;
|
||||
if (height != other.height) {
|
||||
return false;
|
||||
}
|
||||
if (x != other.x) {
|
||||
return false;
|
||||
}
|
||||
if (y != other.y) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Position.class.getName() + " [x=" + x + ", y=" + y + ", height=" + height + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
/**
|
||||
* Represents a single skill.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Skill {
|
||||
|
||||
/**
|
||||
* The attack id.
|
||||
*/
|
||||
public static final int ATTACK = 0;
|
||||
|
||||
/**
|
||||
* The defence id.
|
||||
*/
|
||||
public static final int DEFENCE = 1;
|
||||
|
||||
/**
|
||||
* The strength id.
|
||||
*/
|
||||
public static final int STRENGTH = 2;
|
||||
|
||||
/**
|
||||
* The hitpoints id.
|
||||
*/
|
||||
public static final int HITPOINTS = 3;
|
||||
|
||||
/**
|
||||
* The ranged id.
|
||||
*/
|
||||
public static final int RANGED = 4;
|
||||
|
||||
/**
|
||||
* The prayer id.
|
||||
*/
|
||||
public static final int PRAYER = 5;
|
||||
|
||||
/**
|
||||
* The magic id.
|
||||
*/
|
||||
public static final int MAGIC = 6;
|
||||
|
||||
/**
|
||||
* The cooking id.
|
||||
*/
|
||||
public static final int COOKING = 7;
|
||||
|
||||
/**
|
||||
* The woodcutting id.
|
||||
*/
|
||||
public static final int WOODCUTTING = 8;
|
||||
|
||||
/**
|
||||
* The fletching id.
|
||||
*/
|
||||
public static final int FLETCHING = 9;
|
||||
|
||||
/**
|
||||
* The fishing id.
|
||||
*/
|
||||
public static final int FISHING = 10;
|
||||
|
||||
/**
|
||||
* The firemaking id.
|
||||
*/
|
||||
public static final int FIREMAKING = 11;
|
||||
|
||||
/**
|
||||
* The crafting id.
|
||||
*/
|
||||
public static final int CRAFTING = 12;
|
||||
|
||||
/**
|
||||
* The smithing id.
|
||||
*/
|
||||
public static final int SMITHING = 13;
|
||||
|
||||
/**
|
||||
* The mining id.rivate
|
||||
*/
|
||||
public static final int MINING = 14;
|
||||
|
||||
/**
|
||||
* The herblore id.
|
||||
*/
|
||||
public static final int HERBLORE = 15;
|
||||
|
||||
/**
|
||||
* The agility id.
|
||||
*/
|
||||
public static final int AGILITY = 16;
|
||||
|
||||
/**
|
||||
* The thieving id.
|
||||
*/
|
||||
public static final int THIEVING = 17;
|
||||
|
||||
/**
|
||||
* The slayer id.
|
||||
*/
|
||||
public static final int SLAYER = 18;
|
||||
|
||||
/**
|
||||
* The farming id.
|
||||
*/
|
||||
public static final int FARMING = 19;
|
||||
|
||||
/**
|
||||
* The runecrafting id.
|
||||
*/
|
||||
public static final int RUNECRAFTING = 20;
|
||||
|
||||
/**
|
||||
* The skill names.
|
||||
*/
|
||||
private static final String SKILL_NAMES[] = { "Attack", "Defence", "Strength", "Hitpoints", "Ranged", "Prayer",
|
||||
"Magic", "Cooking", "Woodcutting", "Fletching", "Fishing", "Firemaking", "Crafting", "Smithing", "Mining",
|
||||
"Herblore", "Agility", "Thieving", "Slayer", "Farming", "Runecraft" };
|
||||
|
||||
/**
|
||||
* Gets the name of a skill.
|
||||
* @param id The skill's id.
|
||||
* @return The skill's name.
|
||||
*/
|
||||
public static String getName(int id) {
|
||||
return SKILL_NAMES[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* The experience.
|
||||
*/
|
||||
private final double experience;
|
||||
|
||||
/**
|
||||
* The current level.
|
||||
*/
|
||||
private final int currentLevel;
|
||||
|
||||
/**
|
||||
* The maximum level.
|
||||
*/
|
||||
private final int maximumLevel;
|
||||
|
||||
/**
|
||||
* Creates a skill.
|
||||
* @param experience The experience.
|
||||
* @param currentLevel The current level.
|
||||
* @param maximumLevel The maximum level.
|
||||
*/
|
||||
public Skill(double experience, int currentLevel, int maximumLevel) {
|
||||
this.experience = experience;
|
||||
this.currentLevel = currentLevel;
|
||||
this.maximumLevel = maximumLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the experience.
|
||||
* @return The experience.
|
||||
*/
|
||||
public double getExperience() {
|
||||
return experience;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current level.
|
||||
* @return The current level.
|
||||
*/
|
||||
public int getCurrentLevel() {
|
||||
return currentLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum level.
|
||||
* @return The maximum level.
|
||||
*/
|
||||
public int getMaximumLevel() {
|
||||
return maximumLevel;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user