/******************************************************************************
* 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.awt.Color;
import java.util.ArrayList;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import net.sf.robocode.bv3d.MVCManager;
import net.sf.robocode.bv3d.NetXMLAnimator;
import net.sf.robocode.bv3d.Scene;
import net.sf.robocode.bv3d.robocode.BigExplosion;
import net.sf.robocode.bv3d.robocode.Bullet;
import net.sf.robocode.bv3d.robocode.BulletWake;
import net.sf.robocode.bv3d.robocode.Explosion;
import net.sf.robocode.bv3d.robocode.Field;
import net.sf.robocode.bv3d.robocode.LittleExplosion;
import net.sf.robocode.bv3d.robocode.RobocodeOptionable;
import net.sf.robocode.bv3d.robocode.SkyDome;
import net.sf.robocode.bv3d.robocode.Tank;
import net.sf.robocode.bv3d.robocode.Track;
import net.sf.robocode.bv3d.scenegraph.Drawable;
import net.sf.robocode.bv3d.scenegraph.Light;
import net.sf.robocode.bv3d.scenegraph.TransformationNode;
/**
* @author Marco Della Vedova - pixelinstrument.net
* @author Matteo Foppiano - pixelinstrument.net
*
*/
// TODO this needs to be revisited, probably rewritten from scratch or deleted
// TODO we no longer use XML
public class NetXMLAnimator4Robocode extends NetXMLAnimator implements RobocodeOptionable {
private static final String TANK = "tank";
private static final String BULLET = "bullet";
/** 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. FPS are set to 200 for take the same speed of the client.
* @param manager the MVCManager
* @param portNumber Port number for the connection
*/
public NetXMLAnimator4Robocode(MVCManager manager, int portNumber) {
super(manager, portNumber);
this.setFPS(200); // this is for take the same speed of the client
tanks = new ArrayList<Tank>();
bullets = new ArrayList<Bullet>();
tracks = new ArrayList<Track>();
explosions = new ArrayList<Explosion>();
time = 0;
tankTrack = false;
explosion = false;
bulletWake = false;
}
public NetXMLAnimator4Robocode(MVCManager manager) {
this(manager, 4444);
}
/**
* Redirect the processing to {@link NetXMLAnimator4Robocode#setupScene(Node)} or
* {@link NetXMLAnimator4Robocode#processTurn(Node)}.
*/
@Override
protected void processXMLNode(Node el) {
if (el.getNodeName().equals("settings")) {
setupScene(el);
} else if (el.getNodeName().equals("turn")) {
// this.turn = el;
processTurn(el);
}
}
/**
* Here Battlefield and Lights are created and added to Scene.
* Tanks settings are read and passed to {@link NetXMLAnimator4Robocode#newTNode(Node)}
* @param settings The XML-formed line with the settings
* @see Field
* @see Light
*/
protected void setupScene(Node settings) {
if (field != null) {
clearScene();
}
// 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
NodeList nodelist = settings.getChildNodes();
float width = Float.parseFloat(nodelist.item(0).getAttributes().getNamedItem("sizeX").getNodeValue());
float heigth = Float.parseFloat(nodelist.item(0).getAttributes().getNamedItem("sizeY").getNodeValue());
this.field = new Field(width, heigth);
addDrawableToScene(field);
// Last, tanks settings passed at newTNode(Node)
for (int i = 1; i < nodelist.getLength(); i++) {
addPrimary(newTNode(nodelist.item(i)));
}
displayMessage("New Battle is now displaying");
}
private void processTurn(Node turn) {
this.time = Integer.parseInt(turn.getAttributes().getNamedItem("time").getNodeValue());
// Second, process all the xml nodes
ArrayList<Node> nodes = new ArrayList<Node>();
NodeList nodelist = turn.getChildNodes();
for (int i = 0; i < nodelist.getLength(); i++) {
nodes.add(nodelist.item(i));
}
processNodesThisTurn(nodes);
// 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 processNodesThisTurn(ArrayList<Node> nodesThisTurn) {
// Find Nodes to delete
for (TransformationNode tn : this.getTanksAndBullets()) {
boolean stillActive = false;
for (Node n : nodesThisTurn) {
if (tn.getName().equals(n.getAttributes().getNamedItem("name").getNodeValue())) {
stillActive = true;
break;
}
}
if (!stillActive) {
this.removeDrawableFromScene(tn);
}
}
// Update existing Nodes and create new Nodes
for (Node n : nodesThisTurn) {
String nName = n.getAttributes().getNamedItem("name").getNodeValue();
boolean isNew = true;
for (TransformationNode tn : this.getTanksAndBullets()) {
if (tn.getName().equals(nName)) {
isNew = false;
updateNode(tn, n);
}
}
if (isNew) {
addPrimary(newTNode(n));
}
}
}
/**
* Primaries are children of Field, like tanks and bullets.
* @param tn Tank, Bullet, Track or Explosion to add
*/
protected 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);
}
}
protected ArrayList<TransformationNode> getTanksAndBullets() {
ArrayList<TransformationNode> al = new ArrayList<TransformationNode>();
al.addAll(tanks);
al.addAll(bullets);
return al;
}
/**
* 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((Tank) 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((Bullet) 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);
}
private TransformationNode newTNode(Node n) {
TransformationNode tn = null;
if (n.getNodeName().equals(TANK)) {
Tank tank = new Tank(n.getAttributes().getNamedItem("name").getNodeValue());
Color body = null, head = null, radar = null;
if (n.getAttributes().getNamedItem("body_color") != null) {
body = new Color(Integer.parseInt(n.getAttributes().getNamedItem("body_color").getNodeValue()));
}
if (n.getAttributes().getNamedItem("head_color") != null) {
head = new Color(Integer.parseInt(n.getAttributes().getNamedItem("head_color").getNodeValue()));
}
if (n.getAttributes().getNamedItem("radar_color") != null) {
head = new Color(Integer.parseInt(n.getAttributes().getNamedItem("radar_color").getNodeValue()));
}
tank.setColors(body, head, radar);
tn = tank;
} else if (n.getNodeName().equals(BULLET)) {
tn = new Bullet(n.getAttributes().getNamedItem("name").getNodeValue());
((Bullet) (tn)).setPower(Float.parseFloat(n.getAttributes().getNamedItem("power").getNodeValue()));
updateNode(tn, n);
}
// updateNode(tn, n);
return tn;
}
/**
* Read the XML {@link Node} and update the respective {@link TransformationNode}.
* Here there are also the creations of tank {@link Track} and {@link BulletWake}.
* @param tn {@link Bullet} or {@link Tank} to update
* @param n the XML Node from which take data
*/
private void updateNode(TransformationNode tn, Node n) {
if (n.getNodeName().equals(TANK)) {
Tank tank = (Tank) tn;
// Reading information from XML
float xpos = Float.parseFloat(n.getAttributes().getNamedItem("xpos").getNodeValue());
float ypos = Float.parseFloat(n.getAttributes().getNamedItem("ypos").getNodeValue());
ypos = field.getHeight() - ypos;
float angle = Float.parseFloat(n.getAttributes().getNamedItem("angle").getNodeValue());
float energy = Float.parseFloat(n.getAttributes().getNamedItem("energy").getNodeValue());
Node xhead = n.getFirstChild();
float head = Float.parseFloat(xhead.getAttributes().getNamedItem("angle").getNodeValue());
Node xradar = n.getLastChild();
float radar = Float.parseFloat(xradar.getAttributes().getNamedItem("angle").getNodeValue());
// 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
tank.setRotate(angle, 0, 1, 0);
tank.setTranslate(xpos, 0, ypos);
tank.head.setRotate(head, 0, 1, 0);
tank.radar.setRotate(radar, 0, 1, 0);
tank.setEnergy(energy);
if (this.tankTrack/* && time%4==0 */) {
addPrimary(new Track(this.time, xpos, ypos, angle));
}
} else if (n.getNodeName().equals(BULLET)) {
Bullet bullet = (Bullet) tn;
float xpos = Float.parseFloat(n.getAttributes().getNamedItem("xpos").getNodeValue());
float ypos = Float.parseFloat(n.getAttributes().getNamedItem("ypos").getNodeValue());
ypos = field.getHeight() - ypos;
if (this.bulletWake) {
this.addPrimary(new BulletWake(bullet.getTx(), bullet.getTz(), xpos, ypos));
}
bullet.setTranslate(xpos, 0.07f, ypos);
}
}
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++;
}
}
}
}