package robombs.game.view; import robombs.game.model.*; import robombs.game.util.*; import robombs.clientserver.*; import robombs.game.*; import com.threed.jpct.*; /** * A client object is the visual presentation of a LocalObject. Because we are using a 3D presentation * here, its simply an extended version of jPCT's Object3D class. In addition to a normal Object3D, a ClientObject * holds a few "backbuffer" values, that has been set based on new data from the server but that hasn't been * used in the view until now. */ public class ClientObject extends Object3D implements CollisionParticipant { static final private long serialVersionUID=1L; private boolean isModified = false; private Matrix backRot = new Matrix(); private SimpleVector backPos = new SimpleVector(); private SimpleVector speed = new SimpleVector(); private int backAnim = 0; private int backAnimSpeed = 0; private long lastAccess = 0; private long ticks = 0; private int lastSequence = -1; private int lastAnimSpeed = 1; private boolean isRemote = false; private boolean isTransfered=false; private boolean localOnly=false; private int value=0; private int clientID=-99999999; private int objectID=-99; private boolean childAnimVisible=true; private transient ClientEventQueue eventQueue=null; protected ChildObject3D child = null; protected CollisionMesh colMesh=null; protected boolean collisionMode=false; protected LocalObject model=null; // This is a cyclic reference...bad, but needed! /** * Creates a new, empty client object. This can be used as a "glue" object between other, real, objects. * Just like the dummy Object3D in jPCT would. */ public ClientObject() { super(0); touch(); } /** * Create a new client object based on an existing Object3D. * @param obj the base object */ public ClientObject(Object3D obj) { super(obj); ShaderProvider.setShader("phong", this); touch(); if (Globals.compiledObjects) { compile(Globals.allDynamic); }} /** * Create a new client object based on an existing Object3D. * @param obj the base object */ public ClientObject(Object3D obj, boolean reuseMesh) { super(obj, reuseMesh); ShaderProvider.setShader("phong", this); touch(); if (Globals.compiledObjects) { if (reuseMesh) { shareCompiledData(obj); } compile(Globals.allDynamic); } } /** * Creates a new client object plus a child object. This is a convience method for handling weapons * in this "game". The fact that the actual object is composed of parent and child is reflected in * some methods, not in all. Basically, the behaviour of the composed object is similar to an Object3D * in jPCT that has a child object attached to it. * @param obj the base object * @param child the base object of the child */ public ClientObject(Object3D obj, Object3D child, boolean reuseMesh) { super(obj, reuseMesh); ShaderProvider.setShader("phong", this); addChild(child); if (reuseMesh) { getChild().setMesh(child.getMesh()); } if (Globals.compiledObjects) { if (reuseMesh) { shareCompiledData(obj); getChild().shareCompiledData(child); } compile(Globals.allDynamic); getChild().compile(Globals.allDynamic); } } public ClientObject(Object3D obj, Object3D child) { this(obj, child, false); } /** * Sets the model for the view. This is a cyclic reference as the model also knows its view (if any). * This is ugly, but the collision listener for the bombs needs access to the players model...there * is no way around this...:-( * @param obj */ public void setModel(LocalObject obj) { this.model=obj; } public LocalObject getModel() { return model; } public void addChild(Object3D child) { this.child = new ChildObject3D(child); super.addChild(this.child); touch(); } public void enterCollisionMode() { if (colMesh!=null && getVisibility()) { collisionMode=true; setVisibility(false); colMesh.setVisibility(true); } } public void leaveCollisionMode() { if (colMesh!=null && collisionMode) { collisionMode=false; setVisibility(true); colMesh.setVisibility(Globals.showCollisionMesh); } } public void setCollisionMode(int mode) { super.setCollisionMode(mode); if (colMesh!=null && !Globals.showCollisionMesh) { colMesh.setCollisionMode(mode); } } public void setCollisionMesh(CollisionMesh mesh) { this.colMesh=mesh; super.addChild(colMesh); mesh.setVisibility(Globals.showCollisionMesh); } /** * Do something special based on some object * @param obj */ public void processSpecial(Object obj) {} public void hits(CollisionParticipant target, LocalObject obj, DecalManager decal, CollisionEvent ce) { // do nothing } public void hitByExplosion(CollisionParticipant source, LocalObject obj, DecalManager decal, CollisionEvent ce) { // do nothing } public void hitByLocalPlayer(CollisionParticipant source, LocalObject obj, DecalManager decal, CollisionEvent ce) { // do nothing } public void setClientID(int id) { // Optional...not every ClientObject has to set this. The PlayerDummy does... clientID=id; } public int getClientID() { return clientID; } public void setObjectID(int id) { // Optional...not every ClientObject has to set this. The PlayerDummy does... objectID=id; } public int getObjectID() { return objectID; } public void setEventQueue(ClientEventQueue queue) { this.eventQueue=queue; } public ClientEventQueue getEventQueue() { return eventQueue; } /** * In this default implementation, the method does nothing. You may fill it for a client object * if you want to process something that is based on a local object that has been created by this client and * that should be treated different from what process() itself does. * @param ticks time passed since the last run * @param level the current level * @param lo the LocalObject that this client object represents. Client objects and local objects are not * bound directly, which is why the client object doesn't know of its local one. */ public void processLocal(long ticks, Level level, LocalObject lo) { } /** * In this default implementation, the method does nothing. This is for all kinds of processing like playing * animation, doing the movement etc... * @param ticks time passed since the last run * @param level the current level */ public void process(long ticks, Level level) { } public void processForBotClient(long ticks, Level level) { process(ticks, level); // Defaults to process. May be overridden to change this } public Object3D getChild() { return child; } public void addChildToSuper(Object3D child) { super.addChild(child); } /** * Adds the object as well as a child (if any) to the world. This is similar to World.addObject() from the * jPCT framework but it takes care of the child object. * @param world the world */ public void addToWorld(World world) { world.addObject(this); if (child != null) { world.addObject(child); } if (colMesh!=null) { world.addObject(colMesh); } } public void initTransformation() { SimpleVector pos = getBackPosition(); getTranslationMatrix().setIdentity(); translate(pos); //Matrix m=getBackRotationMatrix(); getRotationMatrix().setTo(getBackRotationMatrix()); //setRotationMatrix(m.cloneMatrix()); } /** * Removes the object as well as a child (if any) from the world. This is similar to World.removeObject() from the * jPCT framework but it takes care of the child object. * @param world the world */ public void removeFromWorld(World world) { try { world.removeObject(this); if (child != null) { world.removeObject(child); } if (colMesh!=null) { world.removeObject(colMesh); } } catch(Exception e) { // Very seldom, it happens that an already removed object should be removed due to some // timing problems...we simply ignore the exception then NetLogger.log("Object "+this.getID()+" already removed from the world!"); } } public void animate(float ix, int seq) { // Synchronize the animations to the sequence. I had funny side-effects // when the bot-client this to play an animation while the normal client was. // The bot client shouldn't play animations at all, but...well...just to be sure... synchronized(super.getAnimationSequence()) { super.animate(ix, seq); } if (child != null) { if (child.getAnimationSequence().getSequenceCount()>=seq) { if (!child.getVisibility()) { child.setRealVisibility(true); } childAnimVisible=true; synchronized(child.getAnimationSequence()) { child.animate(ix, seq); } } else { child.setRealVisibility(false); childAnimVisible=false; } } } public void setSuperVisibility(boolean visi) { super.setVisibility(visi); } public boolean getSuperVisibility() { return super.getVisibility(); } public boolean getVisibility() { boolean res=super.getVisibility(); if (child!=null) { res|=child.getVisibility(); } return res; } public void setVisibility(boolean visi) { super.setVisibility(visi); if (child != null && childAnimVisible) { child.setRealVisibility(visi); } } /** * A short cut to set the clamping mode for this object's animation (and it child object). * @param mode the new clamping mode like in jPCT's Animation class. */ public void setClampingMode(int mode) { if (super.getAnimationSequence()!=null) { super.getAnimationSequence().setClampingMode(mode); if (child != null) { child.getAnimationSequence().setClampingMode(mode); } } } /** * Determines if the object is "old". In this context, an object is old if it hasn't been * touched for more than 5 seconds. * @return boolean is it old? */ public boolean isOld() { return !localOnly && (Ticker.hasPassed(lastAccess, 5000)); } /** * "Touches" the object, i.e. it marks the object as being used with a timestamp. */ public void touch() { lastAccess = Ticker.getTime(); } public void setRemoteFlag(boolean flag) { isRemote = flag; } public void setTransfered(boolean is) { isTransfered=is; } public boolean isTransfered() { return isTransfered; } /** * Returns if this object is a remote one or a local one. * @return boolean is it remote? */ public boolean isRemote() { return isRemote; } /** * Marks this object has being modified. Modified means, that the server has updated the local object which * this object represents. * @param mod true, if it has been modified */ public void setModified(boolean mod) { isModified = mod; } /** * Returns if this object has been modified. Modified means, that the server has updated the local object which * this object represents. * @return boolean is it modified? */ public boolean isModified() { return isModified; } /** * Sets the back rotation matrix. This matrix has been changed due to a change in the server's data but that change * hasn't been reflected in the view yet. * @param mat the new rotation matrix */ public void setBackRotationMatrix(Matrix mat) { backRot = mat; } /** * Returns the back rotation matrix. * @return Matrix the matrix */ public Matrix getBackRotationMatrix() { return backRot; } /** * Sets the back position. This position has been changed due to a change in the server's data but that change * hasn't been reflected in the view yet. * @param pos the new position */ public void setBackPosition(SimpleVector pos) { backPos = pos; } /** * Returns the back position. * @return SimpleVector the position */ public SimpleVector getBackPosition() { return backPos; } /** * Sets the back speed vector. This speed vector has been changed due to a change in the server's data but that change * hasn't been reflected in the view yet. * @param spd the new speed vector */ public void setBackSpeed(SimpleVector spd) { speed = spd; } /** * Returns the nback speed vector. * @return SimpleVector the vector */ public SimpleVector getBackSpeed() { return speed; } /** * Updates the backbuffer of this object with data from a LocalObject. Usually, this object has been * transfered from the server and contains a new position, speed etc... * @param lo the local object that contains the new data */ public void setToLocalObject(LocalObject lo) { if (!lo.getRotation().equals(this.getBackRotationMatrix()) || !lo.getPosition().equals(getBackPosition()) || !lo.getSpeed().equals(getBackSpeed()) || lo.getAnimation()!=getBackAnimation()) { setBackRotationMatrix(lo.getRotation()); setBackPosition(lo.getPosition()); setBackSpeed(lo.getSpeed()); setBackAnimation(lo.getAnimation()); setModified(true); } /*else { if (lo.getType()==Types.PLAYER && !getBackSpeed().equals(SimpleVector.ORIGIN)) { System.out.println(getBackSpeed()+"/"+lo.getRotation()+"/"+lo.getPosition()+"/"+lo.getSpeed()+"/"+lo.getAnimation()); } }*/ setBackAnimationSpeed(lo.getAnimationSpeed()); setBackValue(lo.getValue()); } public int getBackValue() { return value; } public void setBackValue(int val) { value=val; } /** * Resets the animation ticks. A client object doesn't necessarily use an animation, but the LocalObject * itself contains the information. Therefore, the basic animation methods are within this class, even if * it may not use them. */ public void resetTicks() { ticks = 0; } /** * Gets the animation ticks passed. * @return long the ticks */ public long getTicks() { return ticks; } /** * Adds a number of ticks to the current tick count. * @param val the number of ticks to add */ public void addToTicks(long val) { ticks += val; if (ticks < 0) { ticks = lastAnimSpeed - (( -ticks) % lastAnimSpeed); } } /** * Sets the last animation sequence. This is the one that has been used in the last displayed frame. * @param seq the sequence */ public void setLastSequence(int seq) { lastSequence = seq; } /** * Returns the last animation sequence. This is the one that has been used in the last displayed frame. * @return int the sequence */ public int getLastSequence() { return lastSequence; } /** * Sets the animation speed of the currently displayed animation. * @param speed the speed */ public void setAnimSpeed(int speed) { lastAnimSpeed = speed; } /** * Sets the animtion speed of the backbuffer animation. * @param speed the speed */ public void setBackAnimationSpeed(int speed) { backAnimSpeed = speed; } /** * Gets the speed of the backbuffer animation. * @return int the speed */ public int getBackAnimationSpeed() { return backAnimSpeed; } /** * Sets the backbuffer animation sequence. * @param anim the sequence number */ public void setBackAnimation(int anim) { backAnim = anim; } /** * Returns the backbuffer animation sequence. * @return int the sequence number */ public int getBackAnimation() { return backAnim; } public void setAsLocalOnly() { localOnly=true; } public boolean isLocalOnly() { return localOnly; } static class ChildObject3D extends Object3D { protected static final long serialVersionUID=1; public ChildObject3D(Object3D obj) { super(obj); } public void setVisibility(boolean visi) { // Do Nothing, the parent will handle this! } public void setRealVisibility(boolean visi) { super.setVisibility(visi); } } }