package shipmaker; import java.awt.Graphics2D; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import physics.XYTSource; import render.RenderNode; import render.RenderPreferences; import render.XYTRenderNode; import shipmaker.catalog.PartCatalog; import ships.Ship; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.annotations.Expose; import dcpu.WorldPauseHandler; import env.Entity; import equipment.Structure; import equipment.StructureNode; public class EditorShip { private static final float RI_DISTANCE_DIVIDER = 10f; private static class BPLPreview extends XYTRenderNode implements XYTSource { private BlueprintLocation bpl; private CatalogPartType type; public BPLPreview(BlueprintLocation bpl, CatalogPartType type) { super(null); this.bpl = bpl; this.type = type; src = this; } public float position_x() { return (float) (bpl.x + Math.cos(bpl.t1) * bpl.r); } public float position_y() { return (float) (bpl.y + Math.sin(bpl.t1) * bpl.r); } public float alignment_theta() { return bpl.t1 + bpl.t2; } public void draw(Graphics2D g, RenderPreferences prefs) { type.preview(g, prefs); } } public static class EditorShipPart implements Entity { public EditorShipPart(CatalogPart part, BlueprintLocation location) { super(); this.part = part; this.location = location; this.visuals = new BPLPreview(location, part.type()); } private BPLPreview visuals; @Expose public CatalogPart part; @Expose public BlueprintLocation location; public void tickInternals(int msPerTick, WorldPauseHandler handler) { } public void tickPhysics(int msPerTick, WorldPauseHandler handler) { } public RenderNode getVisuals() { return visuals; } } public static interface ShipWatcher { public void partAdded(EditorShipPart p); public void partRemoved(EditorShipPart p); } @Expose private ArrayList<EditorShipPart> parts; @Expose private HashSet<StructureNode> structParts; private ArrayList<ShipWatcher> watchers; public boolean editingStructure = false; public EditorShip() { parts = new ArrayList<EditorShip.EditorShipPart>(); structParts = new HashSet<StructureNode>(); watchers = new ArrayList<ShipWatcher>(); } public EditorShipPart addPart(CatalogPartType type) { BlueprintLocation bpl = new BlueprintLocation(); CatalogPart part = type.create(bpl); EditorShipPart ret = new EditorShipPart(part, bpl); parts.add(ret); for (ShipWatcher w : watchers) { w.partAdded(ret); } return ret; } public float massCenterX() { float massX = 0f, massTotal = 0f; for (EditorShipPart pt : parts) { massX += pt.part.type().mass() * pt.location.effectiveX(); massTotal += pt.part.type().mass(); } return massX / massTotal; } public float massCenterY() { float massY = 0f, massTotal = 0f; for (EditorShipPart pt : parts) { massY += pt.part.type().mass() * pt.location.effectiveY(); massTotal += pt.part.type().mass(); } return massY / massTotal; } public float massTotal() { float total = 0f; for (EditorShipPart pt : parts) { total += pt.part.type().mass(); } return total; } public float riTotal() { float massX = 0f, massY = 0f, massTotal = 0f; for (EditorShipPart pt : parts) { massX += pt.part.type().mass() * pt.location.effectiveX(); massY += pt.part.type().mass() * pt.location.effectiveY(); massTotal += pt.part.type().mass(); } float massCenterX = massX / massTotal; float massCenterY = massY / massTotal; float ri = 0; for (EditorShipPart pt : parts) { if (pt.part.type().placeable()) { ri += pt.part.type().rotationalInertia(); float dx = massCenterX - pt.location.effectiveX(); float dy = massCenterY - pt.location.effectiveY(); dx /= RI_DISTANCE_DIVIDER; dy /= RI_DISTANCE_DIVIDER; ri += pt.part.type().mass() * (dy * dy + dx * dx); } } return ri; } public Ship makeShip() { float massX = 0f, massY = 0f, massTotal = 0f; for (EditorShipPart pt : parts) { massX += pt.part.type().mass() * pt.location.effectiveX(); massY += pt.part.type().mass() * pt.location.effectiveY(); massTotal += pt.part.type().mass(); } float massCenterX = massX / massTotal; float massCenterY = massY / massTotal; float ri = 0; for (EditorShipPart pt : parts) { ri += pt.part.type().rotationalInertia(); float dx = massCenterX - pt.location.effectiveX(); float dy = massCenterY - pt.location.effectiveY(); dx /= RI_DISTANCE_DIVIDER; dy /= RI_DISTANCE_DIVIDER; ri += pt.part.type().mass() * (dy * dy + dx * dx); } Ship s = new Ship(massTotal, ri); for (EditorShipPart pt : parts) { pt.part.applyToShip(pt.location, s, massCenterX, massCenterY); } Structure st = new Structure(); for (StructureNode sn : structParts) { st.addLocation(sn.x, sn.y); } s.addEquipment(st); return s; } public void removePart(EditorShipPart e) { parts.remove(e); for (ShipWatcher w : watchers) { w.partRemoved(e); } } public Collection<EditorShipPart> parts() { return Collections.unmodifiableList(parts); } public Collection<StructureNode> structLocations() { return structParts; } public void addWatcher(ShipWatcher w) { watchers.add(w); } public void removeWatcher(ShipWatcher w) { watchers.remove(w); } public static EditorShip fromJson(String json) { EditorShip ship; GsonBuilder gb = new GsonBuilder(); gb.registerTypeAdapter(EditorShipPart.class, new JsonDeserializer<EditorShipPart>() { public EditorShipPart deserialize(JsonElement obj, Type type, JsonDeserializationContext ctx) throws JsonParseException { BlueprintLocation bpl = ctx.deserialize(obj.getAsJsonObject().get("location"), BlueprintLocation.class); System.out.println(obj.toString()); String types = obj.getAsJsonObject().get("part").getAsJsonObject().get("type").getAsJsonObject().get("name").getAsString(); try { CatalogPartType tp = PartCatalog.getTypeByName(types); CatalogPart part = tp.create(bpl); part.loadOptions(obj.getAsJsonObject().get("part").getAsJsonObject()); return new EditorShipPart(part, bpl); } catch (Throwable e) { e.printStackTrace(); } return null; } }); ship = gb.create().fromJson(json, EditorShip.class); return ship; } public boolean validateStructureContinuity() { if (structParts.isEmpty()) return false; HashSet<StructureNode> visited = new HashSet<>(); LinkedList<StructureNode> queue = new LinkedList<>(); queue.add(structParts.iterator().next()); while (!queue.isEmpty()) { StructureNode node = queue.poll(); if (!visited.contains(node)) for (int dx=-1;dx<=1;dx++) for (int dy=-1;dy<=1;dy++) if (dx == 0 ^ dy==0) { StructureNode newNode = new StructureNode(node.x+dx, node.y+dy); if (structParts.contains(newNode)) { queue.add(newNode); } } visited.add(node); } return visited.size() == structParts.size(); } }