package robombs.game.view; import robombs.game.*; import robombs.game.model.*; import robombs.game.sound.*; import robombs.game.util.*; import com.threed.jpct.*; public class BombView extends ClientObject { public static final float RADIUS = 3.7f; static final private long serialVersionUID = 1L; private static Object3D bluePrint = null; private static Object3D bluePrintFuse = null; private final static float INACTIVE_SCALE = 0.4f; private boolean exploding = false; private float scale = 1f; private float scaleInc = -0.015f; private int cnt = 0; private float oldScale = 1f; private long touchedTime = 0; // A small timerout value to avoid double // touching of bombs static { // Static initializer for bomb blueprint bluePrint = Primitives.getSphere(Globals.enhancedGraphics ? 15 : 12, RADIUS); bluePrint.getMesh().compress(); bluePrint.rotateX((float) Math.PI); bluePrint.rotateMesh(); bluePrint.setRotationMatrix(new Matrix()); if (!Globals.normalMapping) { bluePrint.setTexture("bomb1"); } else { TextureInfo ti = new TextureInfo(TextureManager.getInstance().getTextureID("bomb1")); ti.add(TextureManager.getInstance().getTextureID("bombnormals"), TextureInfo.MODE_MODULATE); bluePrint.setTexture(ti); } bluePrint.calcTextureWrapSpherical(); bluePrint.getMesh().compress(); bluePrint.build(); bluePrintFuse = Primitives.getCylinder(6, 0.2f, 7); bluePrintFuse.rotateZ(0.5f); bluePrintFuse.translate(-2, -4.5f, 0); bluePrintFuse.translateMesh(); bluePrintFuse.setTranslationMatrix(new Matrix()); bluePrintFuse.getMesh().compress(); bluePrintFuse.setTexture("bomb1"); bluePrintFuse.calcTextureWrapSpherical(); bluePrintFuse.build(); if (Globals.compiledObjects) { bluePrintFuse.compile(Globals.allDynamic); } bluePrint.translate(100000, 100000, 100000); } /** * Creates a new bomb based on the blueprint. */ public BombView() { super(bluePrint, bluePrintFuse, false); createCollisionMesh(); if (Globals.normalMapping) { ShaderProvider.setShader("normals", this); } } public BombView(ClientObject obj) { super(obj, bluePrintFuse, true); createCollisionMesh(); if (Globals.normalMapping) { ShaderProvider.setShader("normals", this); } } private void createCollisionMesh() { CollisionMesh cm = new CollisionMesh(this, RADIUS); cm.setCollisionOptimization(Object3D.COLLISION_DETECTION_OPTIMIZED); setCollisionMesh(cm); cm.translate(0, -2, 0); } public void addCollisionListener(CollisionListener cl) { super.addCollisionListener(cl); if (colMesh != null) { colMesh.addCollisionListener(cl); } } public void enterCollisionMode(LocalPlayerObject player) { oldScale = getScale(); setScale(1); if (colMesh != null && getVisibility() && getBackValue() != LocalBombManager.VALUE_INACTIVE) { collisionMode = true; setVisibility(false); SimpleVector bombPos = null; if (player != null) { bombPos = getTranslation(); bombPos.y = player.getPosition().y; } if (player == null || bombPos.calcSub(player.getPosition()).length() >= LocalBombManager.ACTIVATION_DISTANCE * 2) { colMesh.setVisibility(true); } else { // If the player is located inside the bomb, it doesn't // block...this happens seldom, // but it happens. Most likely due to latency problems or // something. This hack avoids // frustration at the cost of accuracy. colMesh.setVisibility(false); SimpleVector cor = player.getPosition().calcSub(bombPos).normalize(); player.setLocalMovementCorrection(cor); } } } public void leaveCollisionMode() { setScale(oldScale); super.leaveCollisionMode(); } public void hitByExplosion(CollisionParticipant source, LocalObject obj, DecalManager decal, CollisionEvent ce) { Event event = new Event(Event.EXPLOSION_HIT, -99, obj.getObjectID(), obj.getClientID()); source.getEventQueue().add(event); } public void hitByLocalPlayer(CollisionParticipant source, LocalObject obj, DecalManager decal, CollisionEvent ce) { ClientObject player = (ClientObject) ce.getSource(); Object3D bomb = (Object3D) ce.getObject(); SimpleVector dir = bomb.getTransformedCenter(); dir = dir.calcSub(player.getTranslation()); dir.y = 0; dir = dir.normalize(); SimpleVector vd = player.getBackRotationMatrix().getZAxis(); vd.y = 0; float pa = vd.x * dir.x + vd.y * dir.y + vd.z * dir.z; // Inaccuracies may cause NaN...fix this: if (pa < -1) { pa = -1; } if (pa > 1) { pa = 1; } pa = (float) Math.acos(pa); LocalPlayerObject tmpPlayer = (LocalPlayerObject) player.getModel(); if (tmpPlayer == null) { throw new RuntimeException("Player model is null!"); } long nt = Ticker.getTime() - touchedTime; if ((nt > 100 || nt < 0) && ((pa <= 0.7f && tmpPlayer.getPlayerPowers().canKick()) || tmpPlayer.isInvincible())) { Event event = new Event(Event.BOMB_TOUCHED, source.getObjectID(), obj.getObjectID(), obj.getClientID()); event.setSourceClientID(source.getClientID()); event.setDirection(dir); event.setOrigin(player.getTranslation()); source.getEventQueue().add(event); touchedTime = Ticker.getTime(); } } /** * Process remote bombs. * * @param ticks * time passed since the last run * @param level * the current level */ public void process(long ticks, Level level) { SimpleVector pOld = getTransformedCenter(); if (isModified()) { SimpleVector pos = getBackPosition(); getTranslationMatrix().setIdentity(); translate(pos); setModified(false); float scale = getScale(); setScale(1f); setScale(scale); } else { SimpleVector spd = new SimpleVector(getBackSpeed()); spd.scalarMul(ticks); translate(spd); } float scaleR = getScale(); setScale(1); Matrix tar = getRotationMatrix().cloneMatrix(); tar.interpolate(getRotationMatrix(), getBackRotationMatrix(), 0.4f * (float) ticks); setRotationMatrix(tar); setScale(scaleR); if (!pOld.equals(getTransformedCenter())) { SimpleVector np = getTransformedCenter(); // The bomb has moved! We have to update the bomb mask to reflect // this. LocalBombManager lbm = level.getLocalBombManager(); if (lbm == null) { throw new RuntimeException("No bomb manager assigned!"); } MapMask mask = lbm.getBombMask(); GridPosition gpn = mask.getGrid(np.x, np.z); GridPosition gpo = mask.getGrid(pOld.x, pOld.z); if (!gpn.equals(gpo)) { // It has crossed a grid border! // System.out.println("Bomb has crossed a grid's borders!"); mask.setMaskAt(gpo, MapMask.NO_BOMB); mask.setMaskAt(gpn, MapMask.BOMB); } } int val = getBackValue(); switch (val) { case LocalBombManager.VALUE_INACTIVE: setScale(INACTIVE_SCALE); setTransparency(10); break; case LocalBombManager.VALUE_ACTIVE: // Bomb scale += scaleInc; if (scale >= 1f) { scaleInc *= -1f; scale = 1; } if (scale <= 0.8f) { scaleInc *= -1f; scale = 0.8f; } setScale(scale); setTransparency(-1); this.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS | Object3D.COLLISION_CHECK_SELF); // Fuse SimpleVector dir = child.getYAxis(); dir.scalarMul((float) ticks / ((float) LocalBombManager.LIFE_TIME / 57.14f)); child.translate(dir); ParticleManager parMan = level.getParticleManager(); SimpleVector d = child.getTransformedCenter(); SimpleVector sd = child.getYAxis(); sd.x += 0.5f; sd.scalarMul(-0.7f); d.add(sd); SimpleVector pos = new SimpleVector(sd); pos.normalize(); int sparks = Globals.sparkNumber; float power = Globals.sparkPower; int time = 400; if (Globals.enhancedGraphics) { sparks *= 2; power *= 1.25f; time = 500; } for (int i = 0; i < sparks * ticks; i++) { sd.set(pos); sd.x += 0.5f - Math.random(); sd.y += 0.5f - Math.random(); sd.z += 0.5f - Math.random(); sd.scalarMul(power); parMan.addParticle(d, sd, time, "spark"); } cnt++; if ((cnt & 1) == 1 || Globals.enhancedGraphics) { for (int i = 0; i < ticks; i++) { SimpleVector vel = new SimpleVector((float) Math.random() / 5f, -0.6 + ((float) Math.random() / 7f), (float) Math.random() / 5f); level.getSmokeCloudManager().addSmokeCloud(d, vel, 1500, "smoke", Globals.enhancedGraphics ? 4 : 2); } } break; case LocalBombManager.VALUE_EXPLODING: if (!exploding) { exploding = true; this.setCollisionMode(Object3D.COLLISION_CHECK_NONE); setVisibility(false); SoundManager.getInstance().play("explosion", getTranslation()); level.getExplosionManager().addExplosion(this, level.getEventQueue(), level); level.getDebrisManager().createDebris(this); } break; case LocalBombManager.VALUE_DISABLED: cnt++; setTransparency(-1); if (getScale() <= INACTIVE_SCALE) { // Fix the scale to 1...otherwise, a remote client may still // have the inactive scaling, which is bad... setScale(1f); } if ((cnt & 8) == 8) { d = child.getTransformedCenter(); sd = child.getYAxis(); sd.x += 0.5f; sd.scalarMul(-0.7f); d.add(sd); for (int i = 0; i < ticks; i++) { SimpleVector vel = new SimpleVector((float) Math.random() / 5f, -0.6 + ((float) Math.random() / 7f), (float) Math.random() / 5f); level.getSmokeCloudManager().addSmokeCloud(d, vel, 1500, "smoke", 1); } } break; default: throw new RuntimeException("Unknown value: " + val); } } public void processForBotClient(long ticks, Level level) { SimpleVector pOld = getTransformedCenter(); if (isModified()) { SimpleVector pos = getBackPosition(); getTranslationMatrix().setIdentity(); translate(pos); setModified(false); } else { SimpleVector spd = new SimpleVector(getBackSpeed()); spd.scalarMul(ticks); translate(spd); } if (!pOld.equals(getTransformedCenter())) { SimpleVector np = getTransformedCenter(); // The bomb has moved! We have to update the bomb mask to reflect // this. LocalBombManager lbm = level.getLocalBombManager(); if (lbm == null) { throw new RuntimeException("No bomb manager assigned!"); } MapMask mask = lbm.getBombMask(); GridPosition gpn = mask.getGrid(np.x, np.z); GridPosition gpo = mask.getGrid(pOld.x, pOld.z); if (!gpn.equals(gpo)) { // It has crossed a grid's border! // System.out.println("Bomb has crossed a grid's borders!"); mask.setMaskAt(gpo, MapMask.NO_BOMB); mask.setMaskAt(gpn, MapMask.BOMB); } } int val = getBackValue(); switch (val) { case LocalBombManager.VALUE_ACTIVE: setScale(1); this.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS | Object3D.COLLISION_CHECK_SELF); break; case LocalBombManager.VALUE_EXPLODING: if (!exploding) { exploding = true; this.setCollisionMode(Object3D.COLLISION_CHECK_NONE); setVisibility(false); level.getExplosionManager().addExplosion(this, level.getEventQueue(), level); } break; } } public void processLocal(long ticks, Level level, LocalObject lo) { } }