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