package com.kartoflane.superluminal2.ui;
import java.util.HashSet;
import java.util.Iterator;
import com.kartoflane.superluminal2.components.Polygon;
import com.kartoflane.superluminal2.components.enums.Shapes;
import com.kartoflane.superluminal2.core.Database;
import com.kartoflane.superluminal2.core.Grid;
import com.kartoflane.superluminal2.core.LayeredPainter.Layers;
import com.kartoflane.superluminal2.events.SLEvent;
import com.kartoflane.superluminal2.events.SLListener;
import com.kartoflane.superluminal2.mvc.controllers.GibController;
import com.kartoflane.superluminal2.mvc.controllers.props.ArcPropController;
import com.kartoflane.superluminal2.mvc.controllers.props.OffsetPropController;
import com.kartoflane.superluminal2.mvc.controllers.props.OrbitPropController;
import com.kartoflane.superluminal2.mvc.controllers.props.PropController;
import com.kartoflane.superluminal2.utils.Utils;
public class GibPropContainer {
public enum PropControls {
NONE, DIRECTION, LINEAR, ANGULAR;
@Override
public String toString() {
switch (this) {
case LINEAR:
return "Linear Velocity";
case ANGULAR:
return "Angular Velocity";
default:
return name().substring(0, 1) + name().substring(1).toLowerCase();
}
}
}
private static final String DIR_MIN_PROP_ID = "DirectionMin";
private static final String DIR_MAX_PROP_ID = "DirectionMax";
private static final String DIR_ARC_PROP_ID = "DirectionArc";
private static final String ANG_PROP_ID = "AngularValue";
private static final String ANG_ARC_PROP_ID = "AngularArc";
private static final String LIN_MIN_PROP_ID = "LinearMin";
private static final String LIN_MAX_PROP_ID = "LinearMax";
private static final String LIN_MIN_RANGE_PROP_ID = "LinearRangeMin";
private static final String LIN_MAX_RANGE_PROP_ID = "LinearRangeMax";
private GibController currentController = null;
private PropControls currentlyShownControls = PropControls.NONE;
private HashSet<PropController> props = null;
private SLListener selectionListener = null;
private boolean dataLoad = false;
public GibPropContainer() {
selectionListener = new SLListener() {
public void handleEvent(SLEvent e) {
if (currentController != null && !currentController.isSelected() && !currentController.isVisible())
setCurrentController(null);
if (currentlyShownControls == PropControls.DIRECTION) {
PropController minProp = getProp(DIR_MIN_PROP_ID);
PropController maxProp = getProp(DIR_MAX_PROP_ID);
String[] propIds = { DIR_MIN_PROP_ID, DIR_MAX_PROP_ID, DIR_ARC_PROP_ID };
for (String id : propIds) {
PropController prop = getProp(id);
prop.setVisible(currentController != null && currentController.isVisible() &&
(currentController.isSelected() || minProp.isSelected() || maxProp.isSelected()));
}
} else if (currentlyShownControls == PropControls.LINEAR) {
PropController minProp = getProp(LIN_MIN_PROP_ID);
PropController maxProp = getProp(LIN_MAX_PROP_ID);
String[] propIds = { LIN_MIN_PROP_ID, LIN_MAX_PROP_ID, LIN_MIN_RANGE_PROP_ID, LIN_MAX_RANGE_PROP_ID };
for (String id : propIds) {
PropController prop = getProp(id);
prop.setVisible(currentController != null && currentController.isVisible() &&
(currentController.isSelected() || minProp.isSelected() || maxProp.isSelected()));
}
if (currentController != null) {
minProp.setBounded(!currentController.isSelected());
maxProp.setBounded(!currentController.isSelected());
dataLoad = true;
int cs = ShipContainer.CELL_SIZE;
int gridW = Grid.getInstance().getSize().x;
minProp.setBoundingPoints(currentController.getX(), currentController.getY() - cs / 2,
maxProp.getX(), currentController.getY() - cs / 2);
minProp.setFollowOffset(minProp.getFollowOffsetX(), -cs / 2);
minProp.updateFollower();
maxProp.setBoundingPoints(minProp.getX(), currentController.getY() - 2 * cs / 3,
gridW, currentController.getY() - 2 * cs / 3);
maxProp.setFollowOffset(maxProp.getFollowOffsetX(), -2 * cs / 3);
maxProp.updateFollower();
dataLoad = false;
}
} else if (currentlyShownControls == PropControls.ANGULAR) {
PropController angProp = getProp(ANG_PROP_ID);
String[] propIds = { ANG_PROP_ID, ANG_ARC_PROP_ID };
for (String id : propIds) {
PropController prop = getProp(id);
prop.setVisible(currentController != null && currentController.isVisible() &&
(currentController.isSelected() || angProp.isSelected()));
}
}
}
};
createProps();
}
public void setCurrentController(GibController controller) {
if (currentController == controller) {
updateData();
return;
}
dataLoad = true;
if (currentController != null) {
currentController.removeListener(SLEvent.SELECT, selectionListener);
currentController.removeListener(SLEvent.DESELECT, selectionListener);
for (PropController prop : props)
prop.setParent(null);
}
currentController = controller;
if (currentController != null) {
currentController.addListener(SLEvent.SELECT, selectionListener);
currentController.addListener(SLEvent.DESELECT, selectionListener);
}
for (PropController prop : props)
prop.setParent(currentController);
updateData();
if (currentController != null) {
dataLoad = true;
for (PropController prop : props)
prop.updateFollower();
dataLoad = false;
} else {
selectionListener.handleEvent(null);
}
}
public GibController getCurrentController() {
return currentController;
}
/**
* Shows prop controllers responsible for modification of the selected property.
*/
public void showControls(PropControls control) {
if (control == null)
throw new IllegalArgumentException("Argument must not be null.");
currentlyShownControls = control;
String[] dirPropIds = { DIR_MIN_PROP_ID, DIR_MAX_PROP_ID, DIR_ARC_PROP_ID };
for (String id : dirPropIds) {
PropController prop = getProp(id);
prop.setVisible(control == PropControls.DIRECTION && currentController != null && currentController.isVisible());
}
String[] linPropIds = { LIN_MIN_PROP_ID, LIN_MAX_PROP_ID, LIN_MIN_RANGE_PROP_ID, LIN_MAX_RANGE_PROP_ID };
for (String id : linPropIds) {
PropController prop = getProp(id);
prop.setVisible(control == PropControls.LINEAR && currentController != null && currentController.isVisible());
}
String[] angPropIds = { ANG_PROP_ID, ANG_ARC_PROP_ID };
for (String id : angPropIds) {
PropController prop = getProp(id);
prop.setVisible(control == PropControls.ANGULAR && currentController != null && currentController.isVisible());
}
updateData();
}
public PropControls getShownControls() {
return currentlyShownControls;
}
private void updateData() {
if (currentController == null)
return;
OrbitPropController opc;
PropController prop;
dataLoad = true;
if (currentlyShownControls == PropControls.DIRECTION) {
opc = (OrbitPropController) getProp(DIR_MIN_PROP_ID);
opc.reposition(opc.angleToOrbitLocation(currentController.getDirectionMin()));
opc.updateFollowOffset();
opc = (OrbitPropController) getProp(DIR_MAX_PROP_ID);
opc.reposition(opc.angleToOrbitLocation(currentController.getDirectionMax()));
opc.updateFollowOffset();
} else if (currentlyShownControls == PropControls.LINEAR) {
double velocity = 0;
prop = getProp(LIN_MIN_PROP_ID);
prop.setBounded(false);
velocity = currentController.getLinearVelocityMin();
velocity *= Database.GIB_DEATH_ANIM_TIME * Database.GIB_LINEAR_SPEED;
prop.setFollowOffset((int) Math.round(velocity), prop.getFollowOffsetY());
prop.updateFollower();
prop.setBounded(!currentController.isSelected());
prop = getProp(LIN_MAX_PROP_ID);
prop.setBounded(false);
velocity = currentController.getLinearVelocityMax();
velocity *= Database.GIB_DEATH_ANIM_TIME * Database.GIB_LINEAR_SPEED;
prop.setFollowOffset((int) Math.round(velocity), prop.getFollowOffsetY());
prop.updateFollower();
prop.setBounded(!currentController.isSelected());
} else if (currentlyShownControls == PropControls.ANGULAR) {
double angle = 0;
opc = (OrbitPropController) getProp(ANG_PROP_ID);
angle = currentController.getAngularVelocityMax();
angle *= Database.GIB_DEATH_ANIM_TIME * Database.GIB_ANGULAR_SPEED;
angle = Math.toDegrees(angle);
opc.reposition(opc.angleToOrbitLocation(angle));
opc.updateFollowOffset();
}
dataLoad = false;
}
private void addProp(PropController prop) {
if (prop == null)
throw new IllegalArgumentException("Argument must not be null.");
if (props == null)
props = new HashSet<PropController>();
if (getProp(prop.getIdentifier()) != null)
throw new IllegalArgumentException(String.format("This object already owns a prop named '%s'", prop.getIdentifier()));
props.add(prop);
}
/**
* @param id
* the identifier of the sought prop
* @return prop with the given identifier, or null if not found
*/
private PropController getProp(String id) {
if (id == null)
throw new IllegalArgumentException("Argument must not be null.");
if (props == null) {
return null;
} else {
PropController result = null;
Iterator<PropController> it = props.iterator();
while (it.hasNext() && result == null) {
PropController p = it.next();
if (p.getIdentifier().equals(id))
result = p;
}
return result;
}
}
private void createProps() {
createDirectionProps();
createLinearVelocityProps();
createAngularVelocityProps();
}
/**
* Creates the prop controllers responsible for direction modification.
*/
private void createDirectionProps() {
SLListener arcListener = new SLListener() {
public void handleEvent(SLEvent e) {
if (currentlyShownControls != PropControls.DIRECTION)
return;
ArcPropController arcProp = (ArcPropController) getProp(DIR_ARC_PROP_ID);
PropController minProp = getProp(DIR_MIN_PROP_ID);
PropController maxProp = getProp(DIR_MAX_PROP_ID);
double minAngle = Utils.angle(arcProp.getLocation(), minProp.getLocation());
double maxAngle = Utils.angle(arcProp.getLocation(), maxProp.getLocation());
if (Math.abs(minAngle - maxAngle) <= 1) {
// If min and max angles are within 1 degree, consider them to be equal
arcProp.setStartAngle(0);
arcProp.setArcSpan(360);
if (!dataLoad) {
currentController.setDirectionMin(0);
currentController.setDirectionMax(360);
}
} else if (minAngle < maxAngle) {
arcProp.setStartAngle((int) Math.round(minAngle) + 90);
arcProp.setArcSpan((int) Math.round(maxAngle - minAngle));
if (!dataLoad) {
currentController.setDirectionMin((int) Math.round(minAngle));
currentController.setDirectionMax((int) Math.round(maxAngle));
}
} else if (minAngle > maxAngle) {
arcProp.setStartAngle((int) Math.round(maxAngle) + 90);
arcProp.setArcSpan((int) Math.round(minAngle - maxAngle - 360));
if (!dataLoad) {
currentController.setDirectionMin((int) Math.round(minAngle - 360));
currentController.setDirectionMax((int) Math.round(maxAngle));
}
}
arcProp.redraw();
}
};
ArcPropController arcProp = new ArcPropController(null, DIR_ARC_PROP_ID);
arcProp.setInheritVisibility(false);
arcProp.setVisible(false);
arcProp.setDefaultBackgroundColor(0, 255, 0);
arcProp.setAlpha(128);
arcProp.setSize(200, 200);
arcProp.addToPainter(Layers.PROP);
arcProp.updateView();
addProp(arcProp);
OrbitPropController orbitProp = new OrbitPropController(null, DIR_MIN_PROP_ID);
orbitProp.setShape(Shapes.OVAL);
orbitProp.setSelectable(true);
orbitProp.setInheritVisibility(false);
orbitProp.setVisible(false);
orbitProp.setDefaultBorderColor(0, 0, 0);
orbitProp.setDefaultBackgroundColor(32, 164, 164);
orbitProp.setOrbitOffset(100);
orbitProp.setSize(2 * ShipContainer.CELL_SIZE / 3, 2 * ShipContainer.CELL_SIZE / 3);
orbitProp.setBorderThickness(3);
orbitProp.addToPainter(Layers.PROP);
orbitProp.updateView();
orbitProp.setCompositeTitle("Minimum Direction");
orbitProp.addListener(SLEvent.SELECT, selectionListener);
orbitProp.addListener(SLEvent.DESELECT, selectionListener);
orbitProp.addListener(SLEvent.MOVE, arcListener);
addProp(orbitProp);
orbitProp = new OrbitPropController(null, DIR_MAX_PROP_ID);
orbitProp.setShape(Shapes.OVAL);
orbitProp.setSelectable(true);
orbitProp.setInheritVisibility(false);
orbitProp.setVisible(false);
orbitProp.setDefaultBorderColor(0, 0, 0);
orbitProp.setDefaultBackgroundColor(64, 255, 255);
orbitProp.setOrbitOffset(120);
orbitProp.setSize(2 * ShipContainer.CELL_SIZE / 3, 2 * ShipContainer.CELL_SIZE / 3);
orbitProp.setBorderThickness(3);
orbitProp.addToPainter(Layers.PROP);
orbitProp.updateView();
orbitProp.setCompositeTitle("Maximum Direction");
orbitProp.addListener(SLEvent.SELECT, selectionListener);
orbitProp.addListener(SLEvent.DESELECT, selectionListener);
orbitProp.addListener(SLEvent.MOVE, arcListener);
addProp(orbitProp);
}
/**
* Creates the prop controllers responsible for linear velocity modification.
*/
public void createLinearVelocityProps() {
ArcPropController apc = new ArcPropController(null, LIN_MAX_RANGE_PROP_ID);
apc.setInheritVisibility(false);
apc.setVisible(false);
apc.setDefaultBackgroundColor(255, 32, 32);
apc.setAlpha(128);
apc.setStartAngle(0);
apc.setArcSpan(360);
apc.addToPainter(Layers.PROP);
apc.updateView();
addProp(apc);
apc = new ArcPropController(null, LIN_MIN_RANGE_PROP_ID);
apc.setInheritVisibility(false);
apc.setVisible(false);
apc.setDefaultBackgroundColor(128, 196, 196);
apc.setAlpha(128);
apc.setStartAngle(0);
apc.setArcSpan(360);
apc.addToPainter(Layers.PROP);
apc.updateView();
addProp(apc);
OffsetPropController opc = new OffsetPropController(null, LIN_MIN_PROP_ID);
opc.setPolygon(new Polygon(new int[] {
0, 0,
ShipContainer.CELL_SIZE / 2, 0,
ShipContainer.CELL_SIZE / 4, ShipContainer.CELL_SIZE / 2
}));
opc.addListener(SLEvent.MOVE, new SLListener() {
@Override
public void handleEvent(SLEvent e) {
if (currentlyShownControls != PropControls.LINEAR)
return;
ArcPropController arc = (ArcPropController) getProp(LIN_MIN_RANGE_PROP_ID);
OffsetPropController opc = (OffsetPropController) getProp(LIN_MIN_PROP_ID);
int dist = opc.getX() - arc.getParent().getX();
arc.resize(Math.abs(dist * 2), Math.abs(dist * 2));
if (!dataLoad)
currentController.setLinearVelocityMin(((double) dist) / (Database.GIB_DEATH_ANIM_TIME * Database.GIB_LINEAR_SPEED));
}
});
opc.setVisible(false);
opc.setFollowOffset(0, -ShipContainer.CELL_SIZE / 2);
opc.setDefaultBackgroundColor(255, 0, 0);
opc.setDefaultBorderColor(0, 0, 0);
opc.addToPainter(Layers.PROP);
opc.setCompositeTitle("Minimum Linear Velocity");
opc.addListener(SLEvent.SELECT, selectionListener);
opc.addListener(SLEvent.DESELECT, selectionListener);
addProp(opc);
opc = new OffsetPropController(null, LIN_MAX_PROP_ID);
opc.setPolygon(new Polygon(new int[] {
0, 0,
ShipContainer.CELL_SIZE / 2, 0,
ShipContainer.CELL_SIZE / 4, 2 * ShipContainer.CELL_SIZE / 3
}));
opc.addListener(SLEvent.MOVE, new SLListener() {
@Override
public void handleEvent(SLEvent e) {
if (currentlyShownControls != PropControls.LINEAR)
return;
ArcPropController arc = (ArcPropController) getProp(LIN_MAX_RANGE_PROP_ID);
OffsetPropController opc = (OffsetPropController) getProp(LIN_MAX_PROP_ID);
int dist = opc.getX() - arc.getParent().getX();
arc.resize(Math.abs(dist * 2), Math.abs(dist * 2));
if (!dataLoad)
currentController.setLinearVelocityMax(((double) dist) / (Database.GIB_DEATH_ANIM_TIME * Database.GIB_LINEAR_SPEED));
}
});
opc.setVisible(false);
opc.setFollowOffset(0, -2 * ShipContainer.CELL_SIZE / 3);
opc.setDefaultBackgroundColor(255, 0, 0);
opc.setDefaultBorderColor(0, 0, 0);
opc.addToPainter(Layers.PROP);
opc.setCompositeTitle("Maximum Linear Velocity");
opc.addListener(SLEvent.SELECT, selectionListener);
opc.addListener(SLEvent.DESELECT, selectionListener);
addProp(opc);
}
/**
* Creates the prop controllers responsible for angular velocity modification.
*/
public void createAngularVelocityProps() {
SLListener angularListener = new SLListener() {
public void handleEvent(SLEvent e) {
if (currentlyShownControls != PropControls.ANGULAR)
return;
PropController angProp = getProp(ANG_PROP_ID);
ArcPropController angArc = (ArcPropController) getProp(ANG_ARC_PROP_ID);
double angle = Utils.angle(currentController.getLocation(), angProp.getLocation());
angArc.setArcSpan((int) Math.round(angle));
angArc.redraw();
if (!dataLoad) {
// Angular velocities are symmetrical, only differ by sign
currentController.setAngularVelocityMin(-Math.toRadians(angle) / (Database.GIB_DEATH_ANIM_TIME * Database.GIB_ANGULAR_SPEED));
currentController.setAngularVelocityMax(Math.toRadians(angle) / (Database.GIB_DEATH_ANIM_TIME * Database.GIB_ANGULAR_SPEED));
}
}
};
ArcPropController arc = new ArcPropController(null, ANG_ARC_PROP_ID);
arc.setInheritVisibility(false);
arc.setVisible(false);
arc.setDefaultBackgroundColor(64, 192, 255);
arc.setAlpha(128);
arc.setSize(200, 200);
arc.setStartAngle(90);
arc.addToPainter(Layers.PROP);
arc.updateView();
addProp(arc);
OrbitPropController orbitProp = new OrbitPropController(null, ANG_PROP_ID);
orbitProp.setShape(Shapes.OVAL);
orbitProp.setSelectable(true);
orbitProp.setInheritVisibility(false);
orbitProp.setVisible(false);
orbitProp.setDefaultBorderColor(0, 0, 0);
orbitProp.setDefaultBackgroundColor(64, 192, 255);
orbitProp.setOrbitOffset(100);
orbitProp.setSize(2 * ShipContainer.CELL_SIZE / 3, 2 * ShipContainer.CELL_SIZE / 3);
orbitProp.setBorderThickness(3);
orbitProp.addToPainter(Layers.PROP);
orbitProp.updateView();
orbitProp.setCompositeTitle("Angular Velocity");
orbitProp.addListener(SLEvent.SELECT, selectionListener);
orbitProp.addListener(SLEvent.DESELECT, selectionListener);
orbitProp.addListener(SLEvent.MOVE, angularListener);
addProp(orbitProp);
}
public void dispose() {
for (PropController prop : props) {
prop.dispose();
}
props.clear();
}
}