package com.kartoflane.superluminal2.mvc.controllers; import java.util.ArrayList; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import com.kartoflane.superluminal2.components.Polygon; import com.kartoflane.superluminal2.components.interfaces.Collidable; import com.kartoflane.superluminal2.components.interfaces.Follower; import com.kartoflane.superluminal2.core.Grid; import com.kartoflane.superluminal2.core.Grid.Snapmodes; import com.kartoflane.superluminal2.core.LayeredPainter.Layers; import com.kartoflane.superluminal2.core.Manager; import com.kartoflane.superluminal2.events.SLEvent; import com.kartoflane.superluminal2.events.SLListener; import com.kartoflane.superluminal2.events.SLModShiftEvent; import com.kartoflane.superluminal2.ftl.ShipObject; import com.kartoflane.superluminal2.mvc.View; import com.kartoflane.superluminal2.mvc.controllers.props.OffsetPropController; import com.kartoflane.superluminal2.mvc.controllers.props.PropController; import com.kartoflane.superluminal2.mvc.models.ObjectModel; import com.kartoflane.superluminal2.mvc.views.ShipView; import com.kartoflane.superluminal2.ui.ShipContainer; import com.kartoflane.superluminal2.ui.sidebar.data.DataComposite; import com.kartoflane.superluminal2.ui.sidebar.data.ShipDataComposite; import com.kartoflane.superluminal2.undo.UndoableMoveEdit; import com.kartoflane.superluminal2.undo.UndoableOffsetEdit; public class ShipController extends ObjectController { public static final String LINE_V_PROP_ID = "LineVertical"; public static final String LINE_H_PROP_ID = "LineHorizontal"; public static final String OFFSET_X_PROP_ID = "OffsetX"; public static final String OFFSET_Y_PROP_ID = "OffsetY"; public static final String OFFSET_FINE_X_PROP_ID = "OffsetHorizontal"; public static final String OFFSET_FINE_Y_PROP_ID = "OffsetVertical"; protected ShipContainer container = null; protected ArrayList<Collidable> collidables = new ArrayList<Collidable>(); private ShipController(ShipContainer container, ObjectModel model, ShipView view) { super(); setModel(model); setView(view); this.container = container; Point gridSize = Grid.getInstance().getSize(); setSelectable(true); setLocModifiable(false); setBounded(true); gridSize = Grid.getInstance().snapToGrid(gridSize, Snapmodes.CROSS); setBoundingArea(0, 0, gridSize.x, gridSize.y); setSnapMode(Snapmodes.CROSS); setPresentedFactor(ShipContainer.CELL_SIZE); setSize(ShipContainer.CELL_SIZE / 2, ShipContainer.CELL_SIZE / 2); createProps(); } public static ShipController newInstance(ShipContainer container, ShipObject object) { ObjectModel model = new ObjectModel(object); ShipView view = new ShipView(); ShipController controller = new ShipController(container, model, view); controller.updateProps(); return controller; } @Override protected void createProps() { OffsetPropController opc = new OffsetPropController(this, OFFSET_FINE_X_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 (isPlayerShip()) { Point p = (Point) e.data; ShipObject ship = getGameObject(); container.setShipFineOffset(p.x - getX(), ship.getVertical()); } } }); opc.setLocModifiable(true); opc.setDefaultBackgroundColor(192, 0, 0); opc.setDefaultBorderColor(0, 0, 0); opc.addToPainter(Layers.SHIP_ORIGIN); opc.setCompositeTitle("Horizontal Offset"); opc.setInheritVisibility(isPlayerShip()); opc.setVisible(isPlayerShip()); addProp(opc); opc = new OffsetPropController(this, OFFSET_FINE_Y_PROP_ID); opc.setPolygon(new Polygon(new int[] { 0, 0, 0, ShipContainer.CELL_SIZE / 2, 2 * ShipContainer.CELL_SIZE / 3, ShipContainer.CELL_SIZE / 4 })); opc.addListener(SLEvent.MOVE, new SLListener() { @Override public void handleEvent(SLEvent e) { Point p = (Point) e.data; ShipObject ship = getGameObject(); container.setShipFineOffset(ship.getHorizontal(), p.y - getY()); } }); opc.setLocModifiable(true); opc.setDefaultBackgroundColor(0, 192, 0); opc.setDefaultBorderColor(0, 0, 0); opc.addToPainter(Layers.SHIP_ORIGIN); opc.setCompositeTitle("Vertical Offset"); addProp(opc); opc = new OffsetPropController(this, OFFSET_X_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) { Point p = (Point) e.data; ShipObject ship = getGameObject(); p.x = (p.x - getX()) / ShipContainer.CELL_SIZE; if (!isSelected() && ship.getXOffset() != p.x) { saveCollidables(); container.setShipOffset(p.x, ship.getYOffset()); restoreCollidables(); } } }); opc.setDefaultBackgroundColor(255, 0, 0); opc.setDefaultBorderColor(0, 0, 0); opc.addToPainter(Layers.SHIP_ORIGIN); opc.setSnapMode(Snapmodes.EDGE_V); opc.setPresentedFactor(ShipContainer.CELL_SIZE); opc.setCompositeTitle("X Offset"); addProp(opc); opc = new OffsetPropController(this, OFFSET_Y_PROP_ID); opc.setPolygon(new Polygon(new int[] { 0, 0, 0, ShipContainer.CELL_SIZE / 2, ShipContainer.CELL_SIZE / 2, ShipContainer.CELL_SIZE / 4 })); opc.addListener(SLEvent.MOVE, new SLListener() { @Override public void handleEvent(SLEvent e) { Point p = (Point) e.data; ShipObject ship = getGameObject(); p.y = (p.y - getY()) / ShipContainer.CELL_SIZE; if (!isSelected() && ship.getYOffset() != p.y) { saveCollidables(); container.setShipOffset(ship.getXOffset(), p.y); restoreCollidables(); } } }); opc.setDefaultBackgroundColor(0, 255, 0); opc.setDefaultBorderColor(0, 0, 0); opc.addToPainter(Layers.SHIP_ORIGIN); opc.setSnapMode(Snapmodes.EDGE_H); opc.setPresentedFactor(ShipContainer.CELL_SIZE); opc.setCompositeTitle("Y Offset"); addProp(opc); PropController prop = new PropController(this, LINE_H_PROP_ID); prop.setInheritVisibility(true); prop.setDefaultBackgroundColor(255, 0, 0); prop.setImage(null); prop.setBorderThickness(1); prop.setAlpha(255); prop.addToPainterBottom(Layers.SHIP_ORIGIN); addProp(prop); prop = new PropController(this, LINE_V_PROP_ID); prop.setInheritVisibility(true); prop.setDefaultBackgroundColor(0, 255, 0); prop.setImage(null); prop.setBorderThickness(1); prop.setAlpha(255); prop.addToPainterBottom(Layers.SHIP_ORIGIN); addProp(prop); } @Override public void select() { super.select(); saveCollidables(); } @Override public void deselect() { super.deselect(); restoreCollidables(); } private void saveCollidables() { for (Follower fol : getFollowers()) { if (fol instanceof Collidable) { Collidable c = (Collidable) fol; if (c.isCollidable()) { c.setCollidable(false); collidables.add(c); } } } } private void restoreCollidables() { for (Collidable c : collidables) c.setCollidable(c instanceof RoomController == false || (c instanceof RoomController == true && !Manager.allowRoomOverlap)); collidables.clear(); } @Override public ShipObject getGameObject() { return (ShipObject) getModel().getGameObject(); } protected ShipView getView() { return (ShipView) view; } @Override public void setView(View view) { super.setView(view); this.view.addToPainter(Layers.SHIP_ORIGIN); } public boolean isPlayerShip() { return getGameObject().isPlayerShip(); } @Override public boolean setLocation(int x, int y) { boolean result = super.setLocation(x, y); updateView(); updateProps(); return result; } public void updateProps() { updateLines(); updateOffsets(); } private void updateLines() { Point gridSize = Grid.getInstance().getSize(); PropController hLine = getProp(LINE_H_PROP_ID); PropController vLine = getProp(LINE_V_PROP_ID); hLine.setSize(gridSize.x, 1); vLine.setSize(1, gridSize.y); hLine.setFollowOffset(hLine.getW() / 2, 0); vLine.setFollowOffset(0, vLine.getH() / 2); hLine.updateFollower(); vLine.updateFollower(); } private void updateOffsets() { int cs = ShipContainer.CELL_SIZE; ShipObject ship = getGameObject(); Point[] bounds = getBoundingPoints(); Point gridSize = Grid.getInstance().getSize(); PropController prop = getProp(OFFSET_X_PROP_ID); prop.setBoundingPoints(getX(), getY() - cs / 2, bounds[1].x + ship.getXOffset() * cs, getY() - cs / 2); prop.setFollowOffset(cs * ship.getXOffset(), -cs / 2); prop.updateFollower(); prop = getProp(OFFSET_Y_PROP_ID); prop.setBoundingPoints(getX() - cs / 2, getY(), getX() - cs / 2, bounds[1].y + ship.getYOffset() * cs); prop.setFollowOffset(-cs / 2, cs * ship.getYOffset()); prop.updateFollower(); prop = getProp(OFFSET_FINE_X_PROP_ID); prop.setBoundingPoints(0, getY() - 2 * cs / 3, gridSize.x, getY() - 2 * cs / 3); prop.setFollowOffset(ship.getHorizontal(), -2 * cs / 3); prop.updateFollower(); prop = getProp(OFFSET_FINE_Y_PROP_ID); prop.setBoundingPoints(getX() - 2 * cs / 3, 0, getX() - 2 * cs / 3, gridSize.y); prop.setFollowOffset(-2 * cs / 3, ship.getVertical()); prop.updateFollower(); } @Override public Point getPresentedLocation() { return new Point(getX() / getPresentedFactor(), getY() / getPresentedFactor()); } @Override public Point getPresentedSize() { throw new UnsupportedOperationException(); } /** * This controller has insufficient information to update its own bounding area (needs data from ShipContainer)<br> * Use {@link ShipContainer#updateBoundingArea()} instead. */ @Override public void updateBoundingArea() { throw new UnsupportedOperationException(); } @Override public DataComposite getDataComposite(Composite parent) { return new ShipDataComposite(parent, this); } @Override public void handleEvent(SLEvent e) { if (e instanceof SLModShiftEvent) { // Finalize any other operations that may be currently in progress undoFinalize(); boolean pressed = (Boolean) e.data; setFollowActive(!pressed); if (pressed) { ShipObject ship = getGameObject(); setBoundingPoints(0, 0, getX() + ship.getXOffset() * ShipContainer.CELL_SIZE, getY() + ship.getYOffset() * ShipContainer.CELL_SIZE); currentEdit = new UndoableOffsetEdit(container); undoInit(); } else { Point offset = container.findShipOffset(); offset.x /= ShipContainer.CELL_SIZE; offset.y /= ShipContainer.CELL_SIZE; container.setShipOffset(offset.x, offset.y); container.updateBoundingArea(); currentEdit = new UndoableMoveEdit(this); undoInit(); } } else { super.handleEvent(e); } } @Override public void mouseDown(MouseEvent e) { super.mouseDown(e); if ((e.stateMask & SWT.SHIFT) == SWT.SHIFT) { currentEdit = new UndoableOffsetEdit(container); undoInit(); } } @Override public void mouseUp(MouseEvent e) { if (isSelected() && (e.stateMask & SWT.SHIFT) == SWT.SHIFT) { Point offset = container.findShipOffset(); offset.x /= ShipContainer.CELL_SIZE; offset.y /= ShipContainer.CELL_SIZE; container.setShipOffset(offset.x, offset.y); updateProps(); } undoFinalize(); super.mouseUp(e); } @Override protected void undoInit() { if (currentEdit instanceof UndoableOffsetEdit) { UndoableOffsetEdit edit = (UndoableOffsetEdit) currentEdit; edit.setOld(getLocation()); } else { super.undoInit(); } } @Override protected void undoFinalize() { if (currentEdit instanceof UndoableOffsetEdit) { UndoableOffsetEdit edit = (UndoableOffsetEdit) currentEdit; edit.setCurrent(getLocation()); } super.undoFinalize(); } }