package com.nage.engine;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import android.util.Log;
import com.nage.components.Component;
import com.nage.components.display.Drawable;
import com.nage.input.Input;
import com.nage.multiplayer.MultiplayerSystem;
import com.nage.utils.ByteArrayBuilder;
public class GameObject {
public float x;
public float y;
public GameGraph parent;
public String name;
ArrayList<Component> objectComponents;
ArrayList<Component> removeQueue;
private ArrayList<Component> addQueue;
public boolean hasMoved;
public boolean remoteObject, localObject; // remote objects will use remote input. local objects use local input. can both be true too.
public String ownerId; // the ID of the phone that owns this object. can be local or remote phone.
public byte owner;
boolean remotelyUpdated;
/** Set to true when the object is modified and this will enable the
* changes to be propagated across multiplayer system.
*/
public boolean modified;
/**
* Unique id for this object that allows it to be identified across
* multiplayer system.
*/
public byte id;
/**
* Assigned to a GameObject when it is created. Make sure this is incremented
* so that each GameObject has a unique id.
*/
private static byte next_id = 0x01;
public GameObject(GameGraph p) {
objectComponents = new ArrayList<Component>();
removeQueue = new ArrayList<Component>();
addQueue = new ArrayList<Component>();
hasMoved = true;
parent = p;
localObject = true; // objects are by default local and not remote.
remoteObject = false;
owner = MultiplayerSystem.DEFAULT;
id = next_id++;
}
public void addComponentWhenPossible(Component c) {
addQueue.add(c);
}
public void addComponent(Component c) {
objectComponents.add(c);
}
public Component getComponent(int id) {
for (Component c : objectComponents) {
if(c.type==id) {
return c;
}
}
return null;
}
public boolean hasComponent(int id) {
for (Component c : objectComponents) {
if(c.type==id) {
return true;
}
}
return false;
}
public void removeComponent(int id) {
for (Component c : objectComponents) {
if(c.type==id) {
removeQueue.add(c);
}
}
}
public void removeComponent(Component c) {
removeQueue.add(c);
}
public ArrayList<Drawable> update(long delta, Input local, Input remote) {
// Log.d("GameObject", " update() start: "+name+" x = "+x+" y = "+y);
if(!removeQueue.isEmpty()) {
for(Component c : removeQueue) {
objectComponents.remove(c);
}
removeQueue.clear();
}
if(!addQueue.isEmpty()) {
for(Component c : addQueue) {
objectComponents.add(c);
}
addQueue.clear();
}
ArrayList<Drawable> drawItems = new ArrayList<Drawable>();
// Log.d("GameObject", "Performing update. Object Name = "+name+", input - remote = "+remote+", local = "+local);
// If we have recieved local input and it is owned by the same owner as this object
if(local!=null && (local.owner==this.owner || local.owner==MultiplayerSystem.DEFAULT)) {
// then update this object with that input.
performUpdate(delta, local, drawItems);
Log.d("GameObject", "Updating "+name+" with local input from "+local.owner);
} else {
// update this object with null input
performUpdate(delta, null, drawItems);
}
// If we have recieved remote input and it is owned by the same owner as this object
if(remote!=null && remote.owner==this.owner) {
// then update this object with that input.
performUpdate(delta, remote, drawItems);
Log.d("GameObject", "Updating "+name+" with remote input from "+remote.owner);
} // otherwise, we don't to update with null input, cause it has already been updated above.
modified = true;
return drawItems;
}
// public void calcVertices(GL10 gl) {
// for(Component c : objectComponents) {
// if(c instanceof Drawable) {
// Drawable d = (Drawable) c;
// d.calcVertices(gl);
// }
// }
// }
private void performUpdate(long delta, Input i, ArrayList<Drawable> drawItems ) {
for (Component c : objectComponents) {
Drawable toAdd = c.update(delta, i);
if (toAdd != null) {
drawItems.add(toAdd);
}
}
}
public void setX(float x) {
this.x = x;
hasMoved = true;
}
public float getX() {
return x;
}
public void setY(float y) {
this.y = y;
hasMoved = true;
}
public float getY() {
return y;
}
public void setX(String x) {
if(x!=null) {
this.x = (float)Integer.parseInt(x);
hasMoved = true;
}
}
public void setY(String y) {
if(y!=null) {
this.y = (float)Integer.parseInt(y);
hasMoved = true;
}
}
/**
* Visit each component in this object and tells it to perform its
* flatten routine using the stream_builder. Adds the overall count
* of components, and a header for each component.
*
* @param stream_builder
* @return
* @throws IOException
*/
public boolean flatten(ByteArrayOutputStream stream_builder) throws IOException {
stream_builder.write(id); // first send obj identification.
stream_builder.write(name.getBytes()); // write name to byte array
stream_builder.write(0); // null terminate the string
stream_builder.write(ByteArrayBuilder.floatToByteArray(x)); // write member variable x
stream_builder.write(ByteArrayBuilder.floatToByteArray(y)); // write member variable y
stream_builder.write(getModifiedCount()); // number of components this object has
int comp_number = 1;
for(Component comp : objectComponents) {
if(comp.modified) {
Log.d("GameObject", "COMP "+comp.id+" ("+comp.getType()+") MODIFIED");
stream_builder.write(comp_number++); // component number
comp.flatten(stream_builder); // component contents
comp.modified = false; // reset modified.
}
}
return comp_number==objectComponents.size();
}
/** Counts the number of objects within graph that have their modified
* flag set to true.
* @return Number of modified objects in graph
*/
private int getModifiedCount() {
int i = 0;
for(Component comp : objectComponents) {
if(comp.modified)
i++;
}
return i;
}
/**
* Expands the byte array b starting at index i. It will check the count of
* components contained in the array, and create that many blank components. Each
* blank component is then asked to expand itself, being given the buffer and an
* index to start expanding from.
*
* @param b Byte array containing a flattened game graph
* @param i Index in array that has been already expanded
* @return
* @throws GameGraphException
*/
public int expand(byte[] b, int i) throws GameGraphException {
id = b[i++]; // read id
StringBuffer str = new StringBuffer();
while(b[i]!=0) { // null terminated, so read till 0 is found.
str.append((char)b[i++]);
}
i++;
name = str.toString(); // read name.
x = ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]); // set x from bytestream
y = ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]); // set y from bytestream
int expected_size = b[i++]; // the is the size of the objectComponents graph
// of the flattened game object. (number of components
// this object has.
Log.d("GameObject", " Expanding "+expected_size+" Components");
for(int j = 1; j <= expected_size; j++) {
if(b[i++]!=j) { throw new GameGraphException("Component numbers do not match"); }
Component comp = Component.createComponent(b[i++], this);
Log.d("GameObject", " Created a blank "+comp.toString());
i = comp.expand(b, i);
addComponent(comp);
}
remotelyUpdated = true;
return i;
}
}
|