CreatingShips - MarcelineVQ/endless-sky GitHub Wiki
Because one of the first things I imagine people will want to try out when customizing or expanding Endless Sky is to create a new kind of ship, I've tried to make that as easy as possible. All you need is a single image (which can be hand-drawn or generated by a 3D modeling program) and an entry in a data file. The recommended process for creating new ships is:
-
Using the cheater plugin, create a pilot who has access to all human technology on the same planet. (If necessary, edit the pilot file to add access to other technologies as well.)
-
Create a new plugin for your ship, rather than modifying the base game data.
-
Create a new sprite, using your tool of choice. (I recommend making 3D models in Blender, then adding "grunge" to the ship by post-processing in GIMP.) Save that sprite in the
images/ship/
folder of your plugin. -
Copy an existing ship definition from ships.txt into your pilot file use as a starting point. To start out with, give your ship large capacities so you won't be limited by them while testing.
-
Use the ship builder to set the gun, turret, and engine hardpoints for your ship. Copy those into the pilot file, as well. (You can also use the ship builder tool to check that your sprite's collision mask will be reasonable, e.g. that there are no stray pixels that might mess it up.)
-
In the game, load that pilot and experiment with different sets of outfits until you find one you like. At this stage you can also edit your pilot file to change the ship's attributes.
-
Once your ship seems well-balanced, reduce the capacities to reasonable numbers, and copy the ship definition from your pilot file into a text file in the
data/
folder of your plugin.
Most space games back in the 90s (after which this one is patterned) used a sprite sheet for each ship with pre-rendered images of it at a variety of rotations. This allowed for some cool effects (like realistic shadows that move across the ship as you turn). But, it also meant that turning had a choppy effect: if, for example, your sprite sheet had 36 images in it, your ship could only turn in 10-degree increments.
Endless Sky takes a simpler approach. Each ship is a single sprite, which is then drawn by OpenGL at different rotations. This means that the shadows don't move as you turn. But it also means that the ship's position and rotation can change by fractions of a pixel at a time, which makes all movement look much less choppy.
To keep the sprite sharp at any rotation, all ship sprites are twice as big as they are drawn on the screen:
If you are creating a human ship, it should be mostly shades of grey with some highlights in red, orange, and yellow. Depending on the government, the colors will be "swizzled" to green and yellow for the Free Worlds, blue and cyan for civilians, etc.
To create a new ship sprite, just add a PNG file to the images/ship/ directory. Everything in the images directory is automatically loaded when the game launches; no need to add it to a list anywhere.
For collision detection, for all images in images/ship/ and images/asteroid/, or one of their sub-folders, a polygonal outline is generated when the sprite is loaded. This means that it's important for your ship image to be in that folder, so the game knows that it needs to calculate its outline. The folder must be named "ship", singular; "ships" will not work.
If you want your ship to be animated, just create multiple files with the same name, with "-#.png" appended:
- images/ship/newship-0.png
- images/ship/newship-1.png
(If there are more than 10 frames, the extensions should be "-00.png", "-01.png", etc. You can also create sprites with additive or "half-additive" blending modes by using the extension "+#.png" or "~#.png" respectively, but these blending modes look much better for projectiles or explosions than for ships.)
Making a full texture-mapped 3D model can be a lot of work, and since all the ships in this game are only shown from directly overhead and at relatively low resolution, it can be overkill. I've found that the fastest way to create good-looking ship images is to use a 3D design program (such as Blender) for rendering the shape of the ship and the basic colors of the different parts of it, but then using an image manipulation program (such as GIMP) for some post-processing.
Editing images in GIMP, some easy ways to make the models look more detailed and less "artificial" include:
- Find a public domain metal texture. Paste it into a separate layer, setting the layer's blending mode to "overlay" and adding an alpha channel to the layer if necessary. Erase portions of the texture so that it is only applied to certain parts of the ship. Repeat with other textures until each part of the ship is covered by at least one.
- In a layer set to "multiply" mode, spray-paint with black to add extra shadows to the left side of the image and to other areas as you see fit. It often also looks good to add dark patches near the engine outlets, to make that part of the ship look a bit "burnt."
- Spray-painting dark patches at a few corners or edges can also help make the metal look reflective.
- In the "multiply" layer, spray paint with red over the yellow or orange paint sections to make the color less uniform.
At a bare minimum, you should edit ship images at a large enough resolution to provide an "@2x" version for high-dpi monitors. Be sure to save the final version of your 3D model, too!
For some examples, you can look at data/ships.txt. The easiest thing to do is to take an existing ship and modify it. If you don't want to change the ships.txt file, you can create a file with any name you like, define your ship there, and save it in the data directory. Each ship attributes is defined as a key name (such as "sprite") followed by (usually) one value. If a key or value has spaces in it, you must enclose it in quotation marks or it will be interpreted as two separate keys.
The data files use indentation, like in the Python language, to define sub-entries within an entry. For example, each ship is defined by a line reading ship "Ship Name"
with no indentation, followed by several keys indented once:
-
"sprite"
: specifies which sprite the ship uses, relative to the "images/" folder, minus the frame number and any extension (e.g. "ship/newship" refers to "images/ship/newship-#.png"). -
"attributes"
: a list of characteristics of the ship, defined as key-value pairs. -
"outfits"
: a list of names of outfits that are installed in this ship by default. To add multiple copies of one outfit, add a number after the name:"Energy Blaster" 2
-
"engine"
: the (x, y) coordinates, relative to the center of the sprite, where engine flares should appear. Positive y is up; negative y is down (e.g.engine -12 -105
). There should be a separate "engine" line for each engine (usually two). -
"gun"
: the (x, y) coordinates of any gun ports. The number of gun outfits cannot exceed the number of gun port locations listed here. -
"turret"
: the (x, y) coordinates of any turrets. The number of turret outfits cannot exceed the number of turret locations listed here. -
(fighter | drone) [<x> <y> [over | under | left | right]]
: specify a fighter or drone bay at the given (x, y) coordinates, e.g.drone -14 64 over
. The coordinates can be followed by one of the four keywords given above: "over" or "under" to make the carried fighter visible over or under the ship that is carrying it, and "left" or "right" to make a bay that launches ships to the side instead of straight ahead. It is not currently possible to combine more than one of these keywords, so you cannot make a side-facing bay where the carried ship is drawn. (v. 0.9.0) -
"explode"
: an effect to create when the ship is dying, and the number of them to create (e.g.explode "small explosion" 10
). These effects are created randomly at an increasing rate until the ship finally explodes in one big explosion where 50% of the explosion effects are generated a second time. -
"final explode"
: the same as"explode"
, but defines an effect that is only included in the final ship explosion, not in the small explosions leading up to that. (v. 0.9.0) -
"never disabled"
: If this tag is included (no value need be specified for it), this ship never becomes disabled due to its hull dropping too low. This means that it cannot be plundered. -
"uncapturable"
: If this tag is included (no value need be specified for it), this ship can be boarded but cannot be captured. This can be used to mark things that are not really "ships," e.g. a derelict hulk that you can plunder but that cannot be repaired to fly on its own. (v. 0.9.0)
The "attributes" key should be followed by a list of ship attributes, ideally listed in the following order:
-
"category"
: the type of ship: "Transport", "Light Freighter", "Heavy Freighter", "Interceptor", "Light Warship", "Heavy Warship", "Fighter", or "Drone". -
"cost"
: the cost of the ship, in credits. -
"shields"
: maximum shield capacity. When a ship is hit, if it has any shields left they absorb the damage; otherwise the hull is damaged. Shields can recharge, whereas hulls generally can only be repaired when landing on a planet. -
"hull"
: maximum hull strength. A ship is disabled when either it is reduced to 10% hull, or to the minimum of 25% hull or 100. (That is, small ships are disabled at 25%, and large ones at 10%.) -
"required crew"
: the number of crew members needed to operate the ship without anything failing. -
"automaton"
: if set to 1, this ship is allowed to have a"required crew"
of 0. Ships with the "Drone" category are automatically automata unless"automaton"
is defined and set to 0. (v. 0.9.0) -
"bunks"
: the maximum number of people on the ship, including crew and passengers. -
"mass"
: the mass of the ship's chassis, without any outfits or cargo. The higher the mass, the more thrust is needed in order to turn or accelerate at a certain rate. -
"drag"
: the maximum speed of the ship will be equal to "thrust" / "drag". -
"heat dissipation"
: how well this ship gets rid of excess heat. This should vary from .9 for tiny ships to .5 or less for large ships. For ships of a given size, it should be higher for ships with more exposed hull area or that might be expected to have higher quality construction, and lower for ships that ought to be plagued by overheating issues. -
"fuel capacity"
: the amount of fuel (one jump = 100) that this ship can carry. -
"cargo space"
: the amount of cargo that can be carried. -
"outfit space"
: the amount of outfits (weapons, generators, engines, etc.) that can be installed. -
"weapon capacity"
: the amount of outfit space that can be occupied by weapons. -
"engine capacity"
: the amount of that outfit space which is suitable for installing engines. Some ships have lots of engine capacity but not much weapon capacity, or vice versa.
There is also one special attribute called weapon
that defines how much damage your ship does when it explodes. Suggested values for those attributes are shown in parentheses below; you can make the damage amount less or more depending on whether you want this ship to have a massive explosion (perhaps because it is carrying lots of ordinance) or a tiny one:
-
"blast radius"
(Typical value value: (shields + hull) * .01) -
"shield damage"
(Typical value value: (shields + hull) * .10) -
"hull damage"
(Typical value value: (shields + hull) * .05) -
"hit force"
(Typical value value: (shields + hull) * .15)
Outfits work by adding or subtracting to these same attributes. The outfits list can be in any order, but for the sake of consistency its preferred order is roughly from "front to back" of the ship:
- guns
- turrets
- generators
- batteries
- shields
- cooling
- other systems
- thruster
- steering
- hyperdrive
In addition to about 40 different human ship models, each model has several "variants" that represent the different ways that an individual captain might outfit a ship. For example, one pilot might prefer being fast enough to run from a fight, and another might want to be strong enough to survive one, and fighting styles include bombarding a target with missiles or other longer-range weapons, or being able to deal maximum damage at close range.
The syntax for a variant is:
ship <base name> <variant name>
...
Instead of specifying all the ship's attributes, the variant should only define a list of outfits. The order of the guns and turrets must also be specified if there are two different kinds of gun outfits or turret outfits, so that it is clear which ones go in which slots. For example:
ship "Bastion" "Bastion (Laser)"
outfits
"Heavy Laser" 4
"Heavy Anti-Missile Turret"
"Heavy Laser Turret" 2
"Fusion Reactor"
"Supercapacitor" 4
"D94-YV Shield Generator"
"Water Coolant System"
"Mass Expansion"
"A370 Atomic Thruster"
"A525 Atomic Steering"
"Hyperdrive"
turret "Heavy Anti-Missile Turret"
turret "Heavy Laser Turret"
turret "Heavy Laser Turret"
In this case, all four guns are the same type (heavy lasers), so there is no need to specify their order, but the order of the turrets must be specified. The turret (x, y) positions need not be specified, because they are given in the original ship descriptor. By only providing the bare minimum information in the variant, we can avoid needing to update all the variants if, say, the ship sprite changes and the turret positions move.
A variant can define new weapon hardpoints, but if so the new set of points completely replaces the old ones. That is, if you want your new variant to have two extra guns, you'll need to copy the base ship's list of guns and turrets, and then add two more gun lines. Similarly, if you define any new engine points the old engine points are discarded. And, if you define a new bay location (drone or fighter), all previous "drone" and "fighter" locations are discarded.