/**
* Copyright (C) BFH www.bfh.ch 2011
* Code written by: Patrick Dobler, Marc Folly
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.bfh.ti.kybernetik.gui.slick;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.vecmath.Point2d;
import javax.vecmath.Vector2d;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import ch.bfh.ti.kybernetik.engine.controller.Simulator;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.SimulatorObserverCommandState;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.LightBulbModelObserverCommand;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.LightBulbModelObserverCommand.LightBulbModelCommandState;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.LightSensorModelObserverCommand;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.LightSensorModelObserverCommand.LightSensorCommandState;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.RoboterModelObserverCommand;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.RoboterModelObserverCommand.RoboterModelCommandState;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.RoboterMoveListModelObserverCommand;
import ch.bfh.ti.kybernetik.engine.controller.observerCommands.model.RoboterMoveListModelObserverCommand.RoboterMoveModelObserverCommandState;
import ch.bfh.ti.kybernetik.engine.model.LightBulb;
import ch.bfh.ti.kybernetik.engine.model.Roboter;
import ch.bfh.ti.kybernetik.gui.slick.components.LightBulbComponent;
import ch.bfh.ti.kybernetik.gui.slick.components.RoboterComponent;
import ch.bfh.ti.kybernetik.gui.slick.components.RoboterMoveListComponent;
import ch.bfh.ti.kybernetik.gui.slick.components.SelectableSlickComponent;
import ch.bfh.ti.kybernetik.lego.ki.RobKIFactr;
import ch.bfh.ti.kybernetik.lego.ki.RobKIFactr.Wesen;
import ch.bfh.ti.kybernetik.lego.ki.RoboterKI;
/**
*
*
*/
public class GuiSimulator extends BasicGame implements Observer {
private static final int RENDER_UPDATE_RATE = 60;
private Simulator simulator;
private List<RoboterComponent> roboterComponents = new CopyOnWriteArrayList<RoboterComponent>();
private List<LightBulbComponent> lightBulbComponents = new CopyOnWriteArrayList<LightBulbComponent>();
private List<RoboterMoveListComponent> roboterMoveListComponent = new CopyOnWriteArrayList<RoboterMoveListComponent>();
private List<GuiSimulatorListener> guiSimulatorListeners = new CopyOnWriteArrayList<GuiSimulatorListener>();
private boolean renderTracing = false;
private enum GuiSimulatorMode {
REDIRECT_ROBOTS_TO_POINT("Redirect All Robots to Mouse Position"), SLICK_COMPONENT_DRAGDROP("Drag & Drop Component"), ROBOT_DRAGDROP_ROTATION(
"Set new direction vector");
private final String displayText;
GuiSimulatorMode(String displayText) {
this.displayText = displayText;
}
public String getDisplayText() {
return displayText;
}
}
public void registerGuiSimulatorListener(GuiSimulatorListener guiSimulatorListener) {
this.guiSimulatorListeners.add(guiSimulatorListener);
}
private GuiSimulatorMode mode = null;
public GuiSimulator(Simulator simulator) {
super("Kybernetik Simulator");
this.simulator = simulator;
this.simulator.addObserver(this);
}
@Override
public void init(final GameContainer gc) throws SlickException {
simulator.setWidth(gc.getWidth());
simulator.setHeight(gc.getHeight());
gc.setAlwaysRender(true);
gc.setTargetFrameRate(RENDER_UPDATE_RATE);
gc.setMaximumLogicUpdateInterval(1 / RENDER_UPDATE_RATE);
gc.setMaximumLogicUpdateInterval(1 / RENDER_UPDATE_RATE);
Iterator<Roboter> it = simulator.getRoboterList().iterator();
while (it.hasNext()) {
Roboter roboter = it.next();
RoboterComponent rc = new RoboterComponent(roboter, simulator.getRoboterConstructionForRoboter(roboter));
this.roboterComponents.add(rc);
}
Iterator<LightBulb> it2 = simulator.getLightBulbList().iterator();
while (it2.hasNext()) {
LightBulb lightBulb = it2.next();
this.lightBulbComponents.add(new LightBulbComponent(lightBulb));
}
this.mode = GuiSimulatorMode.SLICK_COMPONENT_DRAGDROP;
}
@Override
public void update(GameContainer gc, int delta) throws SlickException {
final Input input = gc.getInput();
final Point2d mouseClickedPointPosition = new Point2d(input.getMouseX(), input.getMouseY());
if (input.isKeyPressed(Input.KEY_S)) {
simulator.setSimulating(true);
} else if (input.isKeyPressed(Input.KEY_Q)) {
simulator.setSimulating(false);
}
// SETTING MODE
if (input.isKeyPressed(Input.KEY_F2)) {
this.mode = GuiSimulatorMode.REDIRECT_ROBOTS_TO_POINT;
} else if (input.isKeyPressed(Input.KEY_F1)) {
this.mode = GuiSimulatorMode.SLICK_COMPONENT_DRAGDROP;
deselectAllSelectableSlickComponents();
}
if (input.isKeyDown(Input.KEY_RIGHT)) {
simulator.setSimulatorSpeed(simulator.getSimulatorSpeed() + 5);
} else if (input.isKeyDown(Input.KEY_LEFT)) {
simulator.setSimulatorSpeed(simulator.getSimulatorSpeed() - 5);
}
// INTERPRETE MODE
if (mode == GuiSimulatorMode.REDIRECT_ROBOTS_TO_POINT) {
if (input.isMouseButtonDown(0)) {
redirectRobotsToPoint(mouseClickedPointPosition);
}
} else if (mode == GuiSimulatorMode.SLICK_COMPONENT_DRAGDROP || mode == GuiSimulatorMode.ROBOT_DRAGDROP_ROTATION) {
interpreteDragAndDropMode(input, mouseClickedPointPosition);
}
}
private void interpreteDragAndDropMode(final Input input, final Point2d mouseClickedPointPosition) {
SelectableSlickComponent<?> selectedSlickComponent = getSelectedSlickComponent();
if (input.isMousePressed(0)) {
// while the user still holds the mouse button pressed
SelectableSlickComponent<?> newlySelectedSlickCompontent = selectSlickComponent(mouseClickedPointPosition, false, false);
SelectableSlickComponent<?> currentSelectedSlickComponent = getSelectedSlickComponent();
if (newlySelectedSlickCompontent != null && newlySelectedSlickCompontent != currentSelectedSlickComponent) {
// another slick component was selected
deselectAllSelectableSlickComponents();
newlySelectedSlickCompontent.setSelected(true);
mode = GuiSimulatorMode.SLICK_COMPONENT_DRAGDROP;
for (GuiSimulatorListener guiSimulator : guiSimulatorListeners) {
if (newlySelectedSlickCompontent instanceof RoboterComponent) {
guiSimulator.selctedRoboterComponent((RoboterComponent) newlySelectedSlickCompontent);
} else if (newlySelectedSlickCompontent instanceof LightBulbComponent) {
guiSimulator.selctedLightComponent((LightBulbComponent) newlySelectedSlickCompontent);
}
}
} else if (newlySelectedSlickCompontent == null && currentSelectedSlickComponent != null) {
// nothing was selected
deselectAllSelectableSlickComponents();
currentSelectedSlickComponent.setSelected(true);
mode = GuiSimulatorMode.ROBOT_DRAGDROP_ROTATION;
} else if (newlySelectedSlickCompontent == currentSelectedSlickComponent) {
// keep on drag and drop mode for the selected component
mode = GuiSimulatorMode.SLICK_COMPONENT_DRAGDROP;
}
} else if (input.isMouseButtonDown(0)) {
// the user stopped to press the mouse button
if (mode == GuiSimulatorMode.SLICK_COMPONENT_DRAGDROP) {
if (selectedSlickComponent != null) {
// set new position of the slick component
selectedSlickComponent.setX(mouseClickedPointPosition.getX());
selectedSlickComponent.setY(mouseClickedPointPosition.getY());
}
} else if (mode == GuiSimulatorMode.ROBOT_DRAGDROP_ROTATION) {
if (selectedSlickComponent instanceof RoboterComponent) {
redirectRobotToPoint((RoboterComponent) selectedSlickComponent, mouseClickedPointPosition);
}
}
} else if (input.isKeyPressed(Input.KEY_DELETE)) {
// the user pressed the DELETE KEY
if (selectedSlickComponent != null) {
if (selectedSlickComponent instanceof RoboterComponent) {
simulator.removeRoboter((Roboter) selectedSlickComponent.getModelObject());
} else if (selectedSlickComponent instanceof LightBulbComponent) {
simulator.removeLightBulb((LightBulb) selectedSlickComponent.getModelObject());
}
}
} else if (input.isKeyPressed(Input.KEY_0)) {
// the user pressed the 0 KEY which changes the RoboterKI
if (selectedSlickComponent instanceof RoboterComponent) {
RoboterComponent selectedRoboterComponent = (RoboterComponent) selectedSlickComponent;
RoboterKI selectedRoboterKI = simulator.getRoboterKIForRoboter(selectedRoboterComponent.getModelObject());
Wesen[] wesenArray = RobKIFactr.Wesen.values();
int pos = Arrays.binarySearch(wesenArray, selectedRoboterKI.getWesen());
if (++pos >= wesenArray.length) {
pos = 0;
}
RoboterKI newRoboterKI = RobKIFactr.getRoboterKI(wesenArray[pos]);
simulator.changeRoboterKIForRoboter(selectedRoboterComponent.getModelObject(), newRoboterKI);
}
}
}
private void redirectRobotToPoint(RoboterComponent rc, Point2d mouseClickedPointPosition) {
Roboter roboter = rc.getModelObject();
Vector2d vec = new Vector2d(mouseClickedPointPosition.getX() - roboter.getX(), mouseClickedPointPosition.getY() - roboter.getY());
vec.normalize();
roboter.setDirection(vec);
}
private void redirectRobotsToPoint(Point2d mouseClickedPointPosition) {
Iterator<RoboterComponent> it = this.roboterComponents.iterator();
while (it.hasNext()) {
RoboterComponent rc = it.next();
Roboter roboter = rc.getModelObject();
Vector2d vec = new Vector2d(mouseClickedPointPosition.getX() - roboter.getX(), mouseClickedPointPosition.getY()
- roboter.getY());
vec.normalize();
roboter.setDirection(vec);
}
}
private void deselectAllSelectableSlickComponents() {
List<SelectableSlickComponent<?>> aList = new ArrayList<SelectableSlickComponent<?>>();
aList.addAll(roboterComponents);
aList.addAll(lightBulbComponents);
Iterator<SelectableSlickComponent<?>> it2 = aList.iterator();
while (it2.hasNext()) {
SelectableSlickComponent<?> rc = it2.next();
rc.setSelected(false);
}
}
private SelectableSlickComponent<?> getSelectedSlickComponent() {
List<SelectableSlickComponent<?>> aList = new ArrayList<SelectableSlickComponent<?>>();
aList.addAll(roboterComponents);
aList.addAll(lightBulbComponents);
Iterator<SelectableSlickComponent<?>> it2 = aList.iterator();
while (it2.hasNext()) {
SelectableSlickComponent<?> rc = it2.next();
if (rc.isSelected())
return rc;
}
return null;
}
private SelectableSlickComponent<?> selectSlickComponent(Point2d mouseClickedPointPosition, boolean selectInModel,
boolean deselectOthers) {
List<SelectableSlickComponent<?>> aList = new ArrayList<SelectableSlickComponent<?>>();
aList.addAll(roboterComponents);
aList.addAll(lightBulbComponents);
Iterator<SelectableSlickComponent<?>> it2 = aList.iterator();
boolean selected = false;
SelectableSlickComponent<?> selectedRoboterComponent = null;
while (it2.hasNext()) {
SelectableSlickComponent<?> rc = it2.next();
if (rc.intersectWithPoint(mouseClickedPointPosition) && selected == false) {
if (selectInModel)
rc.setSelected(true);
selected = true;
selectedRoboterComponent = rc;
} else {
if (deselectOthers)
rc.setSelected(false);
}
}
return selectedRoboterComponent;
}
@Override
public void render(GameContainer gc, final Graphics g) throws SlickException {
g.setAntiAlias(true);
Iterator<RoboterComponent> it = this.roboterComponents.iterator();
while (it.hasNext()) {
RoboterComponent rc = it.next();
rc.render(gc, g);
}
Iterator<LightBulbComponent> it2 = this.lightBulbComponents.iterator();
while (it2.hasNext()) {
LightBulbComponent lbc = it2.next();
lbc.render(gc, g);
}
if (renderTracing) {
Iterator<RoboterMoveListComponent> it3 = this.roboterMoveListComponent.iterator();
while (it3.hasNext()) {
RoboterMoveListComponent rtc = it3.next();
rtc.render(gc, g);
}
}
// Render current mode
g.setColor(Color.green);
if (mode != null) {
g.drawString("Mode: " + mode.getDisplayText(), 300, 0);
}
// Draw Simulator Speed
g.drawString("Simulating Speed: " + simulator.getSimulatorSpeed(), 300, 30);
// Draw Selected Roboter Info
SelectableSlickComponent<?> ssc = this.getSelectedSlickComponent();
if (ssc instanceof RoboterComponent) {
RoboterComponent rc = (RoboterComponent) ssc;
if (rc != null) {
RoboterKI roboterKI = simulator.getRoboterKIForRoboter(rc.getModelObject());
g.drawString("Roboter KI: " + roboterKI.getRoboterKIName(), 300, 60);
}
}
}
@Override
public synchronized void update(Observable o, Object observerCommand) {
if (observerCommand instanceof SimulatorObserverCommandState) {
SimulatorObserverCommandState simulatorCommand = (SimulatorObserverCommandState) observerCommand;
if (simulatorCommand == SimulatorObserverCommandState.MOVED_ALL_ROBOTOS) {
}
} else if (observerCommand instanceof RoboterModelObserverCommand) {
RoboterModelObserverCommand rmoc = (RoboterModelObserverCommand) observerCommand;
if (rmoc.getState() == RoboterModelCommandState.ROBOTER_ADDED) {
RoboterComponent newRoboterComponent = new RoboterComponent(rmoc.getModel(),
simulator.getRoboterConstructionForRoboter(rmoc.getModel()));
this.roboterComponents.add(newRoboterComponent);
} else if (rmoc.getState() == RoboterModelCommandState.ROBOTER_REMOVED) {
RoboterComponent rc = findOrAddRoboterComponent(rmoc.getModel());
this.roboterComponents.remove(rc);
}
} else if (observerCommand instanceof LightSensorModelObserverCommand) {
LightSensorModelObserverCommand lsmoc = (LightSensorModelObserverCommand) observerCommand;
if (lsmoc.getState() == LightSensorCommandState.SENSOR_INTENSITY_CHANGED) {
RoboterComponent rc = findOrAddRoboterComponent(lsmoc.getModel());
rc.hightLightRoboterElement(lsmoc.getPayLoad());
} else if (lsmoc.getState() == LightSensorCommandState.SENSOR_INTENSITY_REMAINS) {
RoboterComponent rc = findOrAddRoboterComponent(lsmoc.getModel());
rc.unHightLightRoboterElement(lsmoc.getPayLoad());
}
} else if (observerCommand instanceof RoboterMoveListModelObserverCommand) {
RoboterMoveListModelObserverCommand rmlmoc = (RoboterMoveListModelObserverCommand) observerCommand;
if (rmlmoc.getState() == RoboterMoveModelObserverCommandState.ROBOTER_MOVE_ADDED) {
RoboterMoveListComponent rtc = findOrAddRoboterTraceComponent(rmlmoc.getModel());
rtc.setMoves(rmlmoc.getPayLoad());
}
} else if (observerCommand instanceof LightBulbModelObserverCommand) {
LightBulbModelObserverCommand lbmoc = (LightBulbModelObserverCommand) observerCommand;
if (lbmoc.getState() == LightBulbModelCommandState.LIGHTBULB_ADDED) {
this.lightBulbComponents.add(new LightBulbComponent(lbmoc.getModel()));
} else if (lbmoc.getState() == LightBulbModelCommandState.LIGHTBULB_REMOVED) {
LightBulbComponent rc = findOrAddLightBulbComponent(lbmoc.getModel());
this.lightBulbComponents.remove(rc);
}
}
}
private LightBulbComponent findOrAddLightBulbComponent(LightBulb lightBulb) {
Iterator<LightBulbComponent> it = this.lightBulbComponents.iterator();
while (it.hasNext()) {
LightBulbComponent rc = it.next();
if (rc.getModelObject() == lightBulb) {
return rc;
}
}
LightBulbComponent rc = new LightBulbComponent(lightBulb);
this.lightBulbComponents.add(rc);
return rc;
}
private RoboterComponent findOrAddRoboterComponent(Roboter roboter) {
Iterator<RoboterComponent> it = this.roboterComponents.iterator();
while (it.hasNext()) {
RoboterComponent rc = it.next();
if (rc.getModelObject() == roboter) {
return rc;
}
}
RoboterComponent rc = new RoboterComponent(roboter, simulator.getRoboterConstructionForRoboter(roboter));
this.roboterComponents.add(rc);
return rc;
}
private RoboterMoveListComponent findOrAddRoboterTraceComponent(Roboter roboter) {
Iterator<RoboterMoveListComponent> it = this.roboterMoveListComponent.iterator();
while (it.hasNext()) {
RoboterMoveListComponent rtc = it.next();
if (rtc.getRoboter() == roboter) {
return rtc;
}
}
RoboterMoveListComponent rtc = new RoboterMoveListComponent(roboter);
rtc.setColor(findOrAddRoboterComponent(roboter).getColor());
this.roboterMoveListComponent.add(rtc);
return rtc;
}
public void setRenderTracing(boolean renderTracing) {
this.renderTracing = renderTracing;
}
public boolean isRenderTracing() {
return renderTracing;
}
}