/***********************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see . *
* *
***********************************************************************************/
package services.objects;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import network.packets.swg.zone.UpdateContainmentMessage;
import resources.Location;
import resources.Terrain;
import resources.objects.SWGObject;
import resources.objects.quadtree.QuadTree;
public class ObjectAwareness {
private static final double AWARE_RANGE = 1024;
private final Map > quadTree;
public ObjectAwareness() {
quadTree = new HashMap>();
}
public boolean initialize() {
loadQuadTree();
return true;
}
private void loadQuadTree() {
for (Terrain t : Terrain.values()) {
quadTree.put(t, new QuadTree(16, -8192, -8192, 8192, 8192));
}
}
/**
* Adds the specified object to the awareness quadtree
* @param object the object to add
*/
public void add(SWGObject object) {
update(object);
Location l = object.getLocation();
if (invalidLocation(l))
return;
QuadTree tree = getTree(l);
synchronized (tree) {
tree.put(l.getX(), l.getZ(), object);
}
}
/**
* Removes the specified object from the awareness quadtree
* @param object the object to remove
*/
public void remove(SWGObject object) {
Location l = object.getLocation();
if (invalidLocation(l))
return;
QuadTree tree = getTree(l);
synchronized (tree) {
tree.remove(l.getX(), l.getZ(), object);
}
}
/**
* This function is used for moving objects within the world, which
* includes moving from a cell to the world.
* @param object the object to move
* @param nLocation the new location
*/
public void move(SWGObject object, Location nLocation) {
if (object.getParent() != null) {
object.getParent().removeObject(object); // Moving from cell to world
object.sendObserversAndSelf(new UpdateContainmentMessage(object.getObjectId(), 0, object.getSlotArrangement()));
} else {
remove(object); // World to World
}
object.setLocation(nLocation);
add(object);
}
/**
* This function is used for moving objects to or within containers,
* probably a cell. This handles the logic for removing the object from the
* previous cell and adding it to the new one, if necessary.
* @param object the object to move
* @param nParent the new parent the object will be in
* @param nLocation the new location relative to the parent
*/
public void move(SWGObject object, SWGObject nParent, Location nLocation) {
SWGObject parent = object.getParent();
if (parent != null && nParent != parent) {
parent.removeObject(object); // Moving from cell to cell, for instance
} else if (parent == null) {
remove(object); // Moving from world to cell
}
if (object.getParent() == null) { // Should have been updated in removeObject()
nParent.addObject(object); // If necessary, add to new cell
object.sendObserversAndSelf(new UpdateContainmentMessage(object.getObjectId(), nParent.getObjectId(), object.getSlotArrangement()));
}
object.setLocation(nLocation);
update(object);
}
/**
* Updates the specified object after it has been moved, or to verify that
* the awareness is up to date
* @param obj the object to update
*/
public void update(SWGObject obj) {
if (obj.isBuildout())
return;
Location l = obj.getWorldLocation();
if (invalidLocation(l))
return;
List objectAware = new LinkedList();
QuadTree tree = getTree(l);
synchronized (tree) {
List range = tree.getWithinRange(l.getX(), l.getZ(), AWARE_RANGE);
for (SWGObject inRange : range) {
if (isValidInRange(obj, inRange, l))
objectAware.add(inRange);
}
}
obj.updateObjectAwareness(objectAware);
}
private QuadTree getTree(Location l) {
return quadTree.get(l.getTerrain());
}
private boolean invalidLocation(Location l) {
return l == null || l.getTerrain() == null;
}
private boolean isValidInRange(SWGObject obj, SWGObject inRange, Location objLoc) {
if (inRange.getObjectId() == obj.getObjectId())
return false;
Location inRangeLoc = inRange.getWorldLocation();
double distSquared = distanceSquared(objLoc, inRangeLoc);
if (inRange.getLoadRange() != 0 && distSquared > square(inRange.getLoadRange()))
return false;
if (inRange.getLoadRange() == 0 && distSquared > square(200))
return false;
return true;
}
private double distanceSquared(Location l1, Location l2) {
return square(l1.getX()-l2.getX()) + square(l1.getY()-l2.getY()) + square(l1.getZ()-l2.getZ());
}
private double square(double x) {
return x * x;
}
}