/****************************************************************************** * Copyright (c) 2008 Marco Della Vedova, Matteo Foppiano * and Pimods contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.pixelinstrument.net/license/cpl-v10.html ******************************************************************************/ package net.sf.robocode.bv3d.robocode.animators; import java.util.ArrayList; import java.awt.*; import net.sf.robocode.bv3d.Animator; import net.sf.robocode.bv3d.MVCManager; import net.sf.robocode.bv3d.Scene; import net.sf.robocode.bv3d.robocode.*; import net.sf.robocode.bv3d.scenegraph.Drawable; import net.sf.robocode.bv3d.scenegraph.Light; import net.sf.robocode.bv3d.scenegraph.TransformationNode; import net.sf.robocode.battle.snapshot.TurnSnapshot; import robocode.control.snapshot.IBulletSnapshot; import robocode.control.snapshot.IRobotSnapshot; /** * @author Marco Della Vedova - pixelinstrument.net * @author Matteo Foppiano - pixelinstrument.net * */ public class Animator4Robocode extends Animator implements RobocodeOptionable { /** Field is the battle-field and is the father of Tanks, Bullets, Tracks and Explosions in TrasformationNode tree * @see Scene#root */ protected Field field; protected ArrayList<Tank> tanks; protected ArrayList<Bullet> bullets; protected ArrayList<Track> tracks; protected ArrayList<Explosion> explosions; protected int time; protected boolean tankTrack, explosion, bulletWake; /** * Initialize all class fields. * @param manager the MVCManager */ public Animator4Robocode(MVCManager manager) { super(manager); tanks = new ArrayList<Tank>(); bullets = new ArrayList<Bullet>(); tracks = new ArrayList<Track>(); explosions = new ArrayList<Explosion>(); time = 0; tankTrack = true; explosion = true; bulletWake = true; } /** * Here Battlefield and Lights are created and added to Scene. * @param xField field width dimension * @param yField field heigth dimension * @see Field * @see Light */ public void setupScene(float xField, float yField) { if (field != null) { clearScene(); tanks = new ArrayList<Tank>(); bullets = new ArrayList<Bullet>(); tracks = new ArrayList<Track>(); explosions = new ArrayList<Explosion>(); } // First, set up Lights: some Lights are independent from the Field // and so, are added directly to the Scene Light light0 = new Light(0); light0.setPosition(-0.4f, 0.99f, 0.7f); light0.setDiffuseIntensity(1, 1, 1); light0.setSpecularIntensity(1, 1, 1); light0.turnOn(); Light light1 = new Light(1); light1.setPosition(0.4f, 0.99f, -0.7f); light1.setDiffuseIntensity(1, 1, 1); light1.setSpecularIntensity(1, 1, 1); light1.turnOn(); addDrawableToScene(light0); addDrawableToScene(light1); // Second, the SkyDome addDrawableToScene(new SkyDome()); // Third, the Field, with the parameters read from XML this.field = new Field(xField, yField); addDrawableToScene(field); displayMessage("New Battle is now displaying"); } /** * Cleen-up the scene */ public void newRound() { this.setupScene(this.field.getWidth(), this.field.getHeight()); } /** * Update the transormation node with the snapshot * @param turn */ public void processTurn(TurnSnapshot turn) { this.time = turn.getTurn(); // Tank management for (IRobotSnapshot robot : turn.getRobots()) { if (robot.getState().isAlive()) { boolean isNew = true; for (Tank tank : tanks) { if (tank.getName().equals(robot.getVeryShortName())) { this.updateTank(tank, robot); isNew = false; break; } } if (isNew) { Tank tank = new Tank(robot.getVeryShortName()); tank.setColors(new Color(robot.getBodyColor(), true), new Color(robot.getGunColor(), true), new Color(robot.getRadarColor(), true)); this.addPrimary(tank); this.updateTank(tank, robot); } } else { int i = 0; while (i < tanks.size()) { Tank t = tanks.get(i); if (t.getName().equals(robot.getVeryShortName())) { removeDrawableFromScene(t); break; } else { i++; } } } } // Bullet management boolean isActive = false; for (int i = 0; i < bullets.size();) { Bullet bulletTN = bullets.get(i); for (IBulletSnapshot bullet : turn.getBullets()) { if (bulletTN.getName().equals("bullet" + bullet.getBulletId())) { isActive = true; break; } } if (!isActive) { this.removeDrawableFromScene(bulletTN); } else { i++; } } for (IBulletSnapshot bullet : turn.getBullets()) { if (bullet.getState().getValue() < 2) { boolean isNew = true; for (Bullet bulletTN : bullets) { if (bulletTN.getName().equals("bullet" + bullet.getBulletId())) { isNew = false; this.updateBullet(bulletTN, bullet); break; } } if (isNew) { Bullet b = new Bullet("bullet" + bullet.getBulletId()); b.setPower((float) bullet.getPower()); this.addPrimary(b); this.updateBullet(b, bullet); } } else { for (int i = 0; i < bullets.size();) { Bullet b = bullets.get(i); if (b.getName().equals("bullet" + bullet.getBulletId())) { removeDrawableFromScene(b); break; } else { i++; } } } } // Tank Track management int i = 0; while (tankTrack && i < tracks.size()) { Track track = tracks.get(i); track.heartBeat(); if (this.time - track.getCreationTime() > Track.LIFETIME) { removeDrawableFromScene(track); } else { i++; } } // Explosions management int j = 0; while (j < explosions.size()) { Explosion exp = explosions.get(j); boolean alive = exp.heartBeat(); if (alive) { j++; } else { removeDrawableFromScene(exp); } } } private void updateTank(Tank t, IRobotSnapshot s) { // Reading information from snapshot float xpos = (float) s.getX(); float ypos = (float) s.getY(); ypos = field.getHeight() - ypos; float angle = (float) s.getBodyHeading(); float energy = (float) s.getEnergy(); float head = (float) s.getGunHeading(); float radar = (float) s.getRadarHeading(); // Normalization angle = -(float) (angle * 180 / Math.PI) - 180; head = -(float) (head * 180 / Math.PI) - angle - 180; radar = -(float) (radar * 180 / Math.PI) - angle - head - 180; // Scenegraph update t.setRotate(angle, 0, 1, 0); t.setTranslate(xpos, 0, ypos); t.head.setRotate(head, 0, 1, 0); t.radar.setRotate(radar, 0, 1, 0); t.setEnergy(energy); if (this.tankTrack) { addPrimary(new Track(this.time, xpos, ypos, angle)); } } private void updateBullet(Bullet b, IBulletSnapshot s) { float xpos = (float) s.getX(); float ypos = (float) s.getY(); ypos = field.getHeight() - ypos; if (this.bulletWake) { this.addPrimary(new BulletWake(b.getTx(), b.getTz(), xpos, ypos)); } b.setTranslate(xpos, 0.07f, ypos); } /** * Primaries are children of Field, like tanks and bullets. * @param tn Tank, Bullet, Track or Explosion to add */ private void addPrimary(TransformationNode tn) { field.addDrawable(tn); if (tn instanceof Tank) { tanks.add((Tank) tn); this.addFollowed(((Tank) tn).getTNHead()); this.addFrontal(((Tank) tn).getText3D()); } else if (tn instanceof Bullet) { bullets.add((Bullet) tn); } else if (tn instanceof Track) { tracks.add((Track) tn); } else if (tn instanceof Explosion) { explosions.add((Explosion) tn); } } /** * Remove from Scene and from class field. * If {@link Drawable} is a {@link Tank}, add new {@link BigExplosion}. * If {@link Drawable} is a {@link Bullet}, add new {@link LittleExplosion}. * @param d the element to remove */ @Override public void removeDrawableFromScene(Drawable d) { if (d instanceof Tank) { tanks.remove(d); this.removeFollowed(((Tank) d).getTNHead()); this.removeFrontal(((Tank) d).getText3D()); // add the explosion! if (explosion) { Explosion exp = new BigExplosion(((Tank) d).getTx(), ((Tank) d).getTz()); this.addPrimary(exp); } } else if (d instanceof Bullet) { bullets.remove(d); if (explosion) { this.addPrimary(new LittleExplosion(((Bullet) d).getTx(), ((Bullet) d).getTz())); } } else if (d instanceof Track) { tracks.remove((Track) d); } else if (d instanceof Explosion) { explosions.remove(d); } super.removeDrawableFromScene(d); } public boolean isTankTrackEnable() { return this.tankTrack; } public void setTankTrackEnable(boolean tt) { this.tankTrack = tt; if (!tankTrack) { while (!tracks.isEmpty()) { removeDrawableFromScene(tracks.get(0)); } } } public boolean isBulletWakeEnable() { return this.bulletWake; } public boolean isExplosionEnable() { return this.explosion; } public void setBulletWakeEnable(boolean bw) { this.bulletWake = bw; int i = 0; while (!bulletWake && i < explosions.size()) { Explosion e = explosions.get(i); if (e instanceof BulletWake) { removeDrawableFromScene(e); } else { i++; } } } public void setExplosionEnable(boolean exp) { this.explosion = exp; int i = 0; while (!explosion && i < explosions.size()) { Explosion e = explosions.get(i); if (e instanceof LittleExplosion || e instanceof BigExplosion) { removeDrawableFromScene(e); } else { i++; } } } @Override protected void setup() {} @Override protected void updateScene() {} }