package robombs.game.model; import java.util.*; import robombs.game.*; import robombs.clientserver.*; import robombs.game.view.*; import robombs.game.util.*; import robombs.game.ai.*; import com.threed.jpct.*; import com.threed.jpct.util.*; public class LocalBombManager extends LocalObjectManager { private final static Matrix DUMMY_MAT=new Matrix(); private final static SimpleVector DUMMY_SPEED=new SimpleVector(); private final static SimpleVector BOMB_OFFSET=new SimpleVector(0,5.5f,0); private SimpleVector ellipsoidStart = new SimpleVector(BombView.RADIUS-1.5f, BombView.RADIUS-1.5f, BombView.RADIUS-1.5f); public final static long LIFE_TIME=2000; public final static float ACTIVATION_DISTANCE=3.5f; public final static int VALUE_INACTIVE=1; public final static int VALUE_ACTIVE=2; public final static int VALUE_EXPLODING=3; public final static int VALUE_DISABLED=4; public final static int DURATION_MUL=15; private MapMask bombMask=null; private long lifeTime=LIFE_TIME; private ShadowHelper sh=null; private int count=0; /** * Creates a new manager. */ public LocalBombManager(Level level, ShadowHelper shadower) { bombMask=new MapMask(level.getWidth(), level.getHeight()); sh=shadower; level.setLocalBombManager(this); } public void setLifeTime(long time) { lifeTime=time; } public MapMask getBombMask() { return bombMask; } public LocalObject getLocalBombAt(GridPosition gp) { for (LocalObject bomb : objs) { SimpleVector pos=bomb.getPosition(); GridPosition gpb=bombMask.getGrid(pos.x, pos.z); if (gp.equals(gpb)) { return (LocalBomb) bomb; } } return null; } /** * Adds a new bomb to the manager and, if not null, a new view for that bomb to the world. * @param player the player which places the bomb * @param world the world in which the player exists * @return LocalObject the new bomb model */ public synchronized LocalObject addBomb(SimpleClient client, LocalPlayerObject player, World world, ClientEventQueue eventQueue) { SimpleVector pos=new SimpleVector(player.getPosition()); pos.add(BOMB_OFFSET); GridPosition gridPos=bombMask.getGrid(pos.x, pos.z); if (bombMask.getMaskAt(gridPos)!=MapMask.BOMB && !isTooClose(pos)) { if (Globals.useBombingGrid) { // Correct the bomb's position slightly to allow for a more "dynamic" game play SimpleVector gp=gridPos.convertTo3D(); float dx=gp.x-pos.x; float dz=gp.z-pos.z; if (dx>=-Globals.bombingGridWidth && dx<-2.9f) { pos.x+=(dx+2.9f); //System.out.println("x corrected to "+pos.x+" by "+(dx+3)); } if (dx<=Globals.bombingGridWidth && dx>2.9f) { pos.x+=(dx-2.9f); //System.out.println("x corrected to "+pos.x+" by "+(dx-3)); } if (dz>=-Globals.bombingGridWidth && dz<-2.9f) { pos.z+=(dz+2.9f); //System.out.println("z corrected to "+pos.z+" by "+(dz+3)); } if (dz<=Globals.bombingGridWidth && dz>2.9f) { pos.z-=(dz-2.9f); //System.out.println("z corrected to "+pos.z+" by "+(dz+3)); } } gridPos=bombMask.getGrid(pos.x, pos.z); // Correct the grid. It may have changed. bombMask.setMaskAt(gridPos, MapMask.BOMB); LocalBomb bomb=new LocalBomb(client.getClientID()); bomb.setPosition(pos); bomb.setType(Types.BOMB); Matrix rot=DUMMY_MAT; //float yRot=(float)Math.random()*3f; //rot.rotateY(yRot); bomb.setSpecialValue(player.getObjectID()); bomb.setRotation(rot); bomb.setSpeed(DUMMY_SPEED); bomb.setValue(VALUE_INACTIVE); bomb.setAnimationSpeed(player.getPlayerPowers().getFirePower()); // This is the max length of the blow! if (world!=null) { ClientObject b=ClientObjectFactory.getInstance().create(Types.BOMB); b.setEventQueue(eventQueue); // b.rotateY(yRot); b.setBackAnimationSpeed(bomb.getAnimationSpeed()); // This is the max length of the blow in the view! b.setToLocalObject(bomb); b.addToWorld(world); bomb.setView(b); b.getTranslationMatrix().setIdentity(); b.translate(bomb.getPosition()); b.addCollisionListener(new ClientObjectCollisionListener(bomb, true)); if (sh!=null) { sh.addCaster(b); } count++; } objs.add(bomb); return bomb; } return null; } public boolean isTooClose(SimpleVector pos) { for (LocalObject bomb : objs) { if (bomb.getPosition().calcSub(pos).length()<BombView.RADIUS*3f) { return true; } } return false; } public LocalObject getLocalObject(int viewId) { for (LocalObject bomb : objs) { if (bomb.getView()!=null && bomb.getObjectID()==viewId) { return bomb; } } return null; } public void setShadowHelper(ShadowHelper sh) { this.sh=sh; } public void moveBombs(World world, long ticks) { for (LocalObject bomb : objs) { LocalBomb lb=(LocalBomb) bomb; // This cast is save! if (bomb.getValue()!=VALUE_EXPLODING) { if (lb.isMoving() && lb.getView()!=null) { SimpleVector speed=new SimpleVector(lb.getSpeed()); speed.scalarMul(ticks); SimpleVector temp = world.checkCollisionEllipsoid(lb.getPosition(), speed, ellipsoidStart, 2); if (temp.length()/ticks<(Globals.bulletSpeed*2f)-1) { lb.stop(); lb.setSpeed(SimpleVector.ORIGIN); lb.setValue(VALUE_ACTIVE); // The collision radius is smaller than the explosion...correct this by using a collision // detection algo instead of a collision avoidance one. The spherical approach pushes you // out of collisions. temp = world.checkCollisionSpherical(lb.getPosition(), SimpleVector.ORIGIN, BombView.RADIUS); temp.y=0; SimpleVector pos=lb.getPosition(); pos.add(temp); lb.setPosition(pos); lb.setLocalTimeStamp(0); //BOOM! } else { temp.y=0; SimpleVector pos=lb.getPosition(); pos.add(temp); lb.setPosition(pos); temp.scalarMul(1f/(float) ticks); lb.setSpeed(temp); lb.getRotation().rotateY(0.5f*ticks); } } } else { lb.stop(); lb.setSpeed(SimpleVector.ORIGIN); } } } public void processLocalBombs(LocalPlayerObject player, Level level, long ticks) { for (LocalObject bomb : objs) { if (bomb.getValue()==VALUE_INACTIVE) { SimpleVector pos=new SimpleVector(player.getPosition()); pos.y=0; SimpleVector posBomb=new SimpleVector(bomb.getPosition()); posBomb.y=0; float length=pos.calcSub(posBomb).length(); if (length>=BombView.RADIUS+ACTIVATION_DISTANCE || player.isDead()) { bomb.setValue(VALUE_ACTIVE); bomb.setLocalTimeStamp(Ticker.getTime()); } } else { if (bomb.getValue()==VALUE_ACTIVE && Ticker.getTime()-bomb.getLocalTimeStamp()>=lifeTime) { bomb.setValue(VALUE_EXPLODING); bomb.setLocalTimeStamp(Ticker.getTime()); } } ClientObject bv = bomb.getView(); if (bv != null) { bv.setToLocalObject(bomb); bv.process(ticks, level); } } } /** * Sets the visibility for local bombs. Used to disable collision detection of the bots on their own * bombs for a short time to allow them to escape without getting stuck. * @param playerID * @param maxTime * @param visible */ public void setVisibilityOfBombs(int playerID, long maxTime, boolean visible) { for (LocalObject bomb : objs) { if (bomb.getValue()==VALUE_ACTIVE || bomb.getValue()==VALUE_DISABLED) { if (bomb.getSpecialValue()==playerID && (visible || Ticker.hasNotPassed(bomb.getLocalTimeStamp(), maxTime))) { ClientObject co=bomb.getView(); if (co!=null) { co.setVisibility(visible); } } } } } public void enterCollisionMode() { enterCollisionMode(null); } public void enterCollisionMode(LocalPlayerObject player) { if (player!=null) { player.setLocalMovementCorrection(null); } for (LocalObject bomb : objs) { BombView co=(BombView)bomb.getView(); // This cast is save! co.enterCollisionMode(player); } } public void leaveCollisionMode() { for (LocalObject bomb : objs) { ClientObject co=bomb.getView(); co.leaveCollisionMode(); } } public void processBotBombs(List<Bot> bots, Level level, long ticks) { for (LocalObject bomb : objs) { if (bomb.getValue()==VALUE_INACTIVE) { boolean found=false; for (Bot bot:bots) { SimpleVector pos=new SimpleVector(bot.getPosition()); pos.y=0; SimpleVector posBomb=new SimpleVector(bomb.getPosition()); posBomb.y=0; float length=pos.calcSub(posBomb).length(); if (length<BombView.RADIUS+ACTIVATION_DISTANCE+0.25f && !bot.isDead()) { found=true; break; } } if (!found) { bomb.setValue(VALUE_ACTIVE); bomb.setLocalTimeStamp(Ticker.getTime()); } } else { if (bomb.getValue()==VALUE_ACTIVE && Ticker.getTime()-bomb.getLocalTimeStamp()>=lifeTime) { bomb.setValue(VALUE_EXPLODING); bomb.setLocalTimeStamp(Ticker.getTime()); } } ClientObject bv = bomb.getView(); if (bv != null) { bv.setToLocalObject(bomb); bv.processForBotClient(ticks, level); } } } public void explode(int id) { LocalObject lo = null; lo = getLocalObject(id); if (lo != null) { /* // it happens from time to time, that a bomb doesn't get cleared from the mask... // ...no idea why. Maybe this will fix it: SimpleVector pos=lo.getPosition(); GridPosition gridPos=bombMask.getGrid(pos.x, pos.z); bombMask.setMaskAt(gridPos, MapMask.NO_BOMB); */ lo.setSpeed(SimpleVector.ORIGIN); if (lo.getValue()!=LocalBombManager.VALUE_EXPLODING) { // Do this only if the bomb isn't exploding yet... lo.setLocalTimeStamp(0); // Das l�sst die Bombe platzen! :-) if (lo.getValue()==LocalBombManager.VALUE_DISABLED) { lo.setValue(LocalBombManager.VALUE_ACTIVE); } } } else { NetLogger.log("Unable to explode - local bomb already removed!"); } } public boolean startMoving(int id, Event event) { SimpleVector direction= event.getDirection(); LocalObject lo = null; lo = getLocalObject(id); if (lo != null && !((LocalBomb)lo).isMoving()) { if (lo.getValue()==LocalBombManager.VALUE_ACTIVE || lo.getValue()==LocalBombManager.VALUE_DISABLED) { ((LocalBomb)lo).setDirection(direction); lo.setRotation(new Matrix(lo.getRotation())); if (lo.getView()!=null) { // There has to be a view... lo.getView().setCollisionMode(Object3D.COLLISION_CHECK_OTHERS | Object3D.COLLISION_CHECK_SELF); lo.getView().setRotationMatrix(new Matrix(lo.getView().getRotationMatrix())); return true; } } } return false; } public void defuse(int id) { LocalObject lo = null; lo = getLocalObject(id); if (lo != null) { if (lo.getValue()==LocalBombManager.VALUE_ACTIVE) { // Do this only if the bomb is active ATM... lo.setValue(LocalBombManager.VALUE_DISABLED); lo.disable(); count--; } } } public int getCount() { return count; } public int getCount(int objectID) { int cnt=0; for (Iterator<LocalObject> itty=objs.iterator(); itty.hasNext();) { LocalObject bomb=itty.next(); if (bomb.getSpecialValue()==objectID) { cnt++; } } return cnt; } public synchronized List<LocalObject> fillOrKill(ExtendedDataContainer dc, ClientEventQueue eventQueue) { List<LocalObject> toKill = new ArrayList<LocalObject>(); for (Iterator<LocalObject> itty=objs.iterator(); itty.hasNext();) { LocalObject bomb=itty.next(); if (bomb.getValue()==VALUE_EXPLODING && Ticker.hasPassed(bomb.getLocalTimeStamp(), bomb.getAnimationSpeed()*DURATION_MUL)) { // Explosion ended? Get rid of it! if (sh!=null) { sh.removeCaster(bomb.getView()); } itty.remove(); toKill.add(bomb); Event ev = new Event(Event.ENTITY_REMOVE, bomb, bomb); ev.setOrigin(bomb.getPosition()); eventQueue.add(ev); SimpleVector pos=bomb.getPosition(); GridPosition gridPos=bombMask.getGrid(pos.x, pos.z); bombMask.setMaskAt(gridPos, MapMask.NO_BOMB); if (bomb.getView()!=null && !bomb.isDisabled()) { // Actually, there has to be a view...but just to be sure. // This is another reading for: "The bomb has been placed by the local client". count--; } } dc.add(bomb); } return toKill; } }