diff --git a/Jenkinsfile b/Jenkinsfile
index f1defaf60..393ce7e0c 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,6 +1,9 @@
pipeline {
agent {
- label 'android'
+ kubernetes {
+ label 'android'
+ defaultContainer 'builder' // Use actual container with Android SDK present
+ }
}
stages {
stage('Setup') {
@@ -33,33 +36,42 @@ pipeline {
}
stage('Build Android') {
steps {
- sh 'echo sdk.dir=/opt/android-sdk > local.properties'
- dir('android') {
- script {
- // Allow varying from the default Android repo path for easier development. Assume same Android branch as engine branch.
- def androidGitPath = "https://github.com/MovingBlocks/DestSolAndroid.git"
- if (env.PUBLISH_ORG) {
- androidGitPath = androidGitPath.replace("MovingBlocks", env.PUBLISH_ORG)
- println "Updated target Android Git path to: " + androidGitPath
- } else {
- println "Not varying the Android path from default " + androidGitPath
- }
- // Figure out a suitable target branch in the Android repo, default is the develop branch
- def androidBranch = "develop"
- // Check to see if Jenkins is building a tag, branch, or other (including PRs)
- if (env.TAG_NAME != null && env.TAG_NAME ==~ /v\d+\.\d+\.\d+.*/) {
- println "Going to use target Android tag " + env.TAG_NAME
- androidBranch = "refs/tags/" + env.TAG_NAME
- } else if (env.BRANCH_NAME.equalsIgnoreCase("master") || env.BRANCH_NAME.startsWith("android/")) {
- println "Going to use target unusual Android branch " + env.BRANCH_NAME
- androidBranch = env.BRANCH_NAME
- } else {
- println "Going to use target Android branch 'develop' - not building 'master' nor anything starting with 'android/'"
+ // Set the ANDROID_HOME environment variable
+ withEnv(['ANDROID_HOME=/opt/android-sdk']) {
+
+ writeFile file: 'local.properties', text: 'sdk.dir=/opt/android-sdk'
+
+ sh 'echo "ANDROID_HOME is: $ANDROID_HOME"'
+ sh 'pwd'
+ sh 'ls -la'
+ sh 'cat local.properties'
+ dir('android') {
+ script {
+ // Allow varying from the default Android repo path for easier development. Assume same Android branch as engine branch.
+ def androidGitPath = "https://github.com/MovingBlocks/DestSolAndroid.git"
+ if (env.PUBLISH_ORG) {
+ androidGitPath = androidGitPath.replace("MovingBlocks", env.PUBLISH_ORG)
+ println "Updated target Android Git path to: " + androidGitPath
+ } else {
+ println "Not varying the Android path from default " + androidGitPath
+ }
+ // Figure out a suitable target branch in the Android repo, default is the develop branch
+ def androidBranch = "develop"
+ // Check to see if Jenkins is building a tag, branch, or other (including PRs)
+ if (env.TAG_NAME != null && env.TAG_NAME ==~ /v\d+\.\d+\.\d+.*/) {
+ println "Going to use target Android tag " + env.TAG_NAME
+ androidBranch = "refs/tags/" + env.TAG_NAME
+ } else if (env.BRANCH_NAME.equalsIgnoreCase("master") || env.BRANCH_NAME.startsWith("android/")) {
+ println "Going to use target unusual Android branch " + env.BRANCH_NAME
+ androidBranch = env.BRANCH_NAME
+ } else {
+ println "Going to use target Android branch 'develop' - not building 'master' nor anything starting with 'android/'"
+ }
+ checkout scm: [$class: 'GitSCM', branches: [[name: androidBranch]], extensions: [], userRemoteConfigs: [[credentialsId: 'GooeyHub', url: androidGitPath]]]
}
- checkout scm: [$class: 'GitSCM', branches: [[name: androidBranch]], extensions: [], userRemoteConfigs: [[credentialsId: 'GooeyHub', url: androidGitPath]]]
}
+ sh './gradlew :android:assembleDebug'
}
- sh './gradlew :android:assembleDebug'
archiveArtifacts 'android/build/outputs/apk/debug/android-debug.apk'
}
}
diff --git a/README.md b/README.md
index fe3521928..cac93b6a2 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,10 @@
-

+
+
+
+
+
+
+
[](https://gitpod.io/#https://github.com/MovingBlocks/DestinationSol)
[](http://discord.gg/Terasology)
@@ -60,7 +66,7 @@ Note: You can select either pure keyboard, keyboard + mouse, or controller (in t
*With inventory up*
* [Left, Right] - change page
-* [Up, Down] - scroll up and down
+* [Page Up, Page Down] - scroll up and down
* [Space] - equip / unequip item *OR* buy / sell if talking to a station
* [D] - discard selected item
@@ -68,13 +74,13 @@ Note: You can select either pure keyboard, keyboard + mouse, or controller (in t
Building and running from source
--------
-You only need Java 8 installed to run Destination Sol from source.
+You only need Java 11 installed to run Destination Sol from source.
Run any commands in the project root directory (where you cloned / extracted the project to, using a command prompt / terminal).
* Download / clone the [source from GitHub](https://github.com/MovingBlocks/DestinationSol)
* To run from the command line: `gradlew run` (on Linux you might need to use `./gradlew run`)
-* To prepare for IntelliJ run: `gradlew idea` then load the generated project via `DestinationSol.ipr`
+* IntelliJ should import the project automatically when you open the project directory
* Distributions (Windows, Linux, Mac) can be created locally by running: `gradlew distZipBundleJREs`
For Android a little extra setup is needed. See instructions [here](https://github.com/MovingBlocks/DestSolAndroid).
diff --git a/build-logic/src/main/groovy/destination-sol-constants.gradle b/build-logic/src/main/groovy/destination-sol-constants.gradle
index 2e78bd425..bfb515634 100644
--- a/build-logic/src/main/groovy/destination-sol-constants.gradle
+++ b/build-logic/src/main/groovy/destination-sol-constants.gradle
@@ -3,9 +3,9 @@ ext {
engineVersion = '2.1.0'
gestaltVersion = '8.0.0-SNAPSHOT'
- gdxVersion = '1.9.14'
+ gdxVersion = '1.12.1'
// The LibGDX controllers library is versioned differently to the main LibGDX versions.
// See https://github.com/libgdx/gdx-controllers/wiki/Compatibility for compatible versions.
- gdxControllersVersion = '2.1.0'
+ gdxControllersVersion = '2.2.3'
nuiVersion = '4.0.0-SNAPSHOT'
}
\ No newline at end of file
diff --git a/build-logic/src/main/groovy/destination-sol-jre.gradle b/build-logic/src/main/groovy/destination-sol-jre.gradle
index 49ccafc84..502f5fa52 100644
--- a/build-logic/src/main/groovy/destination-sol-jre.gradle
+++ b/build-logic/src/main/groovy/destination-sol-jre.gradle
@@ -19,12 +19,13 @@ plugins {
}
// Uses Bellsoft Liberica JRE
-def jreVersion = '8u352+8'
+def jreVersion = '11.0.19+7'
def jreUrlBase = "https://download.bell-sw.com/java/$jreVersion/bellsoft-jre$jreVersion"
def jreUrlFilenames = [
lwjreLinux64 : 'linux-amd64.tar.gz',
lwjre : 'windows-i586.zip',
- lwjreOSX : 'macos-amd64.zip'
+ lwjreOSX : 'macos-amd64.zip',
+ lwjreOSXArm : 'macos-aarch64.zip'
]
tasks.register('downloadJreAll') {
diff --git a/build-logic/src/main/groovy/destination-sol-repositories.gradle b/build-logic/src/main/groovy/destination-sol-repositories.gradle
index 78cedd747..416ef11c5 100644
--- a/build-logic/src/main/groovy/destination-sol-repositories.gradle
+++ b/build-logic/src/main/groovy/destination-sol-repositories.gradle
@@ -32,14 +32,13 @@ repositories {
// Terasology Artifactory for any shared libs
maven {
- url "http://artifactory.terasology.org/artifactory/virtual-repo-live"
+ url "https://artifactory.terasology.io/artifactory/virtual-repo-live"
content {
includeGroupByRegex('org\\.terasology(\\..+)?')
includeGroupByRegex('org\\.destinationsol(\\..+)?')
// A copy of jpastebin is hosted here
includeModule('brianbb', 'jpastebin')
}
- allowInsecureProtocol true // TODO: Review this when HTTPS finally supported.
}
google()
diff --git a/build-logic/src/main/groovy/gestalt-repositories.gradle b/build-logic/src/main/groovy/gestalt-repositories.gradle
index 06d5f94e7..327fbdfcb 100644
--- a/build-logic/src/main/groovy/gestalt-repositories.gradle
+++ b/build-logic/src/main/groovy/gestalt-repositories.gradle
@@ -15,12 +15,11 @@ repositories {
// Terasology Artifactory for any shared libs
maven {
- url "http://artifactory.terasology.org/artifactory/virtual-repo-live"
+ url "https://artifactory.terasology.io/artifactory/virtual-repo-live"
content {
includeGroupByRegex('org\\.terasology.gestalt(\\..+)?')
// A copy of Java-semver is hosted here too
includeModule('com.github.zafarkhaja', 'java-semver')
}
- allowInsecureProtocol true // TODO: Review this when HTTPS finally supported.
}
}
\ No newline at end of file
diff --git a/build-logic/src/main/groovy/terasology-publish-common.gradle b/build-logic/src/main/groovy/terasology-publish-common.gradle
index 405558ded..2c0c8c1eb 100644
--- a/build-logic/src/main/groovy/terasology-publish-common.gradle
+++ b/build-logic/src/main/groovy/terasology-publish-common.gradle
@@ -15,11 +15,9 @@ publishing {
repositories {
maven {
name = 'TerasologyOrg'
- allowInsecureProtocol true // Still no HTTPS on our Artifactory yet...
-
if (rootProject.hasProperty("publishRepo")) {
// This first option is good for local testing, you can set a full explicit target repo in gradle.properties
- url = "http://artifactory.terasology.org/artifactory/$publishRepo"
+ url = "https://artifactory.terasology.io/artifactory/$publishRepo"
logger.info("Changing PUBLISH repoKey set via Gradle property to {}", publishRepo)
} else {
@@ -38,7 +36,7 @@ publishing {
}
logger.info("The final deduced publish repo is {}", deducedPublishRepo)
- url = "http://artifactory.terasology.org/artifactory/$deducedPublishRepo"
+ url = "https://artifactory.terasology.io/artifactory/$deducedPublishRepo"
}
if (rootProject.hasProperty("mavenUser") && rootProject.hasProperty("mavenPass")) {
diff --git a/build.gradle b/build.gradle
index 2503ef49f..aab26bd80 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,8 +18,7 @@ repositories {
// Terasology Artifactory for any shared libs
maven {
- url "http://artifactory.terasology.org/artifactory/virtual-repo-live"
- allowInsecureProtocol true // TODO: Review this when HTTPS finally supported.
+ url "https://artifactory.terasology.io/artifactory/virtual-repo-live"
}
maven { url "https://maven.google.com" }
diff --git a/engine/build.gradle b/engine/build.gradle
index 216af6515..e54bf0991 100644
--- a/engine/build.gradle
+++ b/engine/build.gradle
@@ -21,8 +21,7 @@ buildscript {
google()
maven {
- url "http://artifactory.terasology.org/artifactory/virtual-repo-live"
- allowInsecureProtocol true // TODO: Review this when HTTPS finally supported.
+ url "https://artifactory.terasology.io/artifactory/virtual-repo-live"
}
// Needed for Jsemver, which is a gestalt dependency
maven { url = 'https://heisluft.de/maven/' }
diff --git a/engine/src/main/java/org/destinationsol/FactionDisplay.java b/engine/src/main/java/org/destinationsol/FactionDisplay.java
index db448e813..a187d722a 100644
--- a/engine/src/main/java/org/destinationsol/FactionDisplay.java
+++ b/engine/src/main/java/org/destinationsol/FactionDisplay.java
@@ -17,9 +17,7 @@
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
-import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
-import org.destinationsol.game.FactionInfo;
import org.destinationsol.game.ObjectManager;
import org.destinationsol.game.SolCam;
import org.destinationsol.game.SolGame;
@@ -27,6 +25,7 @@
import org.destinationsol.game.ship.SolShip;
import org.destinationsol.ui.SolInputManager;
import org.destinationsol.ui.UiDrawer;
+import org.terasology.nui.backends.libgdx.GdxColorUtil;
/**
* Acquires faction information fromm all the ships and draws it above them.
@@ -49,8 +48,8 @@ public void drawFactionNames(SolGame game, UiDrawer uiDrawer, SolInputManager in
if (obj instanceof SolShip) {
SolShip ship = (SolShip) obj;
Vector2 drawPosition = camera.worldToScreen(ship);
- uiDrawer.drawString(ship.getFactionName(), drawPosition.x * SolApplication.displayDimensions.getRatio(),
- drawPosition.y - .1f, 1, false, Color.valueOf(FactionInfo.getFactionColors().get(ship.getFactionID()).toString()));
+ uiDrawer.drawString(ship.getFaction().getName(), drawPosition.x * SolApplication.displayDimensions.getRatio(),
+ drawPosition.y - .1f, 1, false, GdxColorUtil.terasologyToGDXColor(ship.getFaction().getColour()));
}
}
}
diff --git a/engine/src/main/java/org/destinationsol/SolApplication.java b/engine/src/main/java/org/destinationsol/SolApplication.java
index 8cd6a54ba..50b9f58e4 100644
--- a/engine/src/main/java/org/destinationsol/SolApplication.java
+++ b/engine/src/main/java/org/destinationsol/SolApplication.java
@@ -333,6 +333,9 @@ private void draw() {
public void play(boolean tut, String shipName, boolean isNewGame, WorldConfig worldConfig) {
ModuleManager moduleManager = appContext.getBean(ModuleManager.class);
+ moduleManager.loadEnvironment(worldConfig.getModules());
+ appContext.getBean(AssetHelper.class).switchEnvironment(moduleManager.getEnvironment());
+
gameContext = appContext.getNestedContainer(
new GameConfigurationServiceRegistry(worldConfig),
new EventReceiverServiceRegistry(moduleManager.getEnvironment()),
diff --git a/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java b/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java
index 0e6485e74..97c0cdb8f 100644
--- a/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java
+++ b/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java
@@ -37,6 +37,7 @@
import org.destinationsol.game.chunk.ChunkManager;
import org.destinationsol.game.drawables.DrawableDebugger;
import org.destinationsol.game.drawables.DrawableManager;
+import org.destinationsol.game.faction.FactionsConfigs;
import org.destinationsol.game.farBg.FarBackgroundManagerOld;
import org.destinationsol.game.item.ItemManager;
import org.destinationsol.game.item.LootBuilder;
@@ -69,6 +70,7 @@ public SolGameServiceRegistry(boolean isTutorial) {
this.with(ShipBuilder.class).lifetime(Lifetime.Singleton);
this.with(GridDrawer.class).lifetime(Lifetime.Singleton);
this.with(FarBackgroundManagerOld.class).lifetime(Lifetime.Singleton);
+ this.with(FactionsConfigs.class).lifetime(Lifetime.Singleton);
this.with(FactionManager.class).lifetime(Lifetime.Singleton);
this.with(MapDrawer.class).lifetime(Lifetime.Singleton);
this.with(RubbleBuilder.class).lifetime(Lifetime.Singleton);
diff --git a/engine/src/main/java/org/destinationsol/assets/AssetHelper.java b/engine/src/main/java/org/destinationsol/assets/AssetHelper.java
index c9cdb2c9d..79af1120d 100644
--- a/engine/src/main/java/org/destinationsol/assets/AssetHelper.java
+++ b/engine/src/main/java/org/destinationsol/assets/AssetHelper.java
@@ -131,6 +131,10 @@ public Set listAssets(Class extends Asset>> type, String asset,
return list;
}
+ public void switchEnvironment(ModuleEnvironment environment) {
+ assetTypeManager.switchEnvironment(environment);
+ }
+
public void dispose() {
try {
assetTypeManager.unloadEnvironment();
diff --git a/engine/src/main/java/org/destinationsol/assets/Assets.java b/engine/src/main/java/org/destinationsol/assets/Assets.java
index 92ceb4d8b..51ac3eacf 100644
--- a/engine/src/main/java/org/destinationsol/assets/Assets.java
+++ b/engine/src/main/java/org/destinationsol/assets/Assets.java
@@ -225,7 +225,7 @@ public static Animation getAnimation(String texturePat
}
String animationPath = texturePath + "Animation";
- if (!assetHelper.get(new ResourceUrn(animationPath), DSTexture.class).isPresent()) {
+ if (!assetHelper.get(new ResourceUrn(animationPath), Json.class).isPresent()) {
return new Animation<>(Float.MAX_VALUE, getAtlasRegion(texturePath));
}
diff --git a/engine/src/main/java/org/destinationsol/assets/json/Json.java b/engine/src/main/java/org/destinationsol/assets/json/Json.java
index 9a85b5714..c1d670efb 100644
--- a/engine/src/main/java/org/destinationsol/assets/json/Json.java
+++ b/engine/src/main/java/org/destinationsol/assets/json/Json.java
@@ -21,7 +21,7 @@
import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.gestalt.assets.module.annotations.RegisterAssetType;
-@RegisterAssetType(folderName = {"collisionMeshes", "ships", "items", "configs", "grounds", "mazes", "asteroids", "schemas"}, factoryClass = JsonFactory.class)
+@RegisterAssetType(folderName = {"collisionMeshes", "ships", "factions", "items", "configs", "grounds", "mazes", "asteroids", "schemas"}, factoryClass = JsonFactory.class)
public class Json extends Asset {
private JsonData jsonData;
diff --git a/engine/src/main/java/org/destinationsol/assets/music/OggMusicManager.java b/engine/src/main/java/org/destinationsol/assets/music/OggMusicManager.java
index c4c3619fb..cf7ff2b72 100644
--- a/engine/src/main/java/org/destinationsol/assets/music/OggMusicManager.java
+++ b/engine/src/main/java/org/destinationsol/assets/music/OggMusicManager.java
@@ -238,7 +238,7 @@ public void unregisterModuleMusic() {
}
public void resetMusic() {
- musicMap.put(GAME_MUSIC_SET, new ArrayList<>());
+ musicMap.clear();
}
public String getCurrentMusicSet() {
diff --git a/engine/src/main/java/org/destinationsol/game/AbilityCommonConfig.java b/engine/src/main/java/org/destinationsol/game/AbilityCommonConfig.java
index 752b55592..bdff810a7 100644
--- a/engine/src/main/java/org/destinationsol/game/AbilityCommonConfig.java
+++ b/engine/src/main/java/org/destinationsol/game/AbilityCommonConfig.java
@@ -15,6 +15,8 @@
*/
package org.destinationsol.game;
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import org.destinationsol.assets.Assets;
import org.destinationsol.assets.sound.OggSound;
import org.destinationsol.assets.sound.OggSoundManager;
import org.destinationsol.assets.sound.PlayableSound;
@@ -23,10 +25,12 @@
import org.json.JSONObject;
public class AbilityCommonConfig {
+ public final TextureAtlas.AtlasRegion icon;
public final EffectConfig effect;
public final PlayableSound activatedSound;
- public AbilityCommonConfig(EffectConfig effect, PlayableSound activatedSound) {
+ public AbilityCommonConfig(TextureAtlas.AtlasRegion icon, EffectConfig effect, PlayableSound activatedSound) {
+ this.icon = icon;
this.effect = effect;
this.activatedSound = activatedSound;
}
@@ -34,6 +38,7 @@ public AbilityCommonConfig(EffectConfig effect, PlayableSound activatedSound) {
public static AbilityCommonConfig load(JSONObject node, EffectTypes types, GameColors cols, OggSoundManager soundManager) {
EffectConfig ec = EffectConfig.load(node.has("effect") ? node.getJSONObject("effect") : null, types, cols);
OggSound activatedSound = soundManager.getSound(node.getString("activatedSound"));
- return new AbilityCommonConfig(ec, activatedSound);
+ TextureAtlas.AtlasRegion icon = node.has("icon") ? Assets.getAtlasRegion(node.getString("icon")) : null;
+ return new AbilityCommonConfig(icon, ec, activatedSound);
}
}
diff --git a/engine/src/main/java/org/destinationsol/game/Faction.java b/engine/src/main/java/org/destinationsol/game/Faction.java
deleted file mode 100644
index b09af6202..000000000
--- a/engine/src/main/java/org/destinationsol/game/Faction.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2018 MovingBlocks
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.destinationsol.game;
-
-public enum Faction {
- LAANI("laani"), EHAR("ehar");
- private final String myName;
-
- Faction(String name) {
- myName = name;
- }
-
- public String getName() {
- return myName;
- }
-}
diff --git a/engine/src/main/java/org/destinationsol/game/FactionInfo.java b/engine/src/main/java/org/destinationsol/game/FactionInfo.java
deleted file mode 100644
index c1c317aab..000000000
--- a/engine/src/main/java/org/destinationsol/game/FactionInfo.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2018 MovingBlocks
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.destinationsol.game;
-
-import org.destinationsol.assets.Assets;
-import org.destinationsol.assets.json.Json;
-import org.destinationsol.assets.json.Validator;
-import org.destinationsol.game.ship.SolShip;
-import org.json.JSONArray;
-import org.terasology.gestalt.assets.ResourceUrn;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Loads ands stores faction data
- */
-public class FactionInfo {
- private static ArrayList factionName = new ArrayList();
- private static ArrayList factionColor = new ArrayList();
- private static ArrayList factionDisposition = new ArrayList();
-
- public static void init() {
- createFactionList();
- }
-
- private static void createFactionList() {
- for (String modulePath : getModuleSet()) {
- Json factionJson = Assets.getJson(modulePath);
- Validator.getValidatedJSON(modulePath, "engine:schemaFactions");
- JSONArray factionJsonArray = factionJson.getJsonValue().getJSONArray("factions");
- for (int n = 0; n < factionJsonArray.length(); n++) {
- factionName.add(factionJsonArray.getJSONObject(n).getString("name").replace("\"", ""));
- factionColor.add(factionJsonArray.getJSONObject(n).getString("color").replace("\"", ""));
- factionDisposition.add(factionJsonArray.getJSONObject(n).getInt("disposition"));
- }
- }
- }
-
- private static Set getModuleSet() {
- Set moduleSet = new HashSet();
- Set moduleUrn = Assets.getAssetHelper().listAssets(Json.class, "factions");
- for (ResourceUrn module : moduleUrn) {
- moduleSet.add(module.toString());
- }
- return moduleSet;
- }
-
- public static void clearValues() {
- factionName.clear();
- factionColor.clear();
- factionDisposition.clear();
- }
-
- public static ArrayList getFactionNames() {
- return factionName;
- }
-
- public static ArrayList getFactionColors() {
- return factionColor;
- }
-
- public static int getFactionID(SolShip ship) {
- String shipName = ship.getHull().getHullConfig().getInternalName();
- for (String modulePath : getModuleSet()) {
- Json factionJson = Assets.getJson(modulePath);
- Validator.getValidatedJSON(modulePath, "engine:schemaFactions");
- JSONArray factionJsonArray = factionJson.getJsonValue().getJSONArray("factions");
- shipName = shipName.replaceAll(".*:", "");
- for (int n = 0; n < factionJsonArray.length(); n++) {
- for (int z = 0; z < factionJsonArray.getJSONObject(n).getJSONArray("ships").length(); z++) {
- if (shipName.equals(factionJsonArray.getJSONObject(n).getJSONArray("ships").get(z))) {
- return n;
- }
- }
- }
- }
- return 0;
- }
-
- public static ArrayList getDisposition() {
- return factionDisposition;
- }
-
- public static void setDisposition(int n, int num) {
- if (factionDisposition.get(n) <= 100) {
- factionDisposition.set(n, factionDisposition.get(n) + num);
- }
- }
-}
diff --git a/engine/src/main/java/org/destinationsol/game/FactionManager.java b/engine/src/main/java/org/destinationsol/game/FactionManager.java
index 886a04f99..053aef310 100644
--- a/engine/src/main/java/org/destinationsol/game/FactionManager.java
+++ b/engine/src/main/java/org/destinationsol/game/FactionManager.java
@@ -18,24 +18,105 @@
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.RayCastCallback;
+import org.destinationsol.game.faction.Faction;
+import org.destinationsol.game.faction.FactionsConfigs;
+import org.destinationsol.game.faction.ReputationEvent;
import org.destinationsol.game.input.Pilot;
import org.destinationsol.game.projectile.Projectile;
import org.destinationsol.game.ship.SolShip;
+import org.destinationsol.game.ship.hulls.HullConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.terasology.gestalt.assets.ResourceUrn;
import javax.inject.Inject;
+import java.util.ArrayList;
import java.util.List;
+/**
+ * The faction manager is responsible for managing all factions in the game.
+ */
public class FactionManager {
-
+ private static final Logger logger = LoggerFactory.getLogger(FactionManager.class);
+ private static final ResourceUrn PLAYER_FACTION_URN = new ResourceUrn("engine:player");
+ private static final ResourceUrn GENERIC_ALLY_FACTION_URN = new ResourceUrn("engine:laani");
+ private static final ResourceUrn GENERIC_ENEMY_FACTION_URN = new ResourceUrn("engine:ehar");
private final MyRayBack myRayBack;
+ private final List factions;
+ private Faction playerFaction;
+ private Faction genericAllyFaction;
+ private Faction genericEnemyFaction;
@Inject
- public FactionManager() {
+ public FactionManager(FactionsConfigs factionsConfigs) {
myRayBack = new MyRayBack();
+ factions = new ArrayList<>(factionsConfigs.factionConfigs.values());
+ for (Faction faction : factions) {
+ if (faction.getId().equals(PLAYER_FACTION_URN)) {
+ playerFaction = faction;
+ } else if (faction.getId().equals(GENERIC_ALLY_FACTION_URN)) {
+ genericAllyFaction = faction;
+ } else if (faction.getId().equals(GENERIC_ENEMY_FACTION_URN)) {
+ genericEnemyFaction = faction;
+ }
+ }
+ }
+
+ /**
+ * Reports an event that may influence relations between two factions.
+ * @param instigator the instigating faction that triggered the event.
+ * @param target the faction targeted by the event.
+ * @param event the event that occurred.
+ * @param the type of event.
+ */
+ public & ReputationEvent> void reportEvent(Faction instigator, Faction target, T event) {
+ // TODO: Add support for custom event handlers.
+ // Some examples:
+ // - A pacifist faction is offended by any attacks made by a faction, regardless of the target.
+ // - A merchant faction may randomly give a free bonus when buying items.
+ // - A protective faction may dispatch a fleet to intercept the attacker if one of their ships is attacked.
+ Integer targetReputationImpact = target.getReputationImpact(event);
+ if (targetReputationImpact != null) {
+ target.setRelation(instigator, target.getRelation(instigator) + targetReputationImpact);
+ } else {
+ target.setRelation(instigator, target.getRelation(instigator) + event.getDefaultReputationImpact());
+ }
}
/**
- * Finds the nearest Enemy @{link SolShip} for the given ship
+ * Returns all known factions.
+ * @return all known factions.
+ */
+ public Iterable getFactions() {
+ return factions;
+ }
+
+ /**
+ * Returns the player's personal faction.
+ * @return the player's personal faction.
+ */
+ public Faction getPlayerFaction() {
+ return playerFaction;
+ }
+
+ /**
+ * Returns the built-in generic ally faction, which is friendly with everyone.
+ * @return the generic ally faction.
+ */
+ public Faction getGenericAllyFaction() {
+ return genericAllyFaction;
+ }
+
+ /**
+ * Returns the built-in generic enemy faction, which is hostile to everyone (except themselves).
+ * @return the generic enemy faction.
+ */
+ public Faction getGenericEnemyFaction() {
+ return genericEnemyFaction;
+ }
+
+ /**
+ * Finds the nearest Enemy {@link SolShip} for the given ship
*
* @param game the game object
* @param ship the ship to find enemies for
@@ -48,8 +129,8 @@ public SolShip getNearestEnemy(SolGame game, SolShip ship) {
return null;
}
detectionDist += ship.getHull().config.getApproxRadius();
- Faction f = pilot.getFaction();
- return getNearestEnemy(game, detectionDist, f, ship.getPosition());
+ Faction faction = pilot.getFaction();
+ return getNearestEnemy(game, detectionDist, faction, ship.getPosition());
}
/**
@@ -64,7 +145,7 @@ public SolShip getNearestEnemy(SolGame game, Projectile projectile) {
}
/**
- * Finds the nearest Enemy @{link SolShip}
+ * Finds the nearest Enemy {@link SolShip}
*
* @param game the game object
* @param detectionDist the maximum distance allowed for detection
@@ -94,6 +175,21 @@ public SolShip getNearestEnemy(SolGame game, float detectionDist, Faction factio
return nearestEnemyShip;
}
+ /**
+ * Returns a faction capable of constructing the given hull.
+ * @param hull the hull to be constructed.
+ * @return a faction capable of constructing the given hull, if found, otherwise null.
+ */
+ public Faction getBuilderForHull(HullConfig hull) {
+ for (Faction faction : factions) {
+ if (faction.getShipDesigns().contains(new ResourceUrn(hull.getInternalName()))) {
+ return faction;
+ }
+ }
+ logger.error("Failed to find faction that produces hull: {}", hull.getInternalName());
+ return null;
+ }
+
private boolean hasObstacles(SolGame game, SolShip shipFrom, SolShip shipTo) {
myRayBack.shipFrom = shipFrom;
myRayBack.shipTo = shipTo;
@@ -102,14 +198,26 @@ private boolean hasObstacles(SolGame game, SolShip shipFrom, SolShip shipTo) {
return myRayBack.hasObstacle;
}
+ /**
+ * Specifies whether two ships are enemies to each other.
+ * @param s1 the first ship.
+ * @param s2 the second ship.
+ * @return true, if s1 and s1 are enemies, otherwise false.
+ */
public boolean areEnemies(SolShip s1, SolShip s2) {
Faction f1 = s1.getPilot().getFaction();
Faction f2 = s2.getPilot().getFaction();
return areEnemies(f1, f2);
}
+ /**
+ * Specifies whether two factions are enemies of each other.
+ * @param f1 the first faction.
+ * @param f2 the second faction.
+ * @return true, if f1 and f2 are enemies, otherwise false.
+ */
public boolean areEnemies(Faction f1, Faction f2) {
- return f1 != null && f2 != null && f1 != f2;
+ return f1 != null && f2 != null && (f1.getRelation(f2) < 0 || f2.getRelation(f1) < 0);
}
private static class MyRayBack implements RayCastCallback {
diff --git a/engine/src/main/java/org/destinationsol/game/GalaxyFiller.java b/engine/src/main/java/org/destinationsol/game/GalaxyFiller.java
index f57fb8df5..3259ba75a 100644
--- a/engine/src/main/java/org/destinationsol/game/GalaxyFiller.java
+++ b/engine/src/main/java/org/destinationsol/game/GalaxyFiller.java
@@ -22,6 +22,7 @@
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
import org.destinationsol.files.HullConfigManager;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.input.AiPilot;
import org.destinationsol.game.input.ExplorerDestProvider;
import org.destinationsol.game.input.Guardian;
@@ -85,6 +86,7 @@ private Vector2 getPosForStation(SolarSystem sys, boolean mainStation, ConsumedA
private FarShip build(SolGame game, ShipConfig config, Faction faction, boolean mainStation, SolarSystem system,
ConsumedAngles angles) {
HullConfig hullConf = config.hull;
+ boolean isPlayerAlly = game.getFactionMan().getPlayerFaction().getRelation(faction) >= 0;
MoveDestProvider destProvider;
Vector2 position;
@@ -99,7 +101,7 @@ private FarShip build(SolGame game, ShipConfig config, Faction faction, boolean
boolean isBig = hullConf.getType() == HullConfig.Type.BIG;
destProvider = new ExplorerDestProvider(position, !isBig, hullConf, system);
if (isBig) {
- if (faction == Faction.LAANI) {
+ if (isPlayerAlly) {
tradeConfig = system.getConfig().tradeConfig;
}
} else {
@@ -109,7 +111,7 @@ private FarShip build(SolGame game, ShipConfig config, Faction faction, boolean
Pilot pilot = new AiPilot(destProvider, true, faction, true, "something", detectionDist);
float angle = mainStation ? 0 : SolRandom.seededRandomFloat(180);
boolean hasRepairer;
- hasRepairer = faction == Faction.LAANI;
+ hasRepairer = isPlayerAlly;
int money = config.money;
FarShip ship = game.getShipBuilder().buildNewFar(game, position, null, angle, 0, pilot, config.items, hullConf, null, hasRepairer, money, tradeConfig, true);
game.getObjectManager().addFarObjNow(ship);
@@ -148,7 +150,8 @@ public void fill(SolGame game, HullConfigManager hullConfigManager, ItemManager
ShipConfig mainStationCfg = ShipConfig.load(hullConfigManager, rootNode, itemManager);
ConsumedAngles angles = new ConsumedAngles();
- FarShip mainStation = build(game, mainStationCfg, Faction.LAANI, true, systems.get(0), angles);
+ // TODO: Select an appropriate enemy faction based on the player faction.
+ FarShip mainStation = build(game, mainStationCfg, game.getFactionMan().getBuilderForHull(mainStationCfg.hull), true, systems.get(0), angles);
mainStationPos.set(mainStation.getPosition());
mainStationHc = mainStation.getHullConfig();
@@ -158,14 +161,16 @@ public void fill(SolGame game, HullConfigManager hullConfigManager, ItemManager
for (ShipConfig shipConfig : solarSystemConfig.constAllies) {
int count = (int) (shipConfig.density);
for (int i = 0; i < count; i++) {
- build(game, shipConfig, Faction.LAANI, false, system, angles);
+ // TODO: Select an appropriate ally faction based on the player faction.
+ build(game, shipConfig, game.getFactionMan().getGenericAllyFaction(), false, system, angles);
}
}
for (ShipConfig shipConfig : solarSystemConfig.constEnemies) {
int count = (int) (shipConfig.density);
for (int i = 0; i < count; i++) {
- build(game, shipConfig, Faction.EHAR, false, system, angles);
+ // TODO: Select an appropriate enemy faction based on the player faction.
+ build(game, shipConfig, game.getFactionMan().getGenericEnemyFaction(), false, system, angles);
}
}
@@ -227,7 +232,7 @@ private void link(SolGame game, Planet firstPlanet, Planet secondPlanet) {
private void createGuard(SolGame game, FarShip target, ShipConfig guardConfig, Faction faction, float guardRelAngle) {
Guardian dp = new Guardian(game, guardConfig.hull, target.getPilot(), target.getPosition(), target.getHullConfig(), guardRelAngle);
Pilot pilot = new AiPilot(dp, true, faction, false, null, Const.AI_DET_DIST);
- boolean hasRepairer = faction == Faction.LAANI;
+ boolean hasRepairer = game.getFactionMan().getPlayerFaction().getRelation(faction) >= 0;
int money = guardConfig.money;
FarShip enemy = game.getShipBuilder().buildNewFar(game, dp.getDestination(), null, guardRelAngle, 0, pilot, guardConfig.items,
guardConfig.hull, null, hasRepairer, money, null, true);
diff --git a/engine/src/main/java/org/destinationsol/game/Hero.java b/engine/src/main/java/org/destinationsol/game/Hero.java
index ffe652575..cc5444eab 100644
--- a/engine/src/main/java/org/destinationsol/game/Hero.java
+++ b/engine/src/main/java/org/destinationsol/game/Hero.java
@@ -19,6 +19,7 @@
import org.destinationsol.GameOptions;
import org.destinationsol.assets.music.OggMusicManager;
import org.destinationsol.common.SolException;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.input.Pilot;
import org.destinationsol.game.item.Armor;
import org.destinationsol.game.item.ItemContainer;
@@ -103,6 +104,10 @@ public SolShip getShipUnchecked() {
return shipHero;
}
+ public Faction getFaction() {
+ return getPilot().getFaction();
+ }
+
public StarPort.Transcendent getTranscendentHero() {
if (!isTranscendent) {
throw new SolException("Something is trying to get a Transcendent hero while the hero is in SolShip state.");
diff --git a/engine/src/main/java/org/destinationsol/game/MapDrawer.java b/engine/src/main/java/org/destinationsol/game/MapDrawer.java
index 4292de859..d1c5b95b6 100644
--- a/engine/src/main/java/org/destinationsol/game/MapDrawer.java
+++ b/engine/src/main/java/org/destinationsol/game/MapDrawer.java
@@ -28,6 +28,7 @@
import org.destinationsol.common.SolColor;
import org.destinationsol.common.SolMath;
import org.destinationsol.game.context.Context;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.maze.Maze;
import org.destinationsol.game.maze.MazeBuilder;
import org.destinationsol.game.planet.FarTileObject;
diff --git a/engine/src/main/java/org/destinationsol/game/PlayerCreator.java b/engine/src/main/java/org/destinationsol/game/PlayerCreator.java
index 6be256014..c5d344cb8 100644
--- a/engine/src/main/java/org/destinationsol/game/PlayerCreator.java
+++ b/engine/src/main/java/org/destinationsol/game/PlayerCreator.java
@@ -18,6 +18,7 @@
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import org.destinationsol.Const;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.input.AiPilot;
import org.destinationsol.game.input.BeaconDestProvider;
import org.destinationsol.game.input.Pilot;
@@ -51,7 +52,8 @@ Hero createPlayer(ShipConfig shipConfig, boolean shouldSpawnOnGalaxySpawnPositio
}
private Hero configureAndCreateHero(ShipConfig shipConfig, RespawnState respawnState, SolGame game, boolean isMouseControl, boolean isNewShip, Vector2 position) {
- Pilot pilot = createPilot(game, isMouseControl);
+ Faction faction = game.getFactionMan().getPlayerFaction();
+ Pilot pilot = createPilot(game, faction, isMouseControl);
float money = grantPlayerMoney(shipConfig, respawnState, game);
HullConfig hull = findHullConfig(shipConfig, respawnState);
String items = findItems(shipConfig, respawnState);
@@ -167,11 +169,11 @@ private float grantPlayerMoney(ShipConfig shipConfig, RespawnState respawnState,
return shipConfig.getMoney();
}
- private Pilot createPilot(SolGame game, boolean isMouseControl) {
+ private Pilot createPilot(SolGame game, Faction faction, boolean isMouseControl) {
if (isMouseControl) {
- return new AiPilot(new BeaconDestProvider(), true, Faction.LAANI, false, "you", Const.AI_DET_DIST);
+ return new AiPilot(new BeaconDestProvider(), true, faction, false, "you", Const.AI_DET_DIST);
} else {
- return new UiControlledPilot(game.getScreens().getOldMainGameScreen().getShipControl());
+ return new UiControlledPilot(faction, game.getScreens().getOldMainGameScreen().getShipControl());
}
}
diff --git a/engine/src/main/java/org/destinationsol/game/SaveManager.java b/engine/src/main/java/org/destinationsol/game/SaveManager.java
index cc4725006..b12359e32 100644
--- a/engine/src/main/java/org/destinationsol/game/SaveManager.java
+++ b/engine/src/main/java/org/destinationsol/game/SaveManager.java
@@ -36,10 +36,13 @@
import org.destinationsol.game.item.SolItem;
import org.destinationsol.game.ship.SolShip;
import org.destinationsol.game.ship.hulls.HullConfig;
+import org.destinationsol.modules.ModuleManager;
import org.destinationsol.ui.Waypoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.context.annotation.API;
+import org.terasology.gestalt.module.Module;
+import org.terasology.gestalt.naming.Name;
import java.io.File;
import java.io.FileNotFoundException;
@@ -50,8 +53,10 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
@API
public class SaveManager {
@@ -263,6 +268,15 @@ public static void saveWorld(WorldConfig worldConfig) {
}
world.add("featureGenerators", featureGenerators);
+ JsonArray modulesArray = new JsonArray();
+ for (Name module : ModuleManager.getEnvironmentStatic().getModuleIdsOrderedByDependencies()) {
+ // Exclude built-in modules
+ if (module.compareTo("engine") != 0 && module.compareTo("nui") != 0) {
+ modulesArray.add(module.toString());
+ }
+ }
+ world.add("modules", modulesArray);
+
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String stringToWrite = gson.toJson(world);
@@ -316,6 +330,24 @@ public static Optional loadWorld() {
config.setFeatureGenerators(featureGenerators);
}
+ if (world.has("modules")) {
+ Set modules = new HashSet<>();
+ for (JsonElement value : world.getAsJsonArray("modules")) {
+ if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
+ Module module = ModuleManager.getEnvironmentStatic().get(new Name(value.getAsString()));
+ if (module != null) {
+ modules.add(module);
+ } else {
+ logger.warn("The module \"" + value.getAsString() + "\" is missing!");
+ }
+ }
+ }
+ config.setModules(modules);
+ } else {
+ // This is for compatibility with older saves, which always used all modules unconditionally.
+ config.setModules(new HashSet<>(ModuleManager.getEnvironmentStatic().getModulesOrderedByDependencies()));
+ }
+
logger.debug("Successfully loaded the world file");
return Optional.of(config);
} catch (FileNotFoundException e) {
diff --git a/engine/src/main/java/org/destinationsol/game/SolGame.java b/engine/src/main/java/org/destinationsol/game/SolGame.java
index 9cf9518f6..865b5a542 100644
--- a/engine/src/main/java/org/destinationsol/game/SolGame.java
+++ b/engine/src/main/java/org/destinationsol/game/SolGame.java
@@ -168,7 +168,6 @@ public MainGameScreen getMainGameScreen() {
@Inject
public SolGame(SolApplication solApplication, PlanetConfigManager planetConfigManager, MazeConfigManager mazeConfigManager, BeltConfigManager beltConfigManager) {
- FactionInfo.init();
this.solApplication = solApplication;
boolean isMobile = solApplication.isMobile();
@@ -344,7 +343,6 @@ public void onGameEnd(Context context) {
// TODO: Remove this when context is reset after each game
context.get(EntitySystemManager.class).getEntityManager().allEntities().forEach(EntityRef::delete);
- FactionInfo.clearValues();
try {
objectManager.close();
} catch (Exception e) {
@@ -366,8 +364,16 @@ private void saveShip() {
hull = hero.isTranscendent() ? hero.getTranscendentHero().getShip().getHullConfig() : hero.getHull().config;
money = hero.getMoney();
items = new ArrayList<>();
+
+ SolItem defaultEngineItem = hull.getEngineConfig().exampleEngine;
for (List group : hero.getItemContainer()) {
for (SolItem i : group) {
+ if (i.isSame(defaultEngineItem)) {
+ // The default engine is always added to new ships to mimic previous behaviour.
+ // We do not want to save this engine, as it would otherwise be duplicated on reload.
+ continue;
+ }
+
items.add(0, i);
}
}
@@ -620,8 +626,16 @@ public void setRespawnState() {
respawnState.getRespawnItems().clear();
respawnState.getRespawnWaypoints().clear();
respawnState.setPlayerRespawned(true);
+
+ SolItem defaultShipEngine = respawnState.getRespawnHull().getEngineConfig().exampleEngine;
for (List group : hero.getItemContainer()) {
for (SolItem item : group) {
+ if (item.isSame(defaultShipEngine)) {
+ // The default engine is always added to new ships to mimic previous behaviour.
+ // We do not want to save this engine, as it would otherwise be duplicated on respawn.
+ continue;
+ }
+
boolean equipped = hero.isTranscendent() || hero.maybeUnequip(this, item, false);
if (equipped || SolRandom.test(.75f)) {
respawnState.getRespawnItems().add(item);
diff --git a/engine/src/main/java/org/destinationsol/game/WorldConfig.java b/engine/src/main/java/org/destinationsol/game/WorldConfig.java
index fcb636b44..23f077992 100644
--- a/engine/src/main/java/org/destinationsol/game/WorldConfig.java
+++ b/engine/src/main/java/org/destinationsol/game/WorldConfig.java
@@ -16,30 +16,37 @@
package org.destinationsol.game;
import org.destinationsol.game.planet.SystemsBuilder;
+import org.terasology.gestalt.module.Module;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
public class WorldConfig {
protected long seed;
protected int numberOfSystems;
private List solarSystemGenerators;
private List featureGenerators;
+ private Set modules;
public WorldConfig() {
seed = System.currentTimeMillis();
numberOfSystems = SystemsBuilder.DEFAULT_SYSTEM_COUNT;
solarSystemGenerators = new ArrayList<>();
featureGenerators = new ArrayList<>();
+ modules = new HashSet<>();
}
public WorldConfig(long seed, int numberOfSystems,
List solarSystemGenerators,
- List featureGenerators) {
+ List featureGenerators,
+ Set modules) {
this.seed = seed;
this.numberOfSystems = numberOfSystems;
this.solarSystemGenerators = solarSystemGenerators;
this.featureGenerators = featureGenerators;
+ this.modules = modules;
}
public long getSeed() {
@@ -73,4 +80,12 @@ public List getFeatureGenerators() {
public void setSolarSystemGenerators(List solarSystemGenerators) {
this.solarSystemGenerators = solarSystemGenerators;
}
+
+ public Set getModules() {
+ return modules;
+ }
+
+ public void setModules(Set modules) {
+ this.modules = modules;
+ }
}
diff --git a/engine/src/main/java/org/destinationsol/game/chunk/ChunkFiller.java b/engine/src/main/java/org/destinationsol/game/chunk/ChunkFiller.java
index 7a79e569c..e7a56b8f6 100644
--- a/engine/src/main/java/org/destinationsol/game/chunk/ChunkFiller.java
+++ b/engine/src/main/java/org/destinationsol/game/chunk/ChunkFiller.java
@@ -24,7 +24,6 @@
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
import org.destinationsol.game.DebugOptions;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.RemoveController;
import org.destinationsol.game.ShipConfig;
import org.destinationsol.game.SolGame;
@@ -197,7 +196,7 @@ private FarShip buildSpaceEnemy(SolGame game, Vector2 position, RemoveController
SolMath.fromAl(velocity, SolRandom.randomFloat(180), SolRandom.randomFloat(0, ENEMY_MAX_SPD));
float rotationSpeed = SolRandom.randomFloat(ENEMY_MAX_ROT_SPD);
MoveDestProvider dp = new StillGuard(position, game, enemyConf);
- Pilot provider = new AiPilot(dp, false, Faction.EHAR, true, null, Const.AI_DET_DIST);
+ Pilot provider = new AiPilot(dp, false, game.getFactionMan().getBuilderForHull(enemyConf.hull), true, null, Const.AI_DET_DIST);
HullConfig config = enemyConf.hull;
int money = enemyConf.money;
float angle = SolRandom.randomFloat(180);
diff --git a/engine/src/main/java/org/destinationsol/game/console/ConsoleImpl.java b/engine/src/main/java/org/destinationsol/game/console/ConsoleImpl.java
index 9d79a7e6f..133127e8d 100644
--- a/engine/src/main/java/org/destinationsol/game/console/ConsoleImpl.java
+++ b/engine/src/main/java/org/destinationsol/game/console/ConsoleImpl.java
@@ -89,7 +89,7 @@ private static List splitParameters(String paramStr) {
}
public void init(SolGame game) {
-
+ commandRegistry.clear();
for (Class commands : context.get(ModuleManager.class).getEnvironment().getTypesAnnotatedWith(RegisterCommands.class)) {
try {
Object commandsObject = commands.newInstance();
diff --git a/engine/src/main/java/org/destinationsol/game/faction/DefaultReputationEvent.java b/engine/src/main/java/org/destinationsol/game/faction/DefaultReputationEvent.java
new file mode 100644
index 000000000..6cdade7e3
--- /dev/null
+++ b/engine/src/main/java/org/destinationsol/game/faction/DefaultReputationEvent.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2025 The Terasology Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.destinationsol.game.faction;
+
+/**
+ * Defines events impacting reputation that are likely to be used by the base game.
+ */
+public enum DefaultReputationEvent implements ReputationEvent {
+ /**
+ * A ship (or projectile fired by a ship) has caused damage to another.
+ */
+ DAMAGED_SHIP(-1),
+ /**
+ * A ship (or projectile fired by a ship) has destroyed another.
+ */
+ DESTROYED_SHIP(-20),
+ /**
+ * A ship has bought an item from a station.
+ * (Note: this only applies to the player for now.)
+ */
+ BOUGHT_ITEM(1);
+
+ /**
+ * The default impact on reputation this event will have in absence of a faction-specific value.
+ */
+ private final int defaultReputationImpact;
+
+ DefaultReputationEvent(int defaultReputationImpact) {
+ this.defaultReputationImpact = defaultReputationImpact;
+ }
+
+ /**
+ * Returns the default impact on reputation this event will have in absence of a faction-specific value.
+ * @return the default impact on reputation this event will have in absence of a faction-specific value.
+ */
+ @Override
+ public int getDefaultReputationImpact() {
+ return defaultReputationImpact;
+ }
+}
diff --git a/engine/src/main/java/org/destinationsol/game/faction/Faction.java b/engine/src/main/java/org/destinationsol/game/faction/Faction.java
new file mode 100644
index 000000000..b321be654
--- /dev/null
+++ b/engine/src/main/java/org/destinationsol/game/faction/Faction.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2025 The Terasology Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.destinationsol.game.faction;
+
+import org.joml.Math;
+import org.terasology.gestalt.assets.ResourceUrn;
+import org.terasology.nui.Color;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A faction is an abstraction used to establish common diplomatic relations between aligned entities.
+ * Factions hold an overall disposition towards other factions, which determines their approach towards entities
+ * belonging to those factions. Factions holding a negative disposition towards others will be openly hostile.
+ */
+public class Faction {
+ /**
+ * The minimum possible reputation a faction can have with another.
+ */
+ public static final int MIN_REPUTATION = -100;
+ /**
+ * The maximum possible reputation a faction can have with another.
+ */
+ public static final int MAX_REPUTATION = 100;
+ /**
+ * The identifier used to uniquely identify this faction (e.g. "my-faction")
+ */
+ private final ResourceUrn id;
+ /**
+ * The name of this faction.
+ */
+ private final String name;
+ /**
+ * The faction's description.
+ */
+ private final String description;
+ /**
+ * The faction's primary colour.
+ */
+ private final Color colour;
+ /**
+ * Ship designs that can be produced by this faction.
+ */
+ private final List shipDesigns;
+ /**
+ * The default standing that this faction has towards unknown factions.
+ */
+ private final int defaultDisposition;
+ /**
+ * The relations held between this faction and other factions.
+ */
+ private final Map relations;
+ /**
+ * The impact that certain events will have on relations with another faction if they instigate a given event.
+ * (For example, hitting the ship with a projectile loses reputation, having a negative impact.)
+ * @see DefaultReputationEvent
+ */
+ private final Map reputationImpacts;
+
+ /**
+ * Instantiates a new faction instance.
+ * @param id the faction's id (this should be the urn corresponding to the faction's JSON definition file)
+ * @param name the name of the faction
+ * @param description a description for the faction
+ * @param colour the faction's primary colour
+ * @param defaultDisposition the faction's default disposition towards unknown factions
+ * @param shipDesigns ship designs that can be built by this faction
+ * @param reputationImpacts the impact certain events should have on relationships between this faction and others.
+ */
+ public Faction(ResourceUrn id, String name, String description, Color colour, int defaultDisposition,
+ List shipDesigns, Map reputationImpacts) {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.colour = colour;
+ this.defaultDisposition = Math.clamp(MIN_REPUTATION, MAX_REPUTATION, defaultDisposition);
+ this.shipDesigns = shipDesigns;
+ this.relations = new HashMap<>();
+ this.reputationImpacts = reputationImpacts;
+ }
+
+ /**
+ * Returns the faction's id.
+ * @return the faction's id.
+ */
+ public ResourceUrn getId() {
+ return id;
+ }
+
+ /**
+ * Returns the faction's name.
+ * @return the faction's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the faction's description.
+ * @return the faction's description.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns the faction's primary colour.
+ * @return the faction's primary colour.
+ */
+ public Color getColour() {
+ return colour;
+ }
+
+ /**
+ * Returns a list of ship designs that can be manufactured by the faction.
+ * @return a list of ship designs that this faction can make.
+ */
+ public List getShipDesigns() {
+ return shipDesigns;
+ }
+
+ /**
+ * Gets the impact of an event on this faction's relationships with others.
+ * @param event the event to query.
+ * @return the change in reputation, if known, otherwise null.
+ * @param the type of event.
+ */
+ public & ReputationEvent> Integer getReputationImpact(T event) {
+ return reputationImpacts.get(event.toString());
+ }
+
+ /**
+ * Specifies whether a faction has formal relations with another (meaning they have an explicit reputation built-up).
+ * @param faction the faction to check.
+ * @return true, if the faction is known to this faction, otherwise false.
+ */
+ public boolean isAwareOf(Faction faction) {
+ return relations.containsKey(faction);
+ }
+
+ /**
+ * Returns the reputation held by another faction with this faction.
+ * @param faction the faction to check.
+ * @return the reputation value held with this faction.
+ */
+ public int getRelation(Faction faction) {
+ if (faction == this) {
+ return MAX_REPUTATION;
+ }
+ return relations.getOrDefault(faction, defaultDisposition);
+ }
+
+ /**
+ * Sets the reputation held by this faction for another.
+ * @param faction the faction to assign reputation with.
+ * @param disposition the overall disposition of this faction towards the other.
+ */
+ public void setRelation(Faction faction, int disposition) {
+ if (faction != this) {
+ relations.put(faction, Math.clamp(MIN_REPUTATION, MAX_REPUTATION, disposition));
+ }
+ }
+}
diff --git a/engine/src/main/java/org/destinationsol/game/faction/FactionConfig.java b/engine/src/main/java/org/destinationsol/game/faction/FactionConfig.java
new file mode 100644
index 000000000..9b2c48b75
--- /dev/null
+++ b/engine/src/main/java/org/destinationsol/game/faction/FactionConfig.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2025 The Terasology Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.destinationsol.game.faction;
+
+import com.badlogic.gdx.graphics.Color;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.terasology.gestalt.assets.ResourceUrn;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public final class FactionConfig {
+ private FactionConfig() {
+ }
+
+ public static Faction load(ResourceUrn id, JSONObject jsonObject) {
+ Color gdxColour = Color.valueOf(jsonObject.getString("colour"));
+ List shipDesigns = new ArrayList<>();
+ JSONArray shipDesignsArray = jsonObject.getJSONArray("shipDesigns");
+ for (int designNo = 0; designNo < shipDesignsArray.length(); designNo++) {
+ shipDesigns.add(new ResourceUrn(shipDesignsArray.getString(designNo)));
+ }
+ Map reputationImpacts = new HashMap<>();
+ return new Faction(id, jsonObject.getString("name"),
+ jsonObject.getString("description"),
+ new org.terasology.nui.Color(gdxColour.r, gdxColour.g, gdxColour.b, gdxColour.a),
+ jsonObject.optInt("defaultDisposition", 0), shipDesigns, reputationImpacts);
+ }
+}
diff --git a/engine/src/main/java/org/destinationsol/game/faction/FactionsConfigs.java b/engine/src/main/java/org/destinationsol/game/faction/FactionsConfigs.java
new file mode 100644
index 000000000..0921bc7a0
--- /dev/null
+++ b/engine/src/main/java/org/destinationsol/game/faction/FactionsConfigs.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2025 The Terasology Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.destinationsol.game.faction;
+
+import org.destinationsol.assets.AssetHelper;
+import org.destinationsol.assets.json.Json;
+import org.destinationsol.assets.json.Validator;
+import org.json.JSONObject;
+import org.terasology.gestalt.assets.ResourceUrn;
+
+import javax.inject.Inject;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FactionsConfigs {
+ public final Map factionConfigs;
+
+ @Inject
+ public FactionsConfigs(AssetHelper assetHelper) {
+ factionConfigs = new HashMap<>();
+
+ for (ResourceUrn resource : assetHelper.listAssets(Json.class, "factions")) {
+ JSONObject rootNode = Validator.getValidatedJSON(resource.toString(), "engine:schemaFactions");
+
+ Map> relations = new HashMap<>();
+
+ for (String factionName : rootNode.keySet()) {
+ ResourceUrn normalisedName = new ResourceUrn(factionName);
+ if (!factionConfigs.containsKey(normalisedName)) {
+ factionConfigs.put(normalisedName, FactionConfig.load(normalisedName, assetHelper.get(normalisedName, Json.class).get().getJsonValue()));
+ }
+ Map factionRelations = new HashMap<>();
+ relations.put(normalisedName, new HashMap<>());
+ JSONObject factionAttributes = rootNode.getJSONObject(factionName);
+ if (factionAttributes.has("relations")) {
+ JSONObject factionRelationsJSON = factionAttributes.getJSONObject("relations");
+ for (String key : factionRelationsJSON.keySet()) {
+ factionRelations.put(new ResourceUrn(key), factionRelationsJSON.getInt(key));
+ }
+ }
+ relations.put(normalisedName, factionRelations);
+ }
+
+ for (Map.Entry> factionRelations : relations.entrySet()) {
+ Faction faction = factionConfigs.get(factionRelations.getKey());
+ for (Map.Entry factionRelation : factionRelations.getValue().entrySet()) {
+ Faction otherFaction = factionConfigs.get(factionRelation.getKey());
+ faction.setRelation(otherFaction, factionRelation.getValue());
+ }
+ }
+
+ for (Faction faction : factionConfigs.values()) {
+ for (Faction otherFaction : factionConfigs.values()) {
+ if (faction.isAwareOf(otherFaction) && otherFaction.isAwareOf(faction)) {
+ // Asymmetric relationships are explicitly allowed.
+ continue;
+ }
+
+ if (faction.isAwareOf(otherFaction) && !otherFaction.isAwareOf(faction)) {
+ otherFaction.setRelation(faction, faction.getRelation(otherFaction));
+ } else if (!faction.isAwareOf(otherFaction) && otherFaction.isAwareOf(faction)) {
+ faction.setRelation(otherFaction, otherFaction.getRelation(faction));
+ } else {
+ int factionRelation = faction.getRelation(otherFaction);
+ int otherFactionRelation = otherFaction.getRelation(faction);
+
+ // The simplified rules of uncertain Destination Sol diplomacy:
+ // - If both are friendly, the stronger positivity will prevail.
+ // - If both are hostile, the stronger hostility will prevail.
+ // - If your enemy is hostile to you, you must be hostile to your enemy.
+ // - Neutrality is considered friendly.
+ int relation = (factionRelation >= 0 && otherFactionRelation >= 0) ?
+ Math.max(factionRelation, otherFactionRelation) :
+ Math.min(factionRelation, otherFactionRelation);
+ faction.setRelation(otherFaction, relation);
+ otherFaction.setRelation(faction, relation);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/engine/src/main/java/org/destinationsol/game/faction/ReputationEvent.java b/engine/src/main/java/org/destinationsol/game/faction/ReputationEvent.java
new file mode 100644
index 000000000..248694d2f
--- /dev/null
+++ b/engine/src/main/java/org/destinationsol/game/faction/ReputationEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2025 The Terasology Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.destinationsol.game.faction;
+
+/**
+ * An event that may affect relations between factions.
+ * @see DefaultReputationEvent DefaultReputationEvent
+ */
+public interface ReputationEvent {
+ /**
+ * Returns a string representation of this event.
+ * @return a string representation of this event.
+ */
+ String toString();
+ /**
+ * Returns the default impact on reputation this event will have in absence of a faction-specific value.
+ * @return the default impact on reputation this event will have in absence of a faction-specific value.
+ */
+ int getDefaultReputationImpact();
+}
diff --git a/engine/src/main/java/org/destinationsol/game/gun/GunMount.java b/engine/src/main/java/org/destinationsol/game/gun/GunMount.java
index 179a2157c..7db91c957 100644
--- a/engine/src/main/java/org/destinationsol/game/gun/GunMount.java
+++ b/engine/src/main/java/org/destinationsol/game/gun/GunMount.java
@@ -19,11 +19,11 @@
import com.badlogic.gdx.math.Vector2;
import org.destinationsol.Const;
import org.destinationsol.common.SolMath;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.SolObject;
import org.destinationsol.game.drawables.Drawable;
import org.destinationsol.game.drawables.DrawableManager;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.input.Shooter;
import org.destinationsol.game.item.Gun;
import org.destinationsol.game.item.ItemContainer;
diff --git a/engine/src/main/java/org/destinationsol/game/gun/SolGun.java b/engine/src/main/java/org/destinationsol/game/gun/SolGun.java
index 5b9a38558..629e14a0d 100644
--- a/engine/src/main/java/org/destinationsol/game/gun/SolGun.java
+++ b/engine/src/main/java/org/destinationsol/game/gun/SolGun.java
@@ -21,13 +21,13 @@
import org.destinationsol.common.SolColor;
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.SolObject;
import org.destinationsol.game.drawables.Drawable;
import org.destinationsol.game.drawables.DrawableLevel;
import org.destinationsol.game.drawables.RectSprite;
import org.destinationsol.game.drawables.SpriteManager;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.item.Clip;
import org.destinationsol.game.item.Gun;
import org.destinationsol.game.item.ItemContainer;
diff --git a/engine/src/main/java/org/destinationsol/game/input/AiPilot.java b/engine/src/main/java/org/destinationsol/game/input/AiPilot.java
index eb9f25075..b096b9a59 100644
--- a/engine/src/main/java/org/destinationsol/game/input/AiPilot.java
+++ b/engine/src/main/java/org/destinationsol/game/input/AiPilot.java
@@ -18,8 +18,8 @@
import com.badlogic.gdx.math.Vector2;
import org.destinationsol.common.SolMath;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolGame;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.item.Engine;
import org.destinationsol.game.item.Gun;
import org.destinationsol.game.planet.Planet;
@@ -199,18 +199,6 @@ public Faction getFaction() {
return myFaction;
}
- @Override
- public void stringToFaction(String faction) {
- Map factionMap = new HashMap<>();
- if (faction.equals("laani")) {
- factionMap.put(faction, Faction.LAANI);
- }
- if (faction.equals("ehar")) {
- factionMap.put(faction, Faction.EHAR);
- }
- myFaction = factionMap.get(faction);
- }
-
@Override
public boolean shootsAtObstacles() {
return myShootAtObstacles;
diff --git a/engine/src/main/java/org/destinationsol/game/input/Pilot.java b/engine/src/main/java/org/destinationsol/game/input/Pilot.java
index 5cb1f571e..9348c2e12 100644
--- a/engine/src/main/java/org/destinationsol/game/input/Pilot.java
+++ b/engine/src/main/java/org/destinationsol/game/input/Pilot.java
@@ -16,8 +16,8 @@
package org.destinationsol.game.input;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolGame;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.ship.FarShip;
import org.destinationsol.game.ship.SolShip;
@@ -40,8 +40,6 @@ public interface Pilot {
Faction getFaction();
- void stringToFaction(String faction);
-
boolean shootsAtObstacles();
float getDetectionDist();
diff --git a/engine/src/main/java/org/destinationsol/game/input/UiControlledPilot.java b/engine/src/main/java/org/destinationsol/game/input/UiControlledPilot.java
index 01ce59fa4..7100ac3a8 100644
--- a/engine/src/main/java/org/destinationsol/game/input/UiControlledPilot.java
+++ b/engine/src/main/java/org/destinationsol/game/input/UiControlledPilot.java
@@ -17,17 +17,18 @@
package org.destinationsol.game.input;
import org.destinationsol.Const;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolGame;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.screens.ShipUiControl;
import org.destinationsol.game.ship.FarShip;
import org.destinationsol.game.ship.SolShip;
public class UiControlledPilot implements Pilot {
-
+ private final Faction faction;
private final ShipUiControl uiControls;
- public UiControlledPilot(ShipUiControl controls) {
+ public UiControlledPilot(Faction faction, ShipUiControl controls) {
+ this.faction = faction;
uiControls = controls;
}
@@ -72,12 +73,7 @@ public boolean isAbility() {
@Override
public Faction getFaction() {
- return Faction.LAANI;
- }
-
- @Override
- public void stringToFaction(String faction) {
- // TODO Create values outside of laani and ehar, making this necessary
+ return faction;
}
@Override
diff --git a/engine/src/main/java/org/destinationsol/game/item/Engine.java b/engine/src/main/java/org/destinationsol/game/item/Engine.java
index 4af344e1d..44ce285a3 100644
--- a/engine/src/main/java/org/destinationsol/game/item/Engine.java
+++ b/engine/src/main/java/org/destinationsol/game/item/Engine.java
@@ -16,17 +16,25 @@
package org.destinationsol.game.item;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
+import org.destinationsol.assets.Assets;
import org.destinationsol.assets.json.Validator;
import org.destinationsol.game.SolGame;
import org.json.JSONObject;
public class Engine implements SolItem {
+ private static final TextureAtlas.AtlasRegion DEFAULT_ENGINE_ICON = Assets.getAtlasRegion("engine:iconEngine");
private final Config config;
+ private int equipped;
private Engine(Config config) {
this.config = config;
}
+ private Engine(Config config, int equipped) {
+ this.config = config;
+ this.equipped = equipped;
+ }
+
@Override
public String getDisplayName() {
return config.displayName;
@@ -60,7 +68,7 @@ public boolean isBig() {
@Override
public Engine copy() {
- return new Engine(config);
+ return new Engine(config, equipped);
}
@Override
@@ -75,22 +83,22 @@ public TextureAtlas.AtlasRegion getIcon(SolGame game) {
@Override
public SolItemType getItemType() {
- return null;
+ return config.itemType;
}
@Override
public String getCode() {
- return null;
+ return config.code;
}
@Override
public int isEquipped() {
- return 0;
+ return this.equipped;
}
@Override
public void setEquipped(int equipped) {
-
+ this.equipped = equipped;
}
public static class Config {
@@ -104,32 +112,44 @@ public static class Config {
public final Engine exampleEngine;
public final TextureAtlas.AtlasRegion icon;
public final String code;
+ public final SolItemType itemType;
private Config(String displayName, int price, String description, float rotationAcceleration, float acceleration, float maxRotationSpeed, boolean isBig,
- TextureAtlas.AtlasRegion icon, String code) {
+ TextureAtlas.AtlasRegion icon, String code, ItemManager itemManager, SolItemType itemType) {
this.displayName = displayName;
this.price = price;
- this.description = description;
+ this.description = description + "\n\n" +
+ "Acceleration: " + acceleration + " m/s^2\n" +
+ "Rotational Acceleration: " + rotationAcceleration + " rad/s^2\n" +
+ "Maximum Angular Velocity: " + maxRotationSpeed + " rad/s";
this.rotationAcceleration = rotationAcceleration;
this.acceleration = acceleration;
this.maxRotationSpeed = maxRotationSpeed;
this.isBig = isBig;
this.icon = icon;
this.code = code;
+ this.itemType = itemType;
this.exampleEngine = new Engine(this);
+ itemManager.registerItem(exampleEngine);
}
- public static Config load(String engineName) {
+ public static Config load(String engineName, ItemManager itemManager, SolItemTypes types) {
JSONObject rootNode = Validator.getValidatedJSON(engineName, "engine:schemaEngine");
boolean isBig = rootNode.getBoolean("big");
- float rotationAcceleration = isBig ? 100f : 515f;
- float acceleration = 2f;
- float maxRotationSpeed = isBig ? 40f : 230f;
-
- // TODO: VAMPCAT: The icon / displayName was initially set to null. Is that correct?
-
- return new Config(null, 0, null, rotationAcceleration, acceleration, maxRotationSpeed, isBig, null, engineName);
+ float rotationAcceleration = (float) rootNode.optDouble("rotationAcceleration", isBig ? 100f : 515f);
+ float acceleration = (float) rootNode.optDouble("acceleration", 2f);
+ float maxRotationSpeed = (float) rootNode.optDouble("maxRotationSpeed", isBig ? 40f : 230f);
+
+ TextureAtlas.AtlasRegion icon;
+ try {
+ icon = Assets.getAtlasRegion(engineName + "Icon");
+ } catch (RuntimeException ignore) {
+ icon = DEFAULT_ENGINE_ICON;
+ }
+ return new Config(rootNode.optString("name", engineName), 0,
+ rootNode.optString("description", ""), rotationAcceleration, acceleration,
+ maxRotationSpeed, isBig, icon, engineName, itemManager, types.engine);
}
}
}
diff --git a/engine/src/main/java/org/destinationsol/game/item/ItemManager.java b/engine/src/main/java/org/destinationsol/game/item/ItemManager.java
index 1b9787d9f..c49bf0758 100644
--- a/engine/src/main/java/org/destinationsol/game/item/ItemManager.java
+++ b/engine/src/main/java/org/destinationsol/game/item/ItemManager.java
@@ -155,6 +155,8 @@ public List parseItems(String items) {
Clip.Config.load(itemName, this, myTypes);
} else if (itemName.endsWith("Shield") || itemName.endsWith("shield")) {
Shield.Config.load(itemName, this, soundManager, myTypes);
+ } else if (itemName.endsWith("Engine") || itemName.endsWith("engine")) {
+ Engine.Config.load(itemName, this, myTypes);
} else {
Gun.Config.load(itemName, this, soundManager, myTypes);
}
@@ -193,7 +195,7 @@ public SolItem getExample(String name) {
}
public Engine.Config getEngineConfig(String engineName) {
- return engineConfigs.computeIfAbsent(engineName, engineConfig -> Engine.Config.load(engineConfig));
+ return engineConfigs.computeIfAbsent(engineName, engineConfig -> Engine.Config.load(engineConfig, this, myTypes));
}
public SolItem random() {
diff --git a/engine/src/main/java/org/destinationsol/game/item/SolItemTypes.java b/engine/src/main/java/org/destinationsol/game/item/SolItemTypes.java
index 6597deeb9..1a8cd472d 100644
--- a/engine/src/main/java/org/destinationsol/game/item/SolItemTypes.java
+++ b/engine/src/main/java/org/destinationsol/game/item/SolItemTypes.java
@@ -23,6 +23,7 @@
import org.json.JSONObject;
public class SolItemTypes {
+ public final SolItemType engine;
public final SolItemType clip;
public final SolItemType shield;
public final SolItemType armor;
@@ -37,6 +38,7 @@ public class SolItemTypes {
public SolItemTypes(OggSoundManager soundManager, GameColors cols) {
JSONObject rootNode = Validator.getValidatedJSON("core:types", "engine:schemaTypes");
+ engine = load(rootNode.getJSONObject("engine"), soundManager, cols);
clip = load(rootNode.getJSONObject("clip"), soundManager, cols);
shield = load(rootNode.getJSONObject("shield"), soundManager, cols);
armor = load(rootNode.getJSONObject("armor"), soundManager, cols);
diff --git a/engine/src/main/java/org/destinationsol/game/maze/MazeBuilder.java b/engine/src/main/java/org/destinationsol/game/maze/MazeBuilder.java
index c328d6a01..a6acce2ed 100644
--- a/engine/src/main/java/org/destinationsol/game/maze/MazeBuilder.java
+++ b/engine/src/main/java/org/destinationsol/game/maze/MazeBuilder.java
@@ -21,7 +21,6 @@
import org.destinationsol.Const;
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.ShipConfig;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.input.AiPilot;
@@ -167,7 +166,7 @@ private void buildEnemy(Vector2 position, SolGame game, ShipConfig e, boolean in
if (inner) {
viewDist = TILE_SZ * 1.25f;
}
- Pilot pilot = new AiPilot(new StillGuard(position, game, e), false, Faction.EHAR, true, null, viewDist);
+ Pilot pilot = new AiPilot(new StillGuard(position, game, e), false, game.getFactionMan().getBuilderForHull(e.hull), true, null, viewDist);
int money = e.money;
FarShip s = sb.buildNewFar(game, position, new Vector2(), angle, 0, pilot, e.items, e.hull, null, false, money, null, true);
game.getObjectManager().addFarObjNow(s);
diff --git a/engine/src/main/java/org/destinationsol/game/planet/PlanetObjectsBuilder.java b/engine/src/main/java/org/destinationsol/game/planet/PlanetObjectsBuilder.java
index 6e41d73d9..ea9149194 100644
--- a/engine/src/main/java/org/destinationsol/game/planet/PlanetObjectsBuilder.java
+++ b/engine/src/main/java/org/destinationsol/game/planet/PlanetObjectsBuilder.java
@@ -25,13 +25,13 @@
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
import org.destinationsol.game.DebugOptions;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.ShipConfig;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.drawables.Drawable;
import org.destinationsol.game.drawables.DrawableLevel;
import org.destinationsol.game.drawables.RectSprite;
import org.destinationsol.game.drawables.SpriteManager;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.input.AiPilot;
import org.destinationsol.game.input.OrbiterDestProvider;
import org.destinationsol.game.input.Pilot;
@@ -104,7 +104,7 @@ private void createShips(SolGame game, Planet planet) {
private void buildStation(SolGame game, Planet planet, ConsumedAngles takenAngles) {
ShipConfig stationConfig = planet.getConfig().stationConfig;
if (stationConfig != null) {
- buildGroundShip(game, planet, stationConfig, planet.getConfig().tradeConfig, Faction.LAANI, takenAngles, "Station");
+ buildGroundShip(game, planet, stationConfig, planet.getConfig().tradeConfig, game.getFactionMan().getBuilderForHull(stationConfig.hull), takenAngles, "Station");
}
}
@@ -112,7 +112,7 @@ private void buildGroundEnemies(SolGame game, Planet planet, ConsumedAngles take
for (ShipConfig groundEnemy : config.groundEnemies) {
int count = (int) (groundEnemy.density * groundHeight);
for (int i = 0; i < count; i++) {
- buildGroundShip(game, planet, groundEnemy, null, Faction.EHAR, takenAngles, null);
+ buildGroundShip(game, planet, groundEnemy, null, game.getFactionMan().getBuilderForHull(groundEnemy.hull), takenAngles, null);
}
}
}
@@ -322,13 +322,13 @@ private void addDeco(SolGame game, float groundHeight, Vector2 planetPos,
}
private void buildGroundShip(SolGame game, Planet planet, ShipConfig shipConfig, TradeConfig tradeConfig,
- Faction faction, ConsumedAngles takenAngles, String mapHint) {
+ Faction faction, ConsumedAngles takenAngles, String mapHint) {
Vector2 position = game.getPlanetManager().findFlatPlace(game, planet, takenAngles, shipConfig.hull.getApproxRadius());
boolean goodSpot = true;
boolean station = shipConfig.hull.getType() == HullConfig.Type.STATION;
String shipItems = shipConfig.items;
boolean hasRepairer;
- hasRepairer = faction == Faction.LAANI;
+ hasRepairer = game.getHero().getFaction().getRelation(faction) >= 0;
int money = shipConfig.money;
float height = position.len();
float aboveGround;
@@ -380,7 +380,7 @@ private FarShip buildOrbitEnemy(SolGame game, Planet planet, float heightPercent
SolMath.free(directionToPlanet);
OrbiterDestProvider destProvider = new OrbiterDestProvider(planet, height, clockwise);
- Pilot provider = new AiPilot(destProvider, false, Faction.EHAR, true, null, detectionDistance);
+ Pilot provider = new AiPilot(destProvider, false, game.getFactionMan().getBuilderForHull(shipConfig.hull), true, null, detectionDistance);
int money = shipConfig.money;
diff --git a/engine/src/main/java/org/destinationsol/game/projectile/Projectile.java b/engine/src/main/java/org/destinationsol/game/projectile/Projectile.java
index 4a8bfbd17..cbc8f8c55 100644
--- a/engine/src/main/java/org/destinationsol/game/projectile/Projectile.java
+++ b/engine/src/main/java/org/destinationsol/game/projectile/Projectile.java
@@ -25,7 +25,6 @@
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
import org.destinationsol.game.DmgType;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.FactionManager;
import org.destinationsol.game.FarObject;
import org.destinationsol.game.GameDrawer;
@@ -35,6 +34,8 @@
import org.destinationsol.game.drawables.Drawable;
import org.destinationsol.game.drawables.DrawableLevel;
import org.destinationsol.game.drawables.SpriteManager;
+import org.destinationsol.game.faction.DefaultReputationEvent;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.item.Shield;
import org.destinationsol.game.particle.DSParticleEmitter;
import org.destinationsol.game.particle.EffectConfig;
@@ -216,7 +217,7 @@ private void collided(SolGame game) {
game.getPartMan().blinks(position, game, config.collisionEffectBackground.size);
}
if (ship.getPilot().isPlayer() && obstacle instanceof SolShip) {
- ship.changeDisposition(((SolShip) obstacle).getFactionID());
+ game.getFactionMan().reportEvent(ship.getFaction(), ((SolShip) obstacle).getFaction(), DefaultReputationEvent.DAMAGED_SHIP);
}
game.getSoundManager().play(game, config.collisionSound, null, this);
diff --git a/engine/src/main/java/org/destinationsol/game/screens/BorderDrawer.java b/engine/src/main/java/org/destinationsol/game/screens/BorderDrawer.java
index 182736b96..44c236c5f 100644
--- a/engine/src/main/java/org/destinationsol/game/screens/BorderDrawer.java
+++ b/engine/src/main/java/org/destinationsol/game/screens/BorderDrawer.java
@@ -23,7 +23,6 @@
import org.destinationsol.assets.Assets;
import org.destinationsol.common.SolColor;
import org.destinationsol.common.SolMath;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.FactionManager;
import org.destinationsol.game.HardnessCalc;
import org.destinationsol.game.Hero;
@@ -33,6 +32,7 @@
import org.destinationsol.game.SolObject;
import org.destinationsol.game.StarPort;
import org.destinationsol.game.context.Context;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.planet.Planet;
import org.destinationsol.game.planet.PlanetManager;
import org.destinationsol.game.planet.SolarSystem;
diff --git a/engine/src/main/java/org/destinationsol/game/screens/BuyItemsScreen.java b/engine/src/main/java/org/destinationsol/game/screens/BuyItemsScreen.java
index a17c3e846..7c4423154 100644
--- a/engine/src/main/java/org/destinationsol/game/screens/BuyItemsScreen.java
+++ b/engine/src/main/java/org/destinationsol/game/screens/BuyItemsScreen.java
@@ -17,9 +17,9 @@
package org.destinationsol.game.screens;
import org.destinationsol.SolApplication;
-import org.destinationsol.game.FactionInfo;
import org.destinationsol.game.Hero;
import org.destinationsol.game.SolGame;
+import org.destinationsol.game.faction.DefaultReputationEvent;
import org.destinationsol.game.item.ItemContainer;
import org.destinationsol.game.item.SolItem;
import org.destinationsol.game.ship.SolShip;
@@ -54,7 +54,7 @@ public void initialise(SolApplication solApplication, InventoryScreen inventoryS
target.getTradeContainer().getItems().remove(selectedItem);
hero.getItemContainer().add(selectedItem);
hero.setMoney(hero.getMoney() - selectedItem.getPrice());
- FactionInfo.setDisposition(target.getFactionID(), 1);
+ solApplication.getGame().getFactionMan().reportEvent(hero.getFaction(), target.getFaction(), DefaultReputationEvent.BOUGHT_ITEM);
inventoryScreen.updateItemRows();
});
diff --git a/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java b/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java
index 855dbdfa5..11dfd84ce 100644
--- a/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java
+++ b/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java
@@ -46,7 +46,6 @@ public class MainGameScreen extends SolUiBaseScreen {
static final float HELPER_ROW_1 = 1 - 3f * CELL_SZ;
private final ShipUiControl shipControl;
- private final SolUiControl pauseControl;
private final CameraKeyboardControl cameraControl;
private final SolApplication solApplication;
@@ -75,8 +74,6 @@ public class MainGameScreen extends SolUiBaseScreen {
break;
}
- pauseControl = new SolUiControl(null, true, gameOptions.getKeyPause());
- controls.add(pauseControl);
cameraControl = new CameraKeyboardControl(gameOptions, controls);
}
@@ -125,14 +122,9 @@ public void updateCustom(SolApplication solApplication, SolInputManager.InputPoi
if (solApplication.getNuiManager().hasScreenOfType(ConsoleScreen.class)) {
controls.forEach(x -> x.setEnabled(false));
} else if (!nuiManager.hasScreen(screens.menuScreen)) {
- game.setPaused(false);
controls.forEach(x -> x.setEnabled(true));
}
- if (pauseControl.isJustOff()) {
- game.setPaused(!game.isPaused());
- }
-
for (SolUiScreen screen : gameOverlayScreens) {
screen.updateCustom(solApplication, inputPointers, clickedOutside);
}
diff --git a/engine/src/main/java/org/destinationsol/game/ship/Door.java b/engine/src/main/java/org/destinationsol/game/ship/Door.java
index 28a095f08..5311c084b 100644
--- a/engine/src/main/java/org/destinationsol/game/ship/Door.java
+++ b/engine/src/main/java/org/destinationsol/game/ship/Door.java
@@ -22,12 +22,12 @@
import com.badlogic.gdx.physics.box2d.joints.PrismaticJoint;
import org.destinationsol.assets.sound.SpecialSounds;
import org.destinationsol.common.SolMath;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.FactionManager;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.SolObject;
import org.destinationsol.game.drawables.Drawable;
import org.destinationsol.game.drawables.RectSprite;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.input.Pilot;
import java.util.ArrayList;
diff --git a/engine/src/main/java/org/destinationsol/game/ship/ForceBeacon.java b/engine/src/main/java/org/destinationsol/game/ship/ForceBeacon.java
index da3fa64cb..a97ba7c79 100644
--- a/engine/src/main/java/org/destinationsol/game/ship/ForceBeacon.java
+++ b/engine/src/main/java/org/destinationsol/game/ship/ForceBeacon.java
@@ -19,10 +19,10 @@
import com.badlogic.gdx.math.Vector2;
import org.destinationsol.assets.sound.SpecialSounds;
import org.destinationsol.common.SolMath;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.SolObject;
import org.destinationsol.game.drawables.Drawable;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.input.Pilot;
import org.destinationsol.game.particle.DSParticleEmitter;
diff --git a/engine/src/main/java/org/destinationsol/game/ship/ShipBuilder.java b/engine/src/main/java/org/destinationsol/game/ship/ShipBuilder.java
index 94a180e89..3af3eb624 100644
--- a/engine/src/main/java/org/destinationsol/game/ship/ShipBuilder.java
+++ b/engine/src/main/java/org/destinationsol/game/ship/ShipBuilder.java
@@ -33,7 +33,6 @@
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
import org.destinationsol.game.CollisionMeshLoader;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.GameColors;
import org.destinationsol.game.RemoveController;
import org.destinationsol.game.SolGame;
@@ -41,6 +40,7 @@
import org.destinationsol.game.drawables.DrawableLevel;
import org.destinationsol.game.drawables.RectSprite;
import org.destinationsol.game.drawables.SpriteManager;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.gun.GunMount;
import org.destinationsol.game.input.Pilot;
import org.destinationsol.game.item.Armor;
@@ -85,10 +85,16 @@ public FarShip buildNewFar(SolGame game, Vector2 position, Vector2 velocity, flo
game.getItemMan().fillContainer(itemContainer, items);
Engine.Config ec = hullConfig.getEngineConfig();
Engine ei = ec == null ? null : ec.exampleEngine.copy();
+ // A ship's default engine is defined in its hull config.
+ // Engines are also regular items, so add the default to the ship's item container.
+ if (ei != null) {
+ itemContainer.add(ei);
+ }
TradeContainer tc = tradeConfig == null ? null : new TradeContainer(tradeConfig);
Gun g1 = null;
Gun g2 = null;
+ Engine engine = ei;
Shield shield = null;
Armor armor = null;
@@ -96,6 +102,12 @@ public FarShip buildNewFar(SolGame game, Vector2 position, Vector2 velocity, flo
if (pilot.isPlayer()) {
for (List group : itemContainer) {
for (SolItem i : group) {
+ if (i instanceof Engine) {
+ if (i.isEquipped() > 0) {
+ engine = (Engine) i;
+ continue;
+ }
+ }
if (i instanceof Shield) {
if (i.isEquipped() > 0) {
shield = (Shield) i;
@@ -154,15 +166,16 @@ public FarShip buildNewFar(SolGame game, Vector2 position, Vector2 velocity, flo
}
if (giveAmmo) {
- addAbilityCharges(itemContainer, hullConfig, pilot);
- addAmmo(itemContainer, g1, pilot);
- addAmmo(itemContainer, g2, pilot);
+ boolean isPlayerAlly = game.getFactionMan().getPlayerFaction().getRelation(pilot.getFaction()) >= 0;
+ addAbilityCharges(isPlayerAlly, itemContainer, hullConfig, pilot);
+ addAmmo(isPlayerAlly, itemContainer, g1, pilot);
+ addAmmo(isPlayerAlly, itemContainer, g2, pilot);
}
return new FarShip(new Vector2(position), new Vector2(velocity), angle, rotationSpeed, pilot, itemContainer, hullConfig, hullConfig.getMaxLife(),
- g1, g2, removeController, ei, hasRepairer ? new ShipRepairer() : null, money, tc, shield, armor);
+ g1, g2, removeController, engine, hasRepairer ? new ShipRepairer() : null, money, tc, shield, armor);
}
- private void addAmmo(ItemContainer ic, Gun g, Pilot pilot) {
+ private void addAmmo(boolean isPlayerAlly, ItemContainer ic, Gun g, Pilot pilot) {
if (g == null) {
return;
}
@@ -172,7 +185,7 @@ private void addAmmo(ItemContainer ic, Gun g, Pilot pilot) {
return;
}
float clipUseTime = cc.size * gc.timeBetweenShots + gc.reloadTime;
- float lifeTime = pilot.getFaction() == Faction.LAANI ? AVG_ALLY_LIFE_TIME : AVG_BATTLE_TIME;
+ float lifeTime = isPlayerAlly ? AVG_ALLY_LIFE_TIME : AVG_BATTLE_TIME;
int count = 1 + (int) (lifeTime / clipUseTime) + SolRandom.randomInt(0, 2);
for (int i = 0; i < count; i++) {
if (ic.canAdd(cc.example)) {
@@ -181,7 +194,7 @@ private void addAmmo(ItemContainer ic, Gun g, Pilot pilot) {
}
}
- private void addAbilityCharges(ItemContainer ic, HullConfig hc, Pilot pilot) {
+ private void addAbilityCharges(boolean isPlayerAlly, ItemContainer ic, HullConfig hc, Pilot pilot) {
if (hc.getAbility() != null) {
SolItem ex = hc.getAbility().getChargeExample();
if (ex != null) {
@@ -189,7 +202,7 @@ private void addAbilityCharges(ItemContainer ic, HullConfig hc, Pilot pilot) {
if (pilot.isPlayer()) {
count = 3;
} else {
- float lifeTime = pilot.getFaction() == Faction.LAANI ? AVG_ALLY_LIFE_TIME : AVG_BATTLE_TIME;
+ float lifeTime = isPlayerAlly ? AVG_ALLY_LIFE_TIME : AVG_BATTLE_TIME;
count = (int) (lifeTime / hc.getAbility().getRechargeTime() * SolRandom.randomFloat(.3f, 1));
}
for (int i = 0; i < count; i++) {
@@ -215,6 +228,7 @@ public SolShip build(SolGame game, Vector2 position, Vector2 velocity, float ang
hull.setParticleEmitters(game, ship);
if (engine != null) {
+ engine.setEquipped(1);
hull.setEngine(engine);
}
if (gun1 != null) {
diff --git a/engine/src/main/java/org/destinationsol/game/ship/ShipEngine.java b/engine/src/main/java/org/destinationsol/game/ship/ShipEngine.java
index 7d509431a..c348e922c 100644
--- a/engine/src/main/java/org/destinationsol/game/ship/ShipEngine.java
+++ b/engine/src/main/java/org/destinationsol/game/ship/ShipEngine.java
@@ -38,7 +38,9 @@ public ShipEngine(Engine engine) {
public void update(float angle, SolGame game, Pilot provider, Body body, Vector2 velocity, boolean controlsEnabled,
float mass, SolShip ship) {
-
+ if (myItem.isEquipped() == 0) {
+ return;
+ }
boolean working = applyInput(game, angle, provider, body, velocity, controlsEnabled, mass);
game.getPartMan().updateAllHullEmittersOfType(ship, "engine", working);
}
diff --git a/engine/src/main/java/org/destinationsol/game/ship/SolShip.java b/engine/src/main/java/org/destinationsol/game/ship/SolShip.java
index 0af4a0e68..c556a8dc4 100644
--- a/engine/src/main/java/org/destinationsol/game/ship/SolShip.java
+++ b/engine/src/main/java/org/destinationsol/game/ship/SolShip.java
@@ -26,12 +26,12 @@
import org.destinationsol.common.SolRandom;
import org.destinationsol.game.AbilityCommonConfig;
import org.destinationsol.game.DmgType;
-import org.destinationsol.game.FactionInfo;
import org.destinationsol.game.Hero;
import org.destinationsol.game.RemoveController;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.SolObject;
import org.destinationsol.game.drawables.Drawable;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.gun.GunMount;
import org.destinationsol.game.input.Pilot;
import org.destinationsol.game.item.Armor;
@@ -62,8 +62,6 @@ public class SolShip implements SolObject {
private static final float ENERGY_DMG_FACTOR = .7f;
private boolean colliding;
- private String factionName;
- private int factionID;
private final Pilot myPilot;
private final ItemContainer myItemContainer;
private final TradeContainer myTradeContainer;
@@ -112,8 +110,9 @@ public SolShip(SolGame game, Pilot pilot, Hull hull, RemoveController removeCont
if (myAbility != null) {
myAbilityAwait = myAbility.getConfig().getRechargeTime();
}
- factionID = FactionInfo.getFactionID(this);
- factionName = FactionInfo.getFactionNames().get(factionID).toString();
+ if (myHull.getEngine() != null && !myItemContainer.contains(myHull.getEngine())) {
+ myItemContainer.add(myHull.getEngine());
+ }
}
@Override
@@ -241,12 +240,6 @@ public void update(SolGame game) {
updateIdleTime(game);
updateShield(game);
- if (!isMerc && FactionInfo.getDisposition().get(factionID) < -5) {
- getPilot().stringToFaction("ehar");
- } else {
- getPilot().stringToFaction("laani");
- }
-
if (myArmor != null && !myItemContainer.contains(myArmor)) {
myArmor = null;
}
@@ -537,13 +530,11 @@ public boolean maybeEquip(SolGame game, SolItem item, boolean equip) {
public boolean maybeEquip(SolGame game, SolItem item, boolean secondarySlot, boolean equip) {
if (!secondarySlot) {
if (item instanceof Engine) {
- Gdx.app.log("SolShip", "maybeEquip called for an engine item, can't do that!");
- //throw new AssertionError("engine items not supported");
-
Engine ei = (Engine) item;
boolean ok = ei.isBig() == (myHull.config.getType() == HullConfig.Type.BIG);
if (ok && equip) {
myHull.setEngine(ei);
+ ei.setEquipped(1);
}
return ok;
}
@@ -591,9 +582,8 @@ public boolean maybeUnequip(SolGame game, SolItem item, boolean unequip) {
public boolean maybeUnequip(SolGame game, SolItem item, boolean secondarySlot, boolean unequip) {
if (!secondarySlot) {
if (myHull.getEngine() == item) {
- Gdx.app.log("SolShip", "maybeUnequip called for an engine item, can't do that!");
- //throw new AssertionError("engine items not supported");
if (unequip) {
+ item.setEquipped(0);
myHull.setEngine(null);
}
return true;
@@ -664,6 +654,9 @@ public boolean isControlsEnabled() {
public void dropItem(SolGame game, SolItem item) {
myItemContainer.remove(item);
+ if (item.isEquipped() > 0) {
+ maybeUnequip(game, item, true);
+ }
throwLoot(game, item, false);
}
@@ -689,15 +682,7 @@ public boolean isMerc() {
return this.isMerc;
}
- public String getFactionName() {
- return factionName;
- }
-
- public int getFactionID() {
- return factionID;
- }
-
- public void changeDisposition(int id) {
- FactionInfo.setDisposition(id, -1);
+ public Faction getFaction() {
+ return myPilot.getFaction();
}
}
diff --git a/engine/src/main/java/org/destinationsol/game/ship/Teleport.java b/engine/src/main/java/org/destinationsol/game/ship/Teleport.java
index 66e972746..c5facf2b4 100644
--- a/engine/src/main/java/org/destinationsol/game/ship/Teleport.java
+++ b/engine/src/main/java/org/destinationsol/game/ship/Teleport.java
@@ -24,8 +24,8 @@
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
import org.destinationsol.game.AbilityCommonConfig;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolGame;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.item.ItemManager;
import org.destinationsol.game.item.SolItem;
import org.destinationsol.game.planet.Planet;
diff --git a/engine/src/main/java/org/destinationsol/game/ship/hulls/Hull.java b/engine/src/main/java/org/destinationsol/game/ship/hulls/Hull.java
index 410a15e2c..d9a12a289 100644
--- a/engine/src/main/java/org/destinationsol/game/ship/hulls/Hull.java
+++ b/engine/src/main/java/org/destinationsol/game/ship/hulls/Hull.java
@@ -21,11 +21,11 @@
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import org.destinationsol.common.SolMath;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.SolCam;
import org.destinationsol.game.SolGame;
import org.destinationsol.game.drawables.Drawable;
import org.destinationsol.game.drawables.DrawableManager;
+import org.destinationsol.game.faction.Faction;
import org.destinationsol.game.gun.GunMount;
import org.destinationsol.game.input.Pilot;
import org.destinationsol.game.item.Engine;
@@ -158,7 +158,11 @@ public void onRemove(SolGame game) {
}
public void setEngine(Engine engine) {
- this.engine = new ShipEngine(engine);
+ if (engine != null) {
+ this.engine = new ShipEngine(engine);
+ } else {
+ this.engine = null;
+ }
}
public void setParticleEmitters(SolGame game, SolShip ship) {
diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java
index 488f5f362..a4615b129 100644
--- a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java
+++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java
@@ -21,6 +21,7 @@
import org.destinationsol.game.SolGame;
import org.destinationsol.game.UpdateAwareSystem;
import org.destinationsol.game.item.Armor;
+import org.destinationsol.game.item.Engine;
import org.destinationsol.game.item.Gun;
import org.destinationsol.game.item.Shield;
import org.destinationsol.game.item.SolItem;
@@ -148,6 +149,7 @@ public void start() {
}
Map, String> itemTypesExplanations = new HashMap<>();
+ itemTypesExplanations.put(Engine.class, "Engines allow your ship to move.");
itemTypesExplanations.put(Gun.class, "You can mine asteroids and attack enemies with guns.");
itemTypesExplanations.put(Armor.class, "Armour makes attacks less effective against you.");
itemTypesExplanations.put(Shield.class, "Shields absorb energy-based projectiles until depleted.");
@@ -177,6 +179,7 @@ public void start() {
"Open your inventory (" + gameOptions.getKeyInventoryName() + ")." :
"Open your inventory."),
new ItemTypesExplanationStep(itemTypesExplanations, new Class[] {
+ Engine.class,
Gun.class,
Armor.class,
Shield.class
@@ -228,7 +231,7 @@ public void start() {
solGame.get().getScreens().mapScreen.getCloseButton(),
solGame.get().getScreens().mapScreen,
"Close the map."),
- new FlyToHeroFirstWaypointStep("Fly to your waypoint."),
+ new FlyToHeroFirstWaypointStep("Fly to your waypoint.", "Create a waypoint near your ship."),
new ChangeTutorialSectionStep("Planets"),
new FlyToPlanetSellingMercenariesStep("Head towards a planet.", "Look for the planetary station."),
new ChangeTutorialSectionStep("Mercenaries"),
diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java
index 4e1eeabf3..3e6fa42a8 100644
--- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java
+++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java
@@ -20,7 +20,6 @@
import org.destinationsol.Const;
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.Hero;
import org.destinationsol.game.SolObject;
import org.destinationsol.game.input.AiPilot;
@@ -83,7 +82,7 @@ public void start() {
}
Guardian dp = new Guardian(game, enemyConfig, hero.getPilot(), hero.getPosition(), hero.getHull().getHullConfig(), 0);
- Pilot pilot = new AiPilot(dp, true, Faction.EHAR, false, null, Const.AI_DET_DIST);
+ Pilot pilot = new AiPilot(dp, true, game.getFactionMan().getGenericEnemyFaction(), false, null, Const.AI_DET_DIST);
int money = 60;
FarShip enemy = game.getShipBuilder().buildNewFar(game, enemyPosition, null,
0, 0, pilot, items,
diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java
index f218c9b0a..1a2f00292 100644
--- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java
+++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java
@@ -25,24 +25,39 @@
* A tutorial step that completes when the player ship reaches a nearby spawned waypoint.
*/
public class FlyToHeroFirstWaypointStep extends FlyToWaypointStep {
+ private final String missingWaypointMessage;
+
@Inject
protected FlyToHeroFirstWaypointStep() {
throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported.");
}
- public FlyToHeroFirstWaypointStep(String message) {
+ public FlyToHeroFirstWaypointStep(String message, String missingWaypointMessage) {
super(Vector2.Zero, message);
+ this.missingWaypointMessage = missingWaypointMessage;
}
@Override
public void start() {
- waypoint = game.getHero().getWaypoints().get(0);
- setTutorialText(message);
+ Hero hero = game.getHero();
+ if (hero.getWaypoints().isEmpty()) {
+ setTutorialText(missingWaypointMessage);
+ } else {
+ waypoint = game.getHero().getWaypoints().get(0);
+ setTutorialText(message);
+ }
}
@Override
public boolean checkComplete(float timeStep) {
Hero hero = game.getHero();
+ if (hero.getWaypoints().isEmpty()) {
+ setTutorialText(missingWaypointMessage);
+ return false;
+ } else {
+ setTutorialText(message);
+ }
+
if (!hero.getWaypoints().contains(waypoint) && hero.getWaypoints().size() > 0) {
// Change the target waypoint just in-case the player removes it.
waypoint = hero.getWaypoints().get(0);
diff --git a/engine/src/main/java/org/destinationsol/menu/MenuScreens.java b/engine/src/main/java/org/destinationsol/menu/MenuScreens.java
index 38ac08815..5d7c5e92c 100644
--- a/engine/src/main/java/org/destinationsol/menu/MenuScreens.java
+++ b/engine/src/main/java/org/destinationsol/menu/MenuScreens.java
@@ -22,6 +22,7 @@
import org.destinationsol.ui.nui.screens.mainMenu.InputMapScreen;
import org.destinationsol.ui.nui.screens.mainMenu.LoadingScreen;
import org.destinationsol.ui.nui.screens.mainMenu.MainMenuScreen;
+import org.destinationsol.ui.nui.screens.mainMenu.ModulesScreen;
import org.destinationsol.ui.nui.screens.mainMenu.NewGameScreen;
import org.destinationsol.ui.nui.screens.mainMenu.NewShipScreen;
import org.destinationsol.ui.nui.screens.mainMenu.OptionsScreen;
@@ -36,6 +37,7 @@ public class MenuScreens {
public final LoadingScreen loading;
public final NewGameScreen newGame;
public final NewShipScreen newShip;
+ public final ModulesScreen modules;
public MenuScreens(SolLayouts layouts, boolean mobile, GameOptions gameOptions, NUIManager nuiManager) {
MenuLayout menuLayout = layouts.menuLayout;
@@ -47,5 +49,6 @@ public MenuScreens(SolLayouts layouts, boolean mobile, GameOptions gameOptions,
loading = (LoadingScreen) nuiManager.createScreen("engine:loadingScreen");
newGame = (NewGameScreen) nuiManager.createScreen("engine:newGameScreen");
newShip = (NewShipScreen) nuiManager.createScreen("engine:newShipScreen");
+ modules = (ModulesScreen) nuiManager.createScreen("engine:modulesScreen");
}
}
diff --git a/engine/src/main/java/org/destinationsol/mercenary/MercenaryUtils.java b/engine/src/main/java/org/destinationsol/mercenary/MercenaryUtils.java
index 7d917f8d4..f0193c06b 100644
--- a/engine/src/main/java/org/destinationsol/mercenary/MercenaryUtils.java
+++ b/engine/src/main/java/org/destinationsol/mercenary/MercenaryUtils.java
@@ -19,7 +19,6 @@
import org.destinationsol.Const;
import org.destinationsol.common.SolMath;
import org.destinationsol.common.SolRandom;
-import org.destinationsol.game.Faction;
import org.destinationsol.game.Hero;
import org.destinationsol.game.ShipConfig;
import org.destinationsol.game.SolGame;
@@ -42,7 +41,7 @@ public class MercenaryUtils {
public static boolean createMerc(SolGame game, Hero hero, MercItem mercItem) {
ShipConfig config = mercItem.getConfig();
Guardian guardian = new Guardian(game, config.hull, hero.getPilot(), hero.getPosition(), hero.getHull().config, SolRandom.randomFloat(180));
- AiPilot pilot = new AiPilot(guardian, true, Faction.LAANI, false, "Merc", Const.AI_DET_DIST);
+ AiPilot pilot = new AiPilot(guardian, true, hero.getFaction(), false, "Merc", Const.AI_DET_DIST);
Vector2 position = getPos(game, hero, config.hull);
if (position == null) {
return false;
diff --git a/engine/src/main/java/org/destinationsol/modules/ModuleManager.java b/engine/src/main/java/org/destinationsol/modules/ModuleManager.java
index fad354ab0..741c770e4 100644
--- a/engine/src/main/java/org/destinationsol/modules/ModuleManager.java
+++ b/engine/src/main/java/org/destinationsol/modules/ModuleManager.java
@@ -220,6 +220,7 @@ public class ModuleManager implements AutoCloseable {
private final FacadeModuleConfig moduleConfig;
protected ModuleRegistry registry;
protected Module engineModule;
+ private Set builtInModules;
@Inject
public ModuleManager(BeanContext beanContext, ModuleFactory moduleFactory, ModuleRegistry moduleRegistry,
@@ -240,9 +241,12 @@ public void init() throws Exception {
File modulesRoot = moduleConfig.getModulesPath();
scanner.scan(registry, modulesRoot);
+ builtInModules = Sets.newHashSet();
+ builtInModules.add(engineModule);
+ builtInModules.add(nuiModule);
+ registry.addAll(builtInModules);
+
Set requiredModules = Sets.newHashSet();
- registry.add(engineModule);
- registry.add(nuiModule);
requiredModules.addAll(registry);
loadEnvironment(requiredModules);
@@ -253,6 +257,8 @@ public void init() throws Exception {
}
public void loadEnvironment(Set modules) {
+ modules.addAll(builtInModules);
+
StandardPermissionProviderFactory permissionFactory = new StandardPermissionProviderFactory();
for (String api : API_WHITELIST) {
permissionFactory.getBasePermissionSet().addAPIPackage(api);
@@ -288,6 +294,10 @@ public ModuleEnvironment getEnvironment() {
return environment;
}
+ public Set getBuiltInModules() {
+ return builtInModules;
+ }
+
//TODO: REMOVE THIS
public static ModuleEnvironment getEnvironmentStatic() {
return environment;
@@ -299,6 +309,10 @@ public void printAvailableModules() {
}
}
+ public ModuleRegistry getRegistry() {
+ return registry;
+ }
+
public void dispose() {
environment.close();
}
diff --git a/engine/src/main/java/org/destinationsol/ui/SolInputManager.java b/engine/src/main/java/org/destinationsol/ui/SolInputManager.java
index c520f8d94..0e50b0621 100644
--- a/engine/src/main/java/org/destinationsol/ui/SolInputManager.java
+++ b/engine/src/main/java/org/destinationsol/ui/SolInputManager.java
@@ -324,11 +324,13 @@ private void maybeFixMousePos() {
int mouseX = Gdx.input.getX();
int mouseY = Gdx.input.getY();
// TODO: look into the usefulness of this, and replace with Gdx.graphics.* with displayDimensions if nothing else
- int w = Gdx.graphics.getWidth();
- int h = Gdx.graphics.getHeight();
- mouseX = (int) MathUtils.clamp((float) mouseX, (float) 0, (float) w);
- mouseY = (int) MathUtils.clamp((float) mouseY, (float) 0, (float) h);
- Gdx.input.setCursorPosition(mouseX, mouseY);
+ int screenWidth = Gdx.graphics.getWidth();
+ int screenHeight = Gdx.graphics.getHeight();
+ if (mouseX < 0 || mouseX >= screenWidth || mouseY < 0 || mouseY >= screenHeight) {
+ mouseX = (int) MathUtils.clamp((float) mouseX, (float) 0, (float) screenWidth - 1);
+ mouseY = (int) MathUtils.clamp((float) mouseY, (float) 0, (float) screenHeight - 1);
+ Gdx.input.setCursorPosition(mouseX, mouseY);
+ }
}
private void updatePointers() {
diff --git a/engine/src/main/java/org/destinationsol/ui/SolInputProcessor.java b/engine/src/main/java/org/destinationsol/ui/SolInputProcessor.java
index 0d09036b4..d7e4079ac 100644
--- a/engine/src/main/java/org/destinationsol/ui/SolInputProcessor.java
+++ b/engine/src/main/java/org/destinationsol/ui/SolInputProcessor.java
@@ -59,6 +59,11 @@ public boolean touchUp(int screenX, int screenY, int pointer, int button) {
return false;
}
+ @Override
+ public boolean touchCancelled(int screenX, int screenY, int pointer, int button) {
+ return false;
+ }
+
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
inputManager.maybeTouchDragged(screenX, screenY);
diff --git a/engine/src/main/java/org/destinationsol/ui/nui/NUIManager.java b/engine/src/main/java/org/destinationsol/ui/nui/NUIManager.java
index f0e93e2af..922379545 100644
--- a/engine/src/main/java/org/destinationsol/ui/nui/NUIManager.java
+++ b/engine/src/main/java/org/destinationsol/ui/nui/NUIManager.java
@@ -169,7 +169,7 @@ public NUIManager(SolApplication solApplication,
UIText.DEFAULT_CURSOR_TEXTURE = whiteTexture;
// NOTE: SolApplication::addResizeSubscriber is not intended to be static, so use the instance form for compatibility
- solApplication.addResizeSubscriber(() -> resize(Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight()));
+ solApplication.addResizeSubscriber(() -> resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()));
// Mobile screen densities can vary considerably, so a large digital resolution can be displayed
// on a very small screen. Due to this, it makes sense to scale the UI roughly proportionally
diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/MainGameScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/MainGameScreen.java
index 7a9fe8a11..4eb135913 100644
--- a/engine/src/main/java/org/destinationsol/ui/nui/screens/MainGameScreen.java
+++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/MainGameScreen.java
@@ -38,6 +38,7 @@
import org.destinationsol.game.screens.BorderDrawer;
import org.destinationsol.game.screens.GameScreens;
import org.destinationsol.game.screens.ZoneNameAnnouncer;
+import org.destinationsol.game.ship.ShipAbility;
import org.destinationsol.game.ship.SolShip;
import org.destinationsol.ui.SolInputManager;
import org.destinationsol.ui.UiDrawer;
@@ -237,11 +238,16 @@ public Float get() {
@Override
public UITextureRegion get() {
Hero hero = solApplication.getGame().getHero();
- if (hero.getAbility() == null) {
+ ShipAbility ability = hero.getAbility();
+ if (ability == null) {
return null;
}
- SolItem example = hero.getAbility().getConfig().getChargeExample();
+ if (ability.getCommonConfig().icon != null) {
+ return Assets.getDSTexture(ability.getCommonConfig().icon.name).getUiTexture();
+ }
+
+ SolItem example = ability.getConfig().getChargeExample();
if (example != null) {
return Assets.getDSTexture(example.getIcon(solApplication.getGame()).name).getUiTexture();
}
@@ -378,6 +384,13 @@ public Boolean get() {
return hero.isNonTranscendent() && hero.getArmor() == null;
}
});
+ addWarnDrawer("noEngine", warnColour, "No Engine", new ReadOnlyBinding() {
+ @Override
+ public Boolean get() {
+ Hero hero = solApplication.getGame().getHero();
+ return hero.isNonTranscendent() && hero.getHull().getEngine() == null;
+ }
+ });
addWarnDrawer("enemyWarnDrawer", warnColour, "Dangerous Enemy", new ReadOnlyBinding() {
@Override
public Boolean get() {
diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java
index 34ecb7759..d72e13293 100644
--- a/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java
+++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java
@@ -20,12 +20,14 @@
import org.destinationsol.SolApplication;
import org.destinationsol.assets.music.OggMusicManager;
import org.destinationsol.game.WorldConfig;
+import org.destinationsol.modules.ModuleManager;
import org.destinationsol.ui.nui.NUIManager;
import org.destinationsol.ui.nui.NUIScreenLayer;
import org.terasology.nui.Canvas;
import org.terasology.nui.widgets.UIButton;
import javax.inject.Inject;
+import java.util.HashSet;
/**
* The main menu screen. This is the first screen shown when you open the game.
@@ -33,18 +35,22 @@
public class MainMenuScreen extends NUIScreenLayer {
private final SolApplication solApplication;
+ private final ModuleManager moduleManager;
private UIButton tutorialButton;
@Inject
- public MainMenuScreen(SolApplication solApplication) {
+ public MainMenuScreen(SolApplication solApplication, ModuleManager moduleManager) {
this.solApplication = solApplication;
+ this.moduleManager = moduleManager;
}
@Override
public void initialise() {
tutorialButton = find("tutorialButton", UIButton.class);
tutorialButton.subscribe(button -> {
- solApplication.getMenuScreens().loading.setMode(true, "Imperial Small", true, new WorldConfig());
+ WorldConfig worldConfig = new WorldConfig();
+ worldConfig.setModules(new HashSet<>(moduleManager.getEnvironment().getModulesOrderedByDependencies()));
+ solApplication.getMenuScreens().loading.setMode(true, "Imperial Small", true, worldConfig);
nuiManager.setScreen(solApplication.getMenuScreens().loading);
});
diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/ModulesScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/ModulesScreen.java
new file mode 100644
index 000000000..7c0119f04
--- /dev/null
+++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/ModulesScreen.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2022 The Terasology Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.destinationsol.ui.nui.screens.mainMenu;
+
+import org.destinationsol.SolApplication;
+import org.destinationsol.modules.ModuleManager;
+import org.destinationsol.ui.nui.NUIScreenLayer;
+import org.terasology.gestalt.module.Module;
+import org.terasology.nui.databinding.ReadOnlyBinding;
+import org.terasology.nui.itemRendering.StringTextRenderer;
+import org.terasology.nui.widgets.UIButton;
+import org.terasology.nui.widgets.UIList;
+
+import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This screen is used to select the modules that should be active when playing a particular save.
+ * You can activate and de-activate modules only when initially creating a game.
+ * This is to prevent side-effects from new modules being introduced unexpectedly.
+ */
+public class ModulesScreen extends NUIScreenLayer {
+ private final SolApplication solApplication;
+ private final ModuleManager moduleManager;
+ private Set selectedModules;
+
+ @Inject
+ public ModulesScreen(SolApplication solApplication, ModuleManager moduleManager) {
+ this.solApplication = solApplication;
+ this.moduleManager = moduleManager;
+ }
+
+ @Override
+ public void initialise() {
+ selectedModules = new HashSet<>();
+
+ UIList moduleList = find("modulesList", UIList.class);
+ List modules = new ArrayList<>(moduleManager.getEnvironment().getModulesOrderedByDependencies());
+ modules.removeAll(moduleManager.getBuiltInModules());
+ moduleList.setList(modules);
+ moduleList.setItemRenderer(new StringTextRenderer() {
+ @Override
+ public String getString(Module value) {
+ if (!selectedModules.contains(value)) {
+ return value.getId().toString();
+ } else {
+ return value.getId().toString() + " (Active)";
+ }
+ }
+ });
+ moduleList.subscribe((list, module) -> {
+ if (selectedModules.contains(module)) {
+ selectedModules.remove(module);
+ } else {
+ selectedModules.add(module);
+ }
+ });
+
+ UIButton activateButton = find("activateButton", UIButton.class);
+ activateButton.bindEnabled(new ReadOnlyBinding() {
+ @Override
+ public Boolean get() {
+ Module selectedModule = moduleList.getSelection();
+ return selectedModule != null && !selectedModules.contains(selectedModule);
+ }
+ });
+ activateButton.subscribe(button -> selectedModules.add(moduleList.getSelection()));
+
+ UIButton deactivateButton = find("deactivateButton", UIButton.class);
+ deactivateButton.bindEnabled(new ReadOnlyBinding() {
+ @Override
+ public Boolean get() {
+ Module selectedModule = moduleList.getSelection();
+ return selectedModule != null && selectedModules.contains(selectedModule);
+ }
+ });
+ deactivateButton.subscribe(button -> selectedModules.remove(moduleList.getSelection()));
+
+ UIButton confirmButton = find("confirmButton", UIButton.class);
+ confirmButton.subscribe(button -> {
+ nuiManager.setScreen(solApplication.getMenuScreens().newShip);
+ });
+ }
+
+ public Set getSelectedModules() {
+ return selectedModules;
+ }
+
+ public void setSelectedModules(Set selectedModules) {
+ this.selectedModules = selectedModules;
+ }
+}
diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/NewShipScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/NewShipScreen.java
index a9d0e1b6c..d2b6dcdc4 100644
--- a/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/NewShipScreen.java
+++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/NewShipScreen.java
@@ -21,6 +21,7 @@
import org.destinationsol.assets.json.Validator;
import org.destinationsol.game.WorldConfig;
import org.destinationsol.game.planet.SystemsBuilder;
+import org.destinationsol.modules.ModuleManager;
import org.destinationsol.ui.nui.NUIManager;
import org.destinationsol.ui.nui.NUIScreenLayer;
import org.destinationsol.ui.nui.widgets.KeyActivatedButton;
@@ -28,6 +29,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.gestalt.assets.ResourceUrn;
+import org.terasology.gestalt.module.Module;
+import org.terasology.gestalt.naming.Name;
import org.terasology.nui.Canvas;
import org.terasology.nui.UITextureRegion;
import org.terasology.nui.backends.libgdx.GDXInputUtil;
@@ -36,32 +39,41 @@
import javax.inject.Inject;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
+import java.util.Set;
public class NewShipScreen extends NUIScreenLayer {
private static final Logger logger = LoggerFactory.getLogger(NewShipScreen.class);
private final SolApplication solApplication;
- private int numberOfSystems = SystemsBuilder.DEFAULT_SYSTEM_COUNT;
+ private final ModuleManager moduleManager;
private int playerSpawnConfigIndex = 0;
private List playerSpawnConfigNames = new ArrayList<>();
private List playerSpawnConfigTextures = new ArrayList<>();
+ private WorldConfig worldConfig;
@Inject
- public NewShipScreen(SolApplication solApplication) {
+ public NewShipScreen(SolApplication solApplication, ModuleManager moduleManager) {
this.solApplication = solApplication;
+ this.moduleManager = moduleManager;
}
@Override
public void initialise() {
+ worldConfig = new WorldConfig();
+ worldConfig.setNumberOfSystems(SystemsBuilder.DEFAULT_SYSTEM_COUNT);
+ worldConfig.setModules(new HashSet<>(moduleManager.getEnvironment().getModulesOrderedByDependencies()));
+
UIButton systemsButton = find("systemsButton", UIButton.class);
- systemsButton.setText("Systems: " + numberOfSystems);
+ systemsButton.setText("Systems: " + worldConfig.getNumberOfSystems());
systemsButton.subscribe(button -> {
- int systemCount = (numberOfSystems + 1) % 10;
+ int systemCount = (worldConfig.getNumberOfSystems() + 1) % 10;
if (systemCount < 2) {
systemCount = 2;
}
- numberOfSystems = systemCount;
- ((UIButton)button).setText("Systems: " + numberOfSystems);
+ worldConfig.setNumberOfSystems(systemCount);
+ ((UIButton)button).setText("Systems: " + worldConfig.getNumberOfSystems());
});
for (ResourceUrn configUrn : Assets.getAssetHelper().listAssets(Json.class, "playerSpawnConfig")) {
@@ -90,13 +102,17 @@ public void initialise() {
shipPreviewImage.setImage(playerSpawnConfigTextures.get(playerSpawnConfigIndex));
});
+ UIButton modulesButton = find("modulesButton", UIButton.class);
+ modulesButton.subscribe(button -> {
+ ModulesScreen modulesScreen = solApplication.getMenuScreens().modules;
+ modulesScreen.setSelectedModules(worldConfig.getModules());
+ nuiManager.setScreen(modulesScreen);
+ });
+
// NOTE: The original code used getKeyEscape() for both the "OK" and "Cancel" buttons. This was probably a mistake.
KeyActivatedButton okButton = find("okButton", KeyActivatedButton.class);
okButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeyShoot()));
okButton.subscribe(button -> {
- WorldConfig worldConfig = new WorldConfig();
- worldConfig.setNumberOfSystems(numberOfSystems);
-
LoadingScreen loadingScreen = solApplication.getMenuScreens().loading;
loadingScreen.setMode(false, playerSpawnConfigNames.get(playerSpawnConfigIndex), true, worldConfig);
nuiManager.setScreen(loadingScreen);
@@ -109,6 +125,28 @@ public void initialise() {
});
}
+ @Override
+ public void onAdded() {
+ worldConfig.setSeed(System.currentTimeMillis());
+
+ String currentShip = playerSpawnConfigNames.get(playerSpawnConfigIndex);
+ playerSpawnConfigNames.clear();
+ Set configUrns = Assets.getAssetHelper().listAssets(Json.class, "playerSpawnConfig");
+ for (Module module : worldConfig.getModules()) {
+ ResourceUrn configUrn = new ResourceUrn(module.getId(), new Name("playerSpawnConfig"));
+ if (configUrns.contains(configUrn)) {
+ playerSpawnConfigNames.addAll(Validator.getValidatedJSON(configUrn.toString(), "engine:schemaPlayerSpawnConfig").keySet());
+ }
+ }
+
+ if (!playerSpawnConfigNames.contains(currentShip)) {
+ // The player picked a ship that's now invalid, so reset their selection.
+ playerSpawnConfigIndex = 0;
+ UIButton startingShipButton = find("startingShipButton", UIButton.class);
+ startingShipButton.setText("Starting Ship: " + playerSpawnConfigNames.get(playerSpawnConfigIndex));
+ }
+ }
+
@Override
public void update(float delta) {
super.update(delta);
diff --git a/engine/src/main/resources/org/destinationsol/assets/configs/factions.json b/engine/src/main/resources/org/destinationsol/assets/configs/factions.json
new file mode 100644
index 000000000..c902ad9dd
--- /dev/null
+++ b/engine/src/main/resources/org/destinationsol/assets/configs/factions.json
@@ -0,0 +1,5 @@
+{
+ "engine:player": {},
+ "engine:laani": {},
+ "engine:ehar": {}
+}
\ No newline at end of file
diff --git a/engine/src/main/resources/org/destinationsol/assets/factions/ehar.json b/engine/src/main/resources/org/destinationsol/assets/factions/ehar.json
new file mode 100644
index 000000000..2c57b8a89
--- /dev/null
+++ b/engine/src/main/resources/org/destinationsol/assets/factions/ehar.json
@@ -0,0 +1,7 @@
+{
+ "name": "Ehar",
+ "description": "A generic all-bad faction. The forerunner to all hostile entities.",
+ "colour": "b0c4de",
+ "defaultDisposition": -100,
+ "shipDesigns": []
+}
\ No newline at end of file
diff --git a/engine/src/main/resources/org/destinationsol/assets/factions/laani.json b/engine/src/main/resources/org/destinationsol/assets/factions/laani.json
new file mode 100644
index 000000000..41b70e139
--- /dev/null
+++ b/engine/src/main/resources/org/destinationsol/assets/factions/laani.json
@@ -0,0 +1,7 @@
+{
+ "name": "Laani",
+ "description": "A generic all-good faction. The forerunner to all friendly entities.",
+ "colour": "b0c4de",
+ "defaultDisposition": 100,
+ "shipDesigns": []
+}
\ No newline at end of file
diff --git a/engine/src/main/resources/org/destinationsol/assets/factions/player.json b/engine/src/main/resources/org/destinationsol/assets/factions/player.json
new file mode 100644
index 000000000..7b1181e79
--- /dev/null
+++ b/engine/src/main/resources/org/destinationsol/assets/factions/player.json
@@ -0,0 +1,7 @@
+{
+ "name": "Player",
+ "description": "This is you.",
+ "colour": "b0c4de",
+ "defaultDisposition": 0,
+ "shipDesigns": []
+}
\ No newline at end of file
diff --git a/engine/src/main/resources/org/destinationsol/assets/schemas/schemaAbilitiesConfig.json b/engine/src/main/resources/org/destinationsol/assets/schemas/schemaAbilitiesConfig.json
index 6fcc9a187..63bb87746 100644
--- a/engine/src/main/resources/org/destinationsol/assets/schemas/schemaAbilitiesConfig.json
+++ b/engine/src/main/resources/org/destinationsol/assets/schemas/schemaAbilitiesConfig.json
@@ -11,6 +11,11 @@
"activatedSound"
],
"properties": {
+ "icon": {
+ "type": "string",
+ "description": "The gestalt id of the icon representing this ability.",
+ "pattern": "^\\w+:\\w+$"
+ },
"activatedSound": {
"type": "string",
"description": "The gestalt id of the sound effect played upon ability activation.",
diff --git a/engine/src/main/resources/org/destinationsol/assets/schemas/schemaFaction.json b/engine/src/main/resources/org/destinationsol/assets/schemas/schemaFaction.json
new file mode 100644
index 000000000..c9eae9600
--- /dev/null
+++ b/engine/src/main/resources/org/destinationsol/assets/schemas/schemaFaction.json
@@ -0,0 +1,38 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "description": "Configuration for an in-game faction and the members of the faction.",
+ "required": ["name", "description", "colour", "shipDesigns"],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the faction."
+ },
+ "description": {
+ "type": "string",
+ "description": "A string description the faction. This may include an overview of the faction's background."
+ },
+ "colour": {
+ "type": "string",
+ "description": "Faction colours are hexadecimal, in the form RRGGBB(AA).",
+ "pattern": "^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6})$"
+ },
+ "shipDesigns": {
+ "type": "array",
+ "description": "A list of ship designs that can be produced by this faction.",
+ "items": {
+ "type": "string",
+ "description": "The ResourceUrn of a particular ship.",
+ "pattern": "^\\w+:\\w+$"
+ }
+ },
+ "defaultDisposition": {
+ "type": "integer",
+ "description": "The default disposition that the faction has towards other unknown factions."
+ },
+ "relations": {
+ "type": "object",
+ "additionalProperties": { "type": "integer" },
+ "description": "Relations between factions."
+ }
+ }
+}
diff --git a/engine/src/main/resources/org/destinationsol/assets/schemas/schemaFactions.json b/engine/src/main/resources/org/destinationsol/assets/schemas/schemaFactions.json
index 64519d114..45ab0bd3e 100644
--- a/engine/src/main/resources/org/destinationsol/assets/schemas/schemaFactions.json
+++ b/engine/src/main/resources/org/destinationsol/assets/schemas/schemaFactions.json
@@ -1,43 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Configuration for in-game factions and the members of the factions.",
- "properties": {
- "factions": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string",
- "description": "Faction names can be any non-blank string of ascii characters.",
- "pattern": "^[ -~]+$"
- },
- "color": {
- "type": "string",
- "description": "Faction colors are hexidecimal, in the form RRGGBB(AA).",
- "pattern": "^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6})$"
- },
- "disposition": {
- "type": "integer",
- "description": "The opinion of the faction towards the player. Positive is friendly, negative is hostile. Faction will attack player when disposition < 0."
- },
- "ships": {
- "type": "array",
- "description": "The list of ships in this faction.",
- "items": {
- "type": "string",
- "description": "The string Id of this ship. Can be any sequence of basic alphanumeric characters without a space.",
- "pattern": "^\\w+$"
- }
- }
- },
- "required": [
- "name",
- "color",
- "disposition",
- "ships"
- ]
- }
- }
- }
+ "additionalProperties": { "type": "object" }
}
diff --git a/engine/src/main/resources/org/destinationsol/assets/skins/mainMenu.skin b/engine/src/main/resources/org/destinationsol/assets/skins/mainMenu.skin
index 685ba8726..bcb6b5703 100644
--- a/engine/src/main/resources/org/destinationsol/assets/skins/mainMenu.skin
+++ b/engine/src/main/resources/org/destinationsol/assets/skins/mainMenu.skin
@@ -61,6 +61,9 @@
"creditsButton": {
"font": "engine:main#0.60"
},
+ "modulesButton": {
+ "font": "engine:main#0.55"
+ },
"menuHeaderText": {
"font": "engine:main#1.0"
},
diff --git a/engine/src/main/resources/org/destinationsol/assets/textures/icons/iconEngine.png b/engine/src/main/resources/org/destinationsol/assets/textures/icons/iconEngine.png
new file mode 100644
index 000000000..43738dc09
Binary files /dev/null and b/engine/src/main/resources/org/destinationsol/assets/textures/icons/iconEngine.png differ
diff --git a/engine/src/main/resources/org/destinationsol/assets/ui/mainMenu/modulesScreen.ui b/engine/src/main/resources/org/destinationsol/assets/ui/mainMenu/modulesScreen.ui
new file mode 100644
index 000000000..ff3ef21bf
--- /dev/null
+++ b/engine/src/main/resources/org/destinationsol/assets/ui/mainMenu/modulesScreen.ui
@@ -0,0 +1,106 @@
+{
+ "type": "ModulesScreen",
+ "skin": "engine:mainMenu",
+ "contents": {
+ "type": "RelativeLayout",
+ "contents": [
+ {
+ "type": "UILabel",
+ "id": "headerText",
+ "family": "menuHeaderText",
+ "text": "Modules",
+ "layoutInfo": {
+ "position-horizontal-center": {},
+ "position-top": {
+ "target": "TOP",
+ "offset": 32
+ },
+ "use-content-height": true
+ }
+ },
+ {
+ "type": "ColumnLayout",
+ "id": "moduleSelectLayout",
+ "columns": 2,
+ "column-widths": [0.7, 0.3],
+ "horizontalSpacing": 16,
+ "contents": [
+ {
+ "type": "ScrollableArea",
+ "content": {
+ "type": "UIList",
+ "id": "modulesList",
+ "family": "menuButtons"
+ }
+ },
+ {
+ "type": "RelativeLayout",
+ "id": "moduleActionButtons",
+ "contents": [
+ {
+ "type": "UIButton",
+ "id": "activateButton",
+ "text": "Activate Module",
+ "layoutInfo": {
+ "position-bottom": {
+ "target": "MIDDLE",
+ "offset": 5
+ },
+ "use-content-height": true
+ }
+ },
+ {
+ "type": "UIButton",
+ "id": "deactivateButton",
+ "text": "Deactivate Module",
+ "layoutInfo": {
+ "position-top": {
+ "target": "MIDDLE",
+ "offset": 5
+ },
+ "use-content-height": true
+ }
+ }
+ ]
+ }
+ ],
+ "layoutInfo": {
+ "position-horizontal-center": {},
+ "position-bottom": {
+ "widget": "confirmButton",
+ "target": "TOP",
+ "offset": 16
+ },
+ "position-top": {
+ "widget": "headerText",
+ "target": "BOTTOM",
+ "offset": 16
+ },
+ "position-left": {
+ "offset": 16
+ },
+ "position-right": {
+ "offset": 16
+ }
+ }
+ },
+ {
+ "type": "UIButton",
+ "id": "confirmButton",
+ "text": "Confirm",
+ "layoutInfo": {
+ "position-bottom": {
+ "offset": 32
+ },
+ "position-left": {
+ "offset": 32
+ },
+ "position-right": {
+ "offset": 32
+ },
+ "use-content-height": true
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/resources/org/destinationsol/assets/ui/mainMenu/newShipScreen.ui b/engine/src/main/resources/org/destinationsol/assets/ui/mainMenu/newShipScreen.ui
index 36e8d2454..eed5dd8d8 100644
--- a/engine/src/main/resources/org/destinationsol/assets/ui/mainMenu/newShipScreen.ui
+++ b/engine/src/main/resources/org/destinationsol/assets/ui/mainMenu/newShipScreen.ui
@@ -74,6 +74,18 @@
"use-content-height": true,
"use-content-width": true
}
+ },
+ {
+ "type": "UIButton",
+ "id": "modulesButton",
+ "family": "modulesButton",
+ "text": "Modules",
+ "layoutInfo": {
+ "position-right": {},
+ "position-bottom": {},
+ "width": 100,
+ "height": 50
+ }
}
]
}
diff --git a/gradle/wrapper/groovy-wrapper.jar b/gradle/wrapper/groovy-wrapper.jar
index 7fadbd515..c17384dc4 100644
Binary files a/gradle/wrapper/groovy-wrapper.jar and b/gradle/wrapper/groovy-wrapper.jar differ
diff --git a/launcher/solOSX.sh b/launcher/solOSX.sh
index 4e8d39fbc..db68ba3ac 100755
--- a/launcher/solOSX.sh
+++ b/launcher/solOSX.sh
@@ -1,4 +1,14 @@
#!/usr/bin/env bash
-# TODO: Target the embedded JRE again when it'll listen to arguments or otherwise find the magic trick to allow that
-#lwjreOSX/bin/java -XstartOnFirstThread -jar libs/solDesktop.jar -noSplash
-java -XstartOnFirstThread -jar libs/solDesktop.jar -noSplash
+
+JRE=lwjreOSX/bin/java
+ARCH=$(uname -m)
+if [[ "$ARCH" == "x86_64" ]]; then
+ JRE=lwjreOSX/bin/java
+elif [[ "$ARCH" == "arm64" ]]; then
+ JRE=lwjreOSXArm/bin/java
+else
+ echo "Unsupported architecture $ARCH"
+ exit 1
+fi
+
+$JRE -XstartOnFirstThread -jar libs/solDesktop.jar -noSplash
\ No newline at end of file
diff --git a/modules/core/assets/configs/factions.json b/modules/core/assets/configs/factions.json
index 5d547f6f0..89b931f43 100644
--- a/modules/core/assets/configs/factions.json
+++ b/modules/core/assets/configs/factions.json
@@ -1,73 +1,8 @@
{
- "factions": [
- {
- "name": "Imperial",
- "color": "b0c4de",
- "disposition": 50,
- "ships": [
- "imperialBig",
- "imperialCapital",
- "imperialMedium",
- "imperialSmall",
- "imperialTiny",
- "station"
- ]
- },
- {
- "name": "Pirate",
- "color": "DC143C",
- "disposition": -100,
- "ships": [
- "pirateMedium",
- "pirateOrbiter",
- "piratePlanetTurret",
- "pirateSmall",
- "pirateSpaceTurret",
- "truck"
- ]
- },
- {
- "name": "Techie",
- "color": "169E10",
- "disposition": -50,
- "ships": [
- "techieOrbiter",
- "techiePlanetTurret",
- "techieSmall"
- ]
- },
- {
- "name": "Miner",
- "color": "FFFF33",
- "disposition":0,
- "ships": [
- "minerBoss",
- "minerMedium",
- "minerSmall",
- "minerTurret"
- ]
- },
- {
- "name": "Desert",
- "color": "624A2E",
- "disposition": -50,
- "ships": [
- "desertBoss",
- "desertMedium",
- "desertPlanetTurret",
- "desertSmall",
- "desertSpaceTurret",
- "desertOrbiter"
- ]
- },
- {
- "name": "Trader",
- "color": "006400",
- "disposition": 50,
- "ships": [
- "bus",
- "drome"
- ]
- }
- ]
+ "core:imperial": {},
+ "core:pirate": {},
+ "core:techie": {},
+ "core:miner": {},
+ "core:desert": {},
+ "core:trader": {}
}
diff --git a/modules/core/assets/configs/systemsConfig.json b/modules/core/assets/configs/systemsConfig.json
index be83747bb..4a2da2a2d 100644
--- a/modules/core/assets/configs/systemsConfig.json
+++ b/modules/core/assets/configs/systemsConfig.json
@@ -80,7 +80,7 @@
{
"hull": "core:pirateMedium",
- "items": "core:gun core:miner 0.25|core:lightArmor core:bigShield",
+ "items": "core:gun core:minerGun 0.25|core:lightArmor core:bigShield",
"money": 200,
"density": 0.0002
},
diff --git a/modules/core/assets/factions/desert.json b/modules/core/assets/factions/desert.json
new file mode 100644
index 000000000..9024e4b50
--- /dev/null
+++ b/modules/core/assets/factions/desert.json
@@ -0,0 +1,14 @@
+{
+ "name": "Desert",
+ "description": "Masters of camouflage. They also have a strange affinity for mazes.",
+ "colour": "624A2E",
+ "defaultDisposition": -50,
+ "shipDesigns": [
+ "core:desertBoss",
+ "core:desertMedium",
+ "core:desertPlanetTurret",
+ "core:desertSmall",
+ "core:desertSpaceTurret",
+ "core:desertOrbiter"
+ ]
+}
\ No newline at end of file
diff --git a/modules/core/assets/factions/imperial.json b/modules/core/assets/factions/imperial.json
new file mode 100644
index 000000000..7e0ca8076
--- /dev/null
+++ b/modules/core/assets/factions/imperial.json
@@ -0,0 +1,14 @@
+{
+ "name": "Imperial",
+ "description": "The Imperial faction has reigned for millennia. Their might is barely contested. Offend at your peril.",
+ "colour": "b0c4de",
+ "defaultDisposition": 50,
+ "shipDesigns": [
+ "core:imperialBig",
+ "core:imperialCapital",
+ "core:imperialMedium",
+ "core:imperialSmall",
+ "core:imperialTiny",
+ "core:station"
+ ]
+}
\ No newline at end of file
diff --git a/modules/core/assets/factions/miner.json b/modules/core/assets/factions/miner.json
new file mode 100644
index 000000000..11a9f170d
--- /dev/null
+++ b/modules/core/assets/factions/miner.json
@@ -0,0 +1,12 @@
+{
+ "name": "Miner",
+ "description": "They break space rocks. Somebody's got to do it.",
+ "colour": "FFFF33",
+ "defaultDisposition": 0,
+ "shipDesigns": [
+ "core:minerBoss",
+ "core:minerMedium",
+ "core:minerSmall",
+ "core:minerTurret"
+ ]
+}
\ No newline at end of file
diff --git a/modules/core/assets/factions/pirate.json b/modules/core/assets/factions/pirate.json
new file mode 100644
index 000000000..33974b436
--- /dev/null
+++ b/modules/core/assets/factions/pirate.json
@@ -0,0 +1,14 @@
+{
+ "name": "Pirate",
+ "description": "Pirates. They steal stuff.",
+ "colour": "DC143C",
+ "defaultDisposition": -100,
+ "shipDesigns": [
+ "core:pirateMedium",
+ "core:pirateOrbiter",
+ "core:piratePlanetTurret",
+ "core:pirateSmall",
+ "core:pirateSpaceTurret",
+ "core:truck"
+ ]
+}
\ No newline at end of file
diff --git a/modules/core/assets/factions/techie.json b/modules/core/assets/factions/techie.json
new file mode 100644
index 000000000..8d5b70041
--- /dev/null
+++ b/modules/core/assets/factions/techie.json
@@ -0,0 +1,11 @@
+{
+ "name": "Techie",
+ "description": "Technical wizards. Can fix anything. Specialists in shield hacking technology.",
+ "colour": "169E10",
+ "defaultDisposition": -50,
+ "shipDesigns": [
+ "core:techieOrbiter",
+ "core:techiePlanetTurret",
+ "core:techieSmall"
+ ]
+}
\ No newline at end of file
diff --git a/modules/core/assets/factions/trader.json b/modules/core/assets/factions/trader.json
new file mode 100644
index 000000000..1ea421a33
--- /dev/null
+++ b/modules/core/assets/factions/trader.json
@@ -0,0 +1,10 @@
+{
+ "name": "Trader",
+ "description": "Merchants of the galaxy. Friends to all who can pay.",
+ "colour": "006400",
+ "defaultDisposition": 50,
+ "shipDesigns": [
+ "core:bus",
+ "core:drome"
+ ]
+}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/desertBigEngine/desertBigEngine.json b/modules/core/assets/items/engines/desertBigEngine/desertBigEngine.json
index f299e2ba7..14ae64f3d 100644
--- a/modules/core/assets/items/engines/desertBigEngine/desertBigEngine.json
+++ b/modules/core/assets/items/engines/desertBigEngine/desertBigEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Large Desert Engine",
"big": true
}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/desertEngine/desertEngine.json b/modules/core/assets/items/engines/desertEngine/desertEngine.json
index 9daf6299b..914006418 100644
--- a/modules/core/assets/items/engines/desertEngine/desertEngine.json
+++ b/modules/core/assets/items/engines/desertEngine/desertEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Desert Engine",
"big": false
}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/imperialBigEngine/imperialBigEngine.json b/modules/core/assets/items/engines/imperialBigEngine/imperialBigEngine.json
index f299e2ba7..8b7e90c4b 100644
--- a/modules/core/assets/items/engines/imperialBigEngine/imperialBigEngine.json
+++ b/modules/core/assets/items/engines/imperialBigEngine/imperialBigEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Large Imperial Engine",
"big": true
}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/imperialEngine/imperialEngine.json b/modules/core/assets/items/engines/imperialEngine/imperialEngine.json
index 9daf6299b..982bbbb23 100644
--- a/modules/core/assets/items/engines/imperialEngine/imperialEngine.json
+++ b/modules/core/assets/items/engines/imperialEngine/imperialEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Imperial Engine",
"big": false
}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/minerEngine/minerEngine.json b/modules/core/assets/items/engines/minerEngine/minerEngine.json
index 9daf6299b..ff966bb34 100644
--- a/modules/core/assets/items/engines/minerEngine/minerEngine.json
+++ b/modules/core/assets/items/engines/minerEngine/minerEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Miner Engine",
"big": false
}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/pirateBigEngine/pirateBigEngine.json b/modules/core/assets/items/engines/pirateBigEngine/pirateBigEngine.json
index f299e2ba7..d5461c3f6 100644
--- a/modules/core/assets/items/engines/pirateBigEngine/pirateBigEngine.json
+++ b/modules/core/assets/items/engines/pirateBigEngine/pirateBigEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Large Pirate Engine",
"big": true
}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/pirateEngine/pirateEngine.json b/modules/core/assets/items/engines/pirateEngine/pirateEngine.json
index 9daf6299b..0c68aa935 100644
--- a/modules/core/assets/items/engines/pirateEngine/pirateEngine.json
+++ b/modules/core/assets/items/engines/pirateEngine/pirateEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Pirate Engine",
"big": false
}
\ No newline at end of file
diff --git a/modules/core/assets/items/engines/techieEngine/techieEngine.json b/modules/core/assets/items/engines/techieEngine/techieEngine.json
index 9daf6299b..039d04b82 100644
--- a/modules/core/assets/items/engines/techieEngine/techieEngine.json
+++ b/modules/core/assets/items/engines/techieEngine/techieEngine.json
@@ -1,3 +1,4 @@
{
+ "name": "Techie Engine",
"big": false
}
\ No newline at end of file
diff --git a/modules/core/assets/items/guns/miner/miner.json b/modules/core/assets/items/guns/minerGun/minerGun.json
similarity index 100%
rename from modules/core/assets/items/guns/miner/miner.json
rename to modules/core/assets/items/guns/minerGun/minerGun.json
diff --git a/modules/core/assets/items/guns/miner/miner.png b/modules/core/assets/items/guns/minerGun/minerGun.png
similarity index 100%
rename from modules/core/assets/items/guns/miner/miner.png
rename to modules/core/assets/items/guns/minerGun/minerGun.png
diff --git a/modules/core/assets/items/guns/miner/minerIcon.png b/modules/core/assets/items/guns/minerGun/minerGunIcon.png
similarity index 100%
rename from modules/core/assets/items/guns/miner/minerIcon.png
rename to modules/core/assets/items/guns/minerGun/minerGunIcon.png
diff --git a/modules/core/assets/items/types.json b/modules/core/assets/items/types.json
index 954abe754..9fc4aacbf 100644
--- a/modules/core/assets/items/types.json
+++ b/modules/core/assets/items/types.json
@@ -1,4 +1,10 @@
{
+ "engine": {
+ "color": "hsb 0 100 50",
+ "pickUpSound": "core:otherPickUp",
+ "sz": 0.12
+ },
+
"clip": {
"color": "hsb 0 100 50",
"pickUpSound": "core:otherPickUp",