Added starter components.
This commit is contained in:
		
							parent
							
								
									4873a308ee
								
							
						
					
					
						commit
						300a62e621
					
				| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
.idea/
 | 
			
		||||
target/
 | 
			
		||||
*.iml
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,111 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   width="256"
 | 
			
		||||
   height="256"
 | 
			
		||||
   viewBox="0 0 67.733332 67.733335"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   id="svg5"
 | 
			
		||||
   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
 | 
			
		||||
   sodipodi:docname="icon.svg"
 | 
			
		||||
   inkscape:export-filename="A:\Programming\GitHub-andrewlalis\StarshipArena\design\icon.png"
 | 
			
		||||
   inkscape:export-xdpi="96"
 | 
			
		||||
   inkscape:export-ydpi="96"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg">
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="namedview7"
 | 
			
		||||
     pagecolor="#505050"
 | 
			
		||||
     bordercolor="#ffffff"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     inkscape:pageshadow="0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pagecheckerboard="1"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     units="px"
 | 
			
		||||
     showguides="true"
 | 
			
		||||
     inkscape:guide-bbox="true"
 | 
			
		||||
     inkscape:snap-page="true"
 | 
			
		||||
     inkscape:zoom="2"
 | 
			
		||||
     inkscape:cx="61.5"
 | 
			
		||||
     inkscape:cy="141.75"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1017"
 | 
			
		||||
     inkscape:window-x="-8"
 | 
			
		||||
     inkscape:window-y="-8"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     inkscape:current-layer="layer1">
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       position="33.866667,54.715007"
 | 
			
		||||
       orientation="1,0"
 | 
			
		||||
       id="guide824" />
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       position="2.5765869,33.866667"
 | 
			
		||||
       orientation="0,-1"
 | 
			
		||||
       id="guide826" />
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       position="2.778125,2.5016983e-07"
 | 
			
		||||
       orientation="0,-1"
 | 
			
		||||
       id="guide828" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs2">
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient2822">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#00023d;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop2818" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#8158ac;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop2820" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient2822"
 | 
			
		||||
       id="linearGradient3304"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       x1="33.866665"
 | 
			
		||||
       y1="44.908512"
 | 
			
		||||
       x2="33.866665"
 | 
			
		||||
       y2="0.92604166"
 | 
			
		||||
       gradientTransform="matrix(1.0004146,0,0,0.92621082,-0.01400114,2.308488)" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1">
 | 
			
		||||
    <path
 | 
			
		||||
       style="fill:#ce0900;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
       d="M 37.120775,63.000637 C 37.208969,42.098554 21.012326,39.151829 11.972396,42.569364 l 3.373437,2.645833 c 9.37066,-5.335764 20.407928,7.113912 21.774942,17.78544 z"
 | 
			
		||||
       id="path4745"
 | 
			
		||||
       sodipodi:nodetypes="cccc" />
 | 
			
		||||
    <path
 | 
			
		||||
       style="fill:#05e898;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
       d="M 30.775166,63.000637 C 30.686972,42.098554 46.883615,39.151829 55.923545,42.569364 l -3.373437,2.645833 c -9.37066,-5.335764 -20.407928,7.113912 -21.774942,17.78544 z"
 | 
			
		||||
       id="path8544"
 | 
			
		||||
       sodipodi:nodetypes="cccc" />
 | 
			
		||||
    <path
 | 
			
		||||
       style="fill:url(#linearGradient3304);fill-opacity:1;stroke:#ffb800;stroke-width:1.01874;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
       d="M 12.530819,65.043834 33.866706,3.1661977 55.008802,65.043833 33.866706,40.170249 Z"
 | 
			
		||||
       id="path863"
 | 
			
		||||
       sodipodi:nodetypes="ccccc" />
 | 
			
		||||
    <path
 | 
			
		||||
       style="fill:#ce0900;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
       d="M 11.972396,42.569364 C -2.2722264,48.32423 20.397365,66.043094 41.63193,42.657014 62.866491,19.270926 45.943565,5.5747177 45.943565,5.5747177 51.46268,17.143022 53.619707,20.974556 38.278435,40.494141 25.055936,55.054458 6.5096703,50.64227 15.345833,45.215197 Z"
 | 
			
		||||
       id="path6747"
 | 
			
		||||
       sodipodi:nodetypes="cscccc" />
 | 
			
		||||
    <path
 | 
			
		||||
       style="fill:#05e898;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
       d="M 55.923545,42.569364 C 70.168167,48.32423 47.498576,66.043094 26.264011,42.657014 5.0294498,19.270926 21.952376,5.5747177 21.952376,5.5747177 16.433261,17.143022 14.276234,20.974556 29.617506,40.494141 42.840005,55.054458 61.38627,50.64227 52.550108,45.215197 Z"
 | 
			
		||||
       id="path13334"
 | 
			
		||||
       sodipodi:nodetypes="cscccc" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 4.5 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
 | 
			
		||||
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
    <modelVersion>4.0.0</modelVersion>
 | 
			
		||||
 | 
			
		||||
    <groupId>nl.andrewl</groupId>
 | 
			
		||||
    <artifactId>starship-arena</artifactId>
 | 
			
		||||
    <version>1.0.0-SNAPSHOT</version>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
        <maven.compiler.source>17</maven.compiler.source>
 | 
			
		||||
        <maven.compiler.target>17</maven.compiler.target>
 | 
			
		||||
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.google.code.gson</groupId>
 | 
			
		||||
            <artifactId>gson</artifactId>
 | 
			
		||||
            <version>2.9.0</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
    <build>
 | 
			
		||||
        <plugins>
 | 
			
		||||
            <plugin>
 | 
			
		||||
                <artifactId>maven-assembly-plugin</artifactId>
 | 
			
		||||
                <configuration>
 | 
			
		||||
                    <archive>
 | 
			
		||||
                        <manifest>
 | 
			
		||||
                            <mainClass>nl.andrewl.starship_arena.StarshipArena</mainClass>
 | 
			
		||||
                        </manifest>
 | 
			
		||||
                    </archive>
 | 
			
		||||
                    <descriptorRefs>
 | 
			
		||||
                        <descriptorRef>jar-with-dependencies</descriptorRef>
 | 
			
		||||
                    </descriptorRefs>
 | 
			
		||||
                </configuration>
 | 
			
		||||
                <executions>
 | 
			
		||||
                    <execution>
 | 
			
		||||
                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
 | 
			
		||||
                        <phase>package</phase> <!-- bind to the packaging phase -->
 | 
			
		||||
                        <goals>
 | 
			
		||||
                            <goal>single</goal>
 | 
			
		||||
                        </goals>
 | 
			
		||||
                    </execution>
 | 
			
		||||
                </executions>
 | 
			
		||||
            </plugin>
 | 
			
		||||
        </plugins>
 | 
			
		||||
    </build>
 | 
			
		||||
</project>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
package nl.andrewl.starship_arena;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.starship_arena.model.Arena;
 | 
			
		||||
import nl.andrewl.starship_arena.model.Ship;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ShipModel;
 | 
			
		||||
import nl.andrewl.starship_arena.util.ResourceUtils;
 | 
			
		||||
import nl.andrewl.starship_arena.view.ArenaWindow;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The main executable class which starts the program.
 | 
			
		||||
 */
 | 
			
		||||
public class StarshipArena {
 | 
			
		||||
	public static void main(String[] args) {
 | 
			
		||||
		ShipModel corvette = ShipModel.load(ResourceUtils.getString("/ships/corvette.json"));
 | 
			
		||||
		Ship s = new Ship(corvette);
 | 
			
		||||
		Arena arena = new Arena();
 | 
			
		||||
		arena.getShips().add(s);
 | 
			
		||||
		var window = new ArenaWindow(arena);
 | 
			
		||||
		window.setVisible(true);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents the top-level model containing all objects in an arena that can
 | 
			
		||||
 * interact with each other.
 | 
			
		||||
 */
 | 
			
		||||
public class Arena {
 | 
			
		||||
	private final Collection<Ship> ships = new ArrayList<>();
 | 
			
		||||
	private final Camera camera = new Camera();
 | 
			
		||||
 | 
			
		||||
	public Collection<Ship> getShips() {
 | 
			
		||||
		return ships;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Camera getCamera() {
 | 
			
		||||
		return camera;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model;
 | 
			
		||||
 | 
			
		||||
import java.awt.geom.Point2D;
 | 
			
		||||
 | 
			
		||||
public class Camera {
 | 
			
		||||
	public static final double SCALE_INTERVAL = 50.0;
 | 
			
		||||
 | 
			
		||||
	private Object focus;
 | 
			
		||||
 | 
			
		||||
	private Point2D.Float position = new Point2D.Float();
 | 
			
		||||
 | 
			
		||||
	private int scaleIncrement = 1;
 | 
			
		||||
 | 
			
		||||
	public Object getFocus() {
 | 
			
		||||
		return focus;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setFocus(Object focus) {
 | 
			
		||||
		this.focus = focus;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Point2D.Float getPosition() {
 | 
			
		||||
		return position;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setPosition(Point2D.Float position) {
 | 
			
		||||
		this.position = position;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public int getScaleIncrement() {
 | 
			
		||||
		return scaleIncrement;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setScaleIncrement(int scaleIncrement) {
 | 
			
		||||
		this.scaleIncrement = scaleIncrement;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model;
 | 
			
		||||
 | 
			
		||||
import java.awt.geom.Point2D;
 | 
			
		||||
 | 
			
		||||
public class PhysicsObject {
 | 
			
		||||
	/**
 | 
			
		||||
	 * The position of this object in the scene, in meters from the origin.
 | 
			
		||||
	 * Positive x-axis goes to the right, and positive y-axis goes down.
 | 
			
		||||
	 */
 | 
			
		||||
	private final Point2D.Float position = new Point2D.Float();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * The object's rotation in radians, from 0 to 2 PI.
 | 
			
		||||
	 */
 | 
			
		||||
	private float rotation;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * The object's velocity, in meters per second.
 | 
			
		||||
	 */
 | 
			
		||||
	private final Point2D.Float velocity = new Point2D.Float();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * The object's rotational speed, in radians per second.
 | 
			
		||||
	 */
 | 
			
		||||
	private float rotationSpeed;
 | 
			
		||||
 | 
			
		||||
	public Point2D.Float getPosition() {
 | 
			
		||||
		return position;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public float getRotation() {
 | 
			
		||||
		return rotation;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRotation(float rotation) {
 | 
			
		||||
		while (rotation < 0) rotation += 2 * Math.PI;
 | 
			
		||||
		while (rotation > 2 * Math.PI) rotation -= 2 * Math.PI;
 | 
			
		||||
		this.rotation = rotation;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Point2D.Float getVelocity() {
 | 
			
		||||
		return velocity;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public float getRotationSpeed() {
 | 
			
		||||
		return rotationSpeed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRotationSpeed(float rotationSpeed) {
 | 
			
		||||
		this.rotationSpeed = rotationSpeed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void update(double delta) {
 | 
			
		||||
		position.x += velocity.x * delta;
 | 
			
		||||
		position.y += velocity.y * delta;
 | 
			
		||||
		setRotation((float) (rotation + rotationSpeed * delta));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.Gun;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.Panel;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.ShipComponent;
 | 
			
		||||
import nl.andrewl.starship_arena.util.ResourceUtils;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
 | 
			
		||||
public class Ship extends PhysicsObject {
 | 
			
		||||
	private final String modelName;
 | 
			
		||||
	private final Collection<ShipComponent> components;
 | 
			
		||||
 | 
			
		||||
	private final Collection<Panel> panels;
 | 
			
		||||
	private final Collection<Gun> guns;
 | 
			
		||||
 | 
			
		||||
	private Color primaryColor = Color.GRAY;
 | 
			
		||||
 | 
			
		||||
	public Ship(ShipModel model) {
 | 
			
		||||
		this.modelName = model.getName();
 | 
			
		||||
		this.components = model.getComponents();
 | 
			
		||||
		this.panels = new ArrayList<>();
 | 
			
		||||
		this.guns = new ArrayList<>();
 | 
			
		||||
		for (var c : components) {
 | 
			
		||||
			c.setShip(this);
 | 
			
		||||
			if (c instanceof Panel p) panels.add(p);
 | 
			
		||||
			if (c instanceof Gun g) guns.add(g);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Ship(String modelResource) {
 | 
			
		||||
		this(ShipModel.load(ResourceUtils.getString(modelResource)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getModelName() {
 | 
			
		||||
		return modelName;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Collection<ShipComponent> getComponents() {
 | 
			
		||||
		return components;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Color getPrimaryColor() {
 | 
			
		||||
		return primaryColor;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public float getMass() {
 | 
			
		||||
		float m = 0;
 | 
			
		||||
		for (var c : components) {
 | 
			
		||||
			m += c.getMass();
 | 
			
		||||
		}
 | 
			
		||||
		return m;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import com.google.gson.GsonBuilder;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.ComponentDeserializer;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.GeometricComponent;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.Gun;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.ShipComponent;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
 | 
			
		||||
public class ShipModel {
 | 
			
		||||
	private String name;
 | 
			
		||||
	private Collection<ShipComponent> components;
 | 
			
		||||
 | 
			
		||||
	public String getName() {
 | 
			
		||||
		return name;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Collection<ShipComponent> getComponents() {
 | 
			
		||||
		return components;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Normalizes the geometric properties of the components such that the ship
 | 
			
		||||
	 * model's components are centered around (0, 0).
 | 
			
		||||
	 * TODO: Consider scaling?
 | 
			
		||||
	 */
 | 
			
		||||
	private void normalizeComponents() {
 | 
			
		||||
		float minX = Float.MAX_VALUE;
 | 
			
		||||
		float maxX = Float.MIN_VALUE;
 | 
			
		||||
		float minY = Float.MAX_VALUE;
 | 
			
		||||
		float maxY = Float.MIN_VALUE;
 | 
			
		||||
		for (var c : components) {
 | 
			
		||||
			if (c instanceof GeometricComponent g) {
 | 
			
		||||
				for (var p : g.getPoints()) {
 | 
			
		||||
					minX = Math.min(minX, p.x);
 | 
			
		||||
					maxX = Math.max(maxX, p.x);
 | 
			
		||||
					minY = Math.min(minY, p.y);
 | 
			
		||||
					maxY = Math.max(maxY, p.y);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		final float width = maxX - minX;
 | 
			
		||||
		final float height = maxY - minY;
 | 
			
		||||
		final float offsetX = -minX - width / 2;
 | 
			
		||||
		final float offsetY = -minY - height / 2;
 | 
			
		||||
		// Shift all components to the top-left.
 | 
			
		||||
		for (var c : components) {
 | 
			
		||||
			if (c instanceof GeometricComponent g) {
 | 
			
		||||
				for (var p : g.getPoints()) {
 | 
			
		||||
					p.x += offsetX;
 | 
			
		||||
					p.y += offsetY;
 | 
			
		||||
				}
 | 
			
		||||
			} else if (c instanceof Gun g) {
 | 
			
		||||
				g.getLocation().x += offsetX;
 | 
			
		||||
				g.getLocation().y += offsetY;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static ShipModel load(String json) {
 | 
			
		||||
		Gson gson = new GsonBuilder()
 | 
			
		||||
				.registerTypeAdapter(ShipComponent.class, new ComponentDeserializer())
 | 
			
		||||
				.create();
 | 
			
		||||
		ShipModel model = gson.fromJson(json, ShipModel.class);
 | 
			
		||||
		model.normalizeComponents();
 | 
			
		||||
		return model;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model.ship;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A cockpit represents the control point of the ship.
 | 
			
		||||
 */
 | 
			
		||||
public class Cockpit extends GeometricComponent {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model.ship;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.*;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Type;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Custom deserializer that's used to deserialize components based on their
 | 
			
		||||
 * "type" property.
 | 
			
		||||
 */
 | 
			
		||||
public class ComponentDeserializer implements JsonDeserializer<ShipComponent> {
 | 
			
		||||
	@Override
 | 
			
		||||
	public ShipComponent deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext ctx) throws JsonParseException {
 | 
			
		||||
		JsonObject obj = jsonElement.getAsJsonObject();
 | 
			
		||||
		String componentTypeName = obj.get("type").getAsString();
 | 
			
		||||
		Type componentType = switch (componentTypeName) {
 | 
			
		||||
			case "panel" -> Panel.class;
 | 
			
		||||
			case "cockpit" -> Cockpit.class;
 | 
			
		||||
			case "gun" -> Gun.class;
 | 
			
		||||
			default -> throw new JsonParseException("Invalid ship component type: " + componentTypeName);
 | 
			
		||||
		};
 | 
			
		||||
		return ctx.deserialize(obj, componentType);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model.ship;
 | 
			
		||||
 | 
			
		||||
import java.awt.geom.Point2D;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents a component of a ship that can be drawn as a geometric primitive.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class GeometricComponent extends ShipComponent {
 | 
			
		||||
	private List<Point2D.Float> points;
 | 
			
		||||
 | 
			
		||||
	public List<Point2D.Float> getPoints() {
 | 
			
		||||
		return points;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model.ship;
 | 
			
		||||
 | 
			
		||||
import java.awt.geom.Point2D;
 | 
			
		||||
 | 
			
		||||
public class Gun extends ShipComponent {
 | 
			
		||||
	private String name;
 | 
			
		||||
	private Point2D.Float location;
 | 
			
		||||
 | 
			
		||||
	public String getName() {
 | 
			
		||||
		return name;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Point2D.Float getLocation() {
 | 
			
		||||
		return location;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model.ship;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A simple structural panel that makes up all or part of a ship's body.
 | 
			
		||||
 */
 | 
			
		||||
public class Panel extends GeometricComponent {
 | 
			
		||||
	private String name;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
package nl.andrewl.starship_arena.model.ship;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.starship_arena.model.Ship;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents the top-level component information for any part of a ship.
 | 
			
		||||
 */
 | 
			
		||||
public class ShipComponent {
 | 
			
		||||
	/**
 | 
			
		||||
	 * The ship that this component belongs to.
 | 
			
		||||
	 */
 | 
			
		||||
	private transient Ship ship;
 | 
			
		||||
 | 
			
		||||
	private float mass;
 | 
			
		||||
 | 
			
		||||
	public Ship getShip() {
 | 
			
		||||
		return ship;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setShip(Ship ship) {
 | 
			
		||||
		this.ship = ship;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public float getMass() {
 | 
			
		||||
		return mass;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
package nl.andrewl.starship_arena.util;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.UncheckedIOException;
 | 
			
		||||
 | 
			
		||||
public class ResourceUtils {
 | 
			
		||||
	public static InputStream get(String name) {
 | 
			
		||||
		InputStream in = ResourceUtils.class.getResourceAsStream(name);
 | 
			
		||||
		if (in == null) throw new UncheckedIOException(new IOException("Could not load resource: " + name));
 | 
			
		||||
		return in;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static String getString(String name) {
 | 
			
		||||
		try (var in = get(name)) {
 | 
			
		||||
			return new String(in.readAllBytes());
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			throw new UncheckedIOException(e);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
package nl.andrewl.starship_arena.view;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.starship_arena.model.Arena;
 | 
			
		||||
import nl.andrewl.starship_arena.model.Camera;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.geom.AffineTransform;
 | 
			
		||||
 | 
			
		||||
public class ArenaPanel extends JPanel {
 | 
			
		||||
	private final Arena arena;
 | 
			
		||||
 | 
			
		||||
	private final ShipRenderer shipRenderer = new ShipRenderer();
 | 
			
		||||
 | 
			
		||||
	public ArenaPanel(Arena arena) {
 | 
			
		||||
		this.arena = arena;
 | 
			
		||||
		this.addMouseWheelListener(e -> {
 | 
			
		||||
			arena.getCamera().setScaleIncrement(arena.getCamera().getScaleIncrement() + e.getWheelRotation());
 | 
			
		||||
			repaint();
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void paintComponent(Graphics g) {
 | 
			
		||||
		super.paintComponent(g);
 | 
			
		||||
		Graphics2D g2 = (Graphics2D) g;
 | 
			
		||||
		g2.setColor(Color.BLACK);
 | 
			
		||||
		g2.fillRect(0, 0, getWidth(), getHeight());
 | 
			
		||||
 | 
			
		||||
		AffineTransform originalTx = g2.getTransform();
 | 
			
		||||
		AffineTransform tx = new AffineTransform();
 | 
			
		||||
 | 
			
		||||
		Camera cam = arena.getCamera();
 | 
			
		||||
 | 
			
		||||
		double translateX = (double) getWidth() / 2;
 | 
			
		||||
		double translateY = (double) getHeight() / 2;
 | 
			
		||||
		if (cam.getFocus() == null) {
 | 
			
		||||
			translateX += cam.getPosition().x;
 | 
			
		||||
			translateY += cam.getPosition().y;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		double scale = 1 * Camera.SCALE_INTERVAL;
 | 
			
		||||
		if (cam.getScaleIncrement() > 0) {
 | 
			
		||||
			scale = cam.getScaleIncrement() * Camera.SCALE_INTERVAL;
 | 
			
		||||
		} else if (cam.getScaleIncrement() < 0) {
 | 
			
		||||
			scale = 1.0 / Math.abs(cam.getScaleIncrement() * Camera.SCALE_INTERVAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tx.translate(translateX, translateY);
 | 
			
		||||
		tx.scale(scale, scale);
 | 
			
		||||
		g2.setTransform(tx);
 | 
			
		||||
 | 
			
		||||
		for (var s : arena.getShips()) {
 | 
			
		||||
			shipRenderer.render(s, g2);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		g2.setTransform(originalTx);
 | 
			
		||||
 | 
			
		||||
		g2.setColor(Color.GREEN);
 | 
			
		||||
		g2.fillRect(0, 0, 20, 20);
 | 
			
		||||
		g2.setColor(Color.BLUE);
 | 
			
		||||
		g2.fillRect(getWidth() - 20, getHeight() - 20, 20, 20);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
package nl.andrewl.starship_arena.view;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.starship_arena.model.Arena;
 | 
			
		||||
import nl.andrewl.starship_arena.util.ResourceUtils;
 | 
			
		||||
 | 
			
		||||
import javax.imageio.ImageIO;
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
public class ArenaWindow extends JFrame {
 | 
			
		||||
	private final ArenaPanel arenaPanel;
 | 
			
		||||
 | 
			
		||||
	public ArenaWindow(Arena arena) {
 | 
			
		||||
		super("Starship Arena");
 | 
			
		||||
		setDefaultCloseOperation(EXIT_ON_CLOSE);
 | 
			
		||||
		setUndecorated(true);
 | 
			
		||||
		try {
 | 
			
		||||
			GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
 | 
			
		||||
			device.setFullScreenWindow(this);
 | 
			
		||||
			setPreferredSize(new Dimension(device.getDisplayMode().getWidth(), device.getDisplayMode().getHeight()));
 | 
			
		||||
		} catch (HeadlessException e) {
 | 
			
		||||
			System.err.println("Cannot start the program on systems without a screen.");
 | 
			
		||||
			System.exit(1);
 | 
			
		||||
		}
 | 
			
		||||
		try {
 | 
			
		||||
			InputStream in = ResourceUtils.get("/img/icon.png");
 | 
			
		||||
			setIconImage(ImageIO.read(in));
 | 
			
		||||
			in.close();
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		arenaPanel = new ArenaPanel(arena);
 | 
			
		||||
		add(arenaPanel);
 | 
			
		||||
		pack();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
package nl.andrewl.starship_arena.view;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
 | 
			
		||||
public interface Renderer<T> {
 | 
			
		||||
	void render(T obj, Graphics2D g);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
package nl.andrewl.starship_arena.view;
 | 
			
		||||
 | 
			
		||||
import nl.andrewl.starship_arena.model.Ship;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.Cockpit;
 | 
			
		||||
import nl.andrewl.starship_arena.model.ship.GeometricComponent;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.geom.Path2D;
 | 
			
		||||
 | 
			
		||||
public class ShipRenderer implements Renderer<Ship> {
 | 
			
		||||
	@Override
 | 
			
		||||
	public void render(Ship ship, Graphics2D g) {
 | 
			
		||||
		for (var c : ship.getComponents()) {
 | 
			
		||||
			if (c instanceof GeometricComponent geo) {
 | 
			
		||||
				Path2D.Float path = new Path2D.Float();
 | 
			
		||||
				var first = geo.getPoints().get(0);
 | 
			
		||||
				path.moveTo(first.x, first.y);
 | 
			
		||||
				for (int i = 0; i < geo.getPoints().size(); i++) {
 | 
			
		||||
					var point = geo.getPoints().get(i);
 | 
			
		||||
					path.lineTo(point.x, point.y);
 | 
			
		||||
				}
 | 
			
		||||
				if (geo instanceof Cockpit) {
 | 
			
		||||
					g.setColor(new Color(0f, 0f, 0.5f, 0.5f));
 | 
			
		||||
				} else {
 | 
			
		||||
					g.setColor(ship.getPrimaryColor());
 | 
			
		||||
				}
 | 
			
		||||
				g.fill(path);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 17 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "Corvette",
 | 
			
		||||
  "components": [
 | 
			
		||||
    {
 | 
			
		||||
      "type": "panel",
 | 
			
		||||
      "name": "Main Fuselage",
 | 
			
		||||
      "mass": 5000,
 | 
			
		||||
      "points": [
 | 
			
		||||
        {"x": 0.3, "y": 0.6},
 | 
			
		||||
        {"x": 0.2, "y": 0.1},
 | 
			
		||||
        {"x": 0.1, "y": 0.5},
 | 
			
		||||
        {"x": 0.2, "y": 0.8},
 | 
			
		||||
        {"x": 0.8, "y": 0.8},
 | 
			
		||||
        {"x": 0.9, "y": 0.5},
 | 
			
		||||
        {"x": 0.8, "y": 0.1},
 | 
			
		||||
        {"x": 0.7, "y": 0.6}
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "type": "panel",
 | 
			
		||||
      "name": "Front Cargo Bay",
 | 
			
		||||
      "mass": 1000,
 | 
			
		||||
      "points": [
 | 
			
		||||
        {"x": 0.4, "y": 0.2},
 | 
			
		||||
        {"x": 0.35, "y": 0.6},
 | 
			
		||||
        {"x": 0.65, "y": 0.6},
 | 
			
		||||
        {"x": 0.6, "y": 0.2}
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "type": "cockpit",
 | 
			
		||||
      "mass": 800,
 | 
			
		||||
      "points": [
 | 
			
		||||
        {"x": 0.5, "y": 0.0},
 | 
			
		||||
        {"x": 0.4, "y": 0.2},
 | 
			
		||||
        {"x": 0.6, "y": 0.2}
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "type": "gun",
 | 
			
		||||
      "name": "Port-Side Machine Gun",
 | 
			
		||||
      "mass": 500,
 | 
			
		||||
      "location": {"x": 0.15, "y": 0.4}
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "type": "gun",
 | 
			
		||||
      "name": "Starboard-Side Machine Gun",
 | 
			
		||||
      "mass": 500,
 | 
			
		||||
      "location": {"x": 0.85, "y": 0.4}
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue