Merge branch 'master' into patch-1
27
README.md
@@ -9,34 +9,34 @@ _[Trello Board](https://trello.com/b/aE2tcUwF/mindustry-40-plans)_
|
|||||||
_[Wiki](https://mindustrygame.github.io/wiki)_
|
_[Wiki](https://mindustrygame.github.io/wiki)_
|
||||||
_[Javadoc](https://mindustrygame.github.io/docs/)_
|
_[Javadoc](https://mindustrygame.github.io/docs/)_
|
||||||
|
|
||||||
### Contributing
|
## Contributing
|
||||||
|
|
||||||
See [CONTRIBUTING](CONTRIBUTING.md).
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
||||||
|
|
||||||
### Building
|
## Building
|
||||||
|
|
||||||
Bleeding-edge builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/MindustryBuilds/releases).
|
Bleeding-edge builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/MindustryBuilds/releases).
|
||||||
|
|
||||||
If you'd rather compile on your own, follow these instructions.
|
If you'd rather compile on your own, follow these instructions.
|
||||||
First, make sure you have [JDK 14](https://adoptopenjdk.net/) installed. Open a terminal in the root directory, `cd` to the Mindustry folder and run the following commands:
|
First, make sure you have [JDK 14](https://adoptopenjdk.net/) installed. Open a terminal in the root directory, `cd` to the Mindustry folder and run the following commands:
|
||||||
|
|
||||||
#### Windows
|
### Windows
|
||||||
|
|
||||||
_Running:_ `gradlew desktop:run`
|
_Running:_ `gradlew desktop:run`
|
||||||
_Building:_ `gradlew desktop:dist`
|
_Building:_ `gradlew desktop:dist`
|
||||||
_Sprite Packing:_ `gradlew tools:pack`
|
_Sprite Packing:_ `gradlew tools:pack`
|
||||||
|
|
||||||
#### Linux/Mac OS
|
### Linux/Mac OS
|
||||||
|
|
||||||
_Running:_ `./gradlew desktop:run`
|
_Running:_ `./gradlew desktop:run`
|
||||||
_Building:_ `./gradlew desktop:dist`
|
_Building:_ `./gradlew desktop:dist`
|
||||||
_Sprite Packing:_ `./gradlew tools:pack`
|
_Sprite Packing:_ `./gradlew tools:pack`
|
||||||
|
|
||||||
#### Server
|
### Server
|
||||||
|
|
||||||
Server builds are bundled with each released build (in Releases). If you'd rather compile on your own, replace 'desktop' with 'server', e.g. `gradlew server:dist`.
|
Server builds are bundled with each released build (in Releases). If you'd rather compile on your own, replace 'desktop' with 'server', e.g. `gradlew server:dist`.
|
||||||
|
|
||||||
#### Android
|
### Android
|
||||||
|
|
||||||
1. Install the Android SDK [here.](https://developer.android.com/studio#downloads) Make sure you're downloading the "Command line tools only", as Android Studio is not required.
|
1. Install the Android SDK [here.](https://developer.android.com/studio#downloads) Make sure you're downloading the "Command line tools only", as Android Studio is not required.
|
||||||
2. Set the `ANDROID_HOME` environment variable to point to your unzipped Android SDK directory.
|
2. Set the `ANDROID_HOME` environment variable to point to your unzipped Android SDK directory.
|
||||||
@@ -44,20 +44,29 @@ Server builds are bundled with each released build (in Releases). If you'd rathe
|
|||||||
|
|
||||||
To debug the application on a connected phone, run `gradlew android:installDebug android:run`.
|
To debug the application on a connected phone, run `gradlew android:installDebug android:run`.
|
||||||
|
|
||||||
##### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
|
#### Permission Denied
|
||||||
|
|
||||||
If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, run `chmod +x ./gradlew` before running `./gradlew`. *This is a one-time procedure.*
|
If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, run `chmod +x ./gradlew` before running `./gradlew`. *This is a one-time procedure.*
|
||||||
|
|
||||||
|
#### Can not attach to current VM / Error during attachment using [...]
|
||||||
|
|
||||||
|
This is a problem related to [Jabel](https://github.com/bsideup/jabel), which I use to compile Java 8-compatible bytecode while using Java 9+ language features.
|
||||||
|
I don't know of a complete fix to this issue. However, if you're getting the error when running through IntelliJ, a workaround is to launch through Gradle *once*, which starts a daemon and avoids the problem in future runs through IntelliJ.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Gradle may take up to several minutes to download files. Be patient. <br>
|
Gradle may take up to several minutes to download files. Be patient. <br>
|
||||||
After building, the output .JAR file should be in `/desktop/build/libs/Mindustry.jar` for desktop builds, and in `/server/build/libs/server-release.jar` for server builds.
|
After building, the output .JAR file should be in `/desktop/build/libs/Mindustry.jar` for desktop builds, and in `/server/build/libs/server-release.jar` for server builds.
|
||||||
|
|
||||||
### Feature Requests
|
## Feature Requests
|
||||||
|
|
||||||
Post feature requests and feedback [here](https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose).
|
Post feature requests and feedback [here](https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose).
|
||||||
|
|
||||||
### Downloads
|
## Downloads
|
||||||
|
|
||||||
[<img src="https://static.itch.io/images/badge.svg"
|
[<img src="https://static.itch.io/images/badge.svg"
|
||||||
alt="Get it on Itch.io"
|
alt="Get it on Itch.io"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 274 B |
@@ -100,8 +100,7 @@ committingchanges = Comitting Changes
|
|||||||
done = Done
|
done = Done
|
||||||
feature.unsupported = Your device does not support this feature.
|
feature.unsupported = Your device does not support this feature.
|
||||||
|
|
||||||
mods.alphainfo = Keep in mind that mods are in alpha, and[scarlet] may be very buggy[].\nReport any issues you find to the Mindustry GitHub or Discord.
|
mods.alphainfo = Keep in mind that mods are in alpha, and[scarlet] may be very buggy[].\nReport any issues you find to the Mindustry GitHub.
|
||||||
mods.alpha = [accent](Alpha)
|
|
||||||
mods = Mods
|
mods = Mods
|
||||||
mods.none = [lightgray]No mods found!
|
mods.none = [lightgray]No mods found!
|
||||||
mods.guide = Modding Guide
|
mods.guide = Modding Guide
|
||||||
@@ -730,7 +729,6 @@ setting.fullscreen.name = Fullscreen
|
|||||||
setting.borderlesswindow.name = Borderless Window[lightgray] (restart may be required)
|
setting.borderlesswindow.name = Borderless Window[lightgray] (restart may be required)
|
||||||
setting.fps.name = Show FPS & Ping
|
setting.fps.name = Show FPS & Ping
|
||||||
setting.smoothcamera.name = Smooth Camera
|
setting.smoothcamera.name = Smooth Camera
|
||||||
setting.blockselectkeys.name = Show Block Select Keys
|
|
||||||
setting.vsync.name = VSync
|
setting.vsync.name = VSync
|
||||||
setting.pixelate.name = Pixelate
|
setting.pixelate.name = Pixelate
|
||||||
setting.minimap.name = Show Minimap
|
setting.minimap.name = Show Minimap
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ credits.text = Creado por [royal]Anuken[] - [sky]anukendev@gmail.com[]
|
|||||||
credits = Créditos
|
credits = Créditos
|
||||||
contributors = Traductores y Contribuidores
|
contributors = Traductores y Contribuidores
|
||||||
discord = ¡Únete al Discord de Mindustry!
|
discord = ¡Únete al Discord de Mindustry!
|
||||||
link.discord.description = La sala oficial del Discord de Mindustry
|
link.discord.description = El servidor official de Discord de Mindustry
|
||||||
link.reddit.description = El subreddit de Mindustry
|
link.reddit.description = El subreddit de Mindustry
|
||||||
link.github.description = Código fuente del juego
|
link.github.description = Código fuente del juego
|
||||||
link.changelog.description = Lista de actualizaciones
|
link.changelog.description = Lista de actualizaciones
|
||||||
link.dev-builds.description = Versiones de desarrollo inestables
|
link.dev-builds.description = Versiones en desarrollo inestables
|
||||||
link.trello.description = Tablero de Trello oficial para las características planificadas
|
link.trello.description = Tablero de Trello oficial para las características planificadas
|
||||||
link.itch.io.description = itch.io es la página donde podes descargar las versiones para PC y Servidor
|
link.itch.io.description = itch.io es la página donde podes descargar las versiones para PC y Servidor
|
||||||
link.google-play.description = Ficha en la Google Play Store
|
link.google-play.description = Ficha en la Google Play Store
|
||||||
@@ -18,7 +18,7 @@ screenshot = Captura de pantalla guardada en {0}
|
|||||||
screenshot.invalid = Mapa demasiado grande, no hay suficiente memoria para la captura de pantalla.
|
screenshot.invalid = Mapa demasiado grande, no hay suficiente memoria para la captura de pantalla.
|
||||||
gameover = Tu núcleo ha sido destruido.
|
gameover = Tu núcleo ha sido destruido.
|
||||||
gameover.pvp = ¡El equipo[accent] {0}[] ha ganado!
|
gameover.pvp = ¡El equipo[accent] {0}[] ha ganado!
|
||||||
highscore = [accent]¡Nueva mejor puntuación!
|
highscore = [accent]¡Nuevo récord de puntuación!
|
||||||
copied = Copiado.
|
copied = Copiado.
|
||||||
indev.popup = [accent]v6[] esta actualmente en [accent]alpha[].\n[lightgray]Esto significa que:[]\n[scarlet]- El modo de campaña no esta totalmente acabado[]\n-Falta contenido dentro del juego\n - Mucha de la [scarlet]AI de enemigos[] no funciona totalmente\n- Algunas unidades todavia no estan acabadas\n- Todo lo que ves es probable que sea cambiado o removido.\n\nReporta bugs o crasheos en [accent]Github[].
|
indev.popup = [accent]v6[] esta actualmente en [accent]alpha[].\n[lightgray]Esto significa que:[]\n[scarlet]- El modo de campaña no esta totalmente acabado[]\n-Falta contenido dentro del juego\n - Mucha de la [scarlet]AI de enemigos[] no funciona totalmente\n- Algunas unidades todavia no estan acabadas\n- Todo lo que ves es probable que sea cambiado o removido.\n\nReporta bugs o crasheos en [accent]Github[].
|
||||||
indev.notready = Esta parte del juego no esta lista todavia.
|
indev.notready = Esta parte del juego no esta lista todavia.
|
||||||
@@ -38,12 +38,12 @@ be.ignore = Ignorar
|
|||||||
be.noupdates = No se encontraron actualizaciones.
|
be.noupdates = No se encontraron actualizaciones.
|
||||||
be.check = Revisando actualizaciones
|
be.check = Revisando actualizaciones
|
||||||
|
|
||||||
schematic = Esquemático
|
schematic = Esquema
|
||||||
schematic.add = Guardar esquemático...
|
schematic.add = Guardar esquema...
|
||||||
schematics = Esquemáticos
|
schematics = Esquemas
|
||||||
schematic.replace = Un esquemático con ese nombre ya existe. ¿Deseas remplazarlo?
|
schematic.replace = Un esquema con ese nombre ya existe. ¿Deseas remplazarlo?
|
||||||
schematic.exists = Un esquemático con ese nombre ya existe.
|
schematic.exists = Un esquema con ese nombre ya existe.
|
||||||
schematic.import = Importar esquemático...
|
schematic.import = Importar esquema...
|
||||||
schematic.exportfile = Exportar archivo
|
schematic.exportfile = Exportar archivo
|
||||||
schematic.importfile = Importar archivo
|
schematic.importfile = Importar archivo
|
||||||
schematic.browseworkshop = Buscar en el Steam Workshop
|
schematic.browseworkshop = Buscar en el Steam Workshop
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 827 B |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 372 KiB |
|
Before Width: | Height: | Size: 429 KiB After Width: | Height: | Size: 429 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 426 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
@@ -36,6 +36,8 @@ public class Vars implements Loadable{
|
|||||||
public static boolean loadLocales = true;
|
public static boolean loadLocales = true;
|
||||||
/** Whether the logger is loaded. */
|
/** Whether the logger is loaded. */
|
||||||
public static boolean loadedLogger = false, loadedFileLogger = false;
|
public static boolean loadedLogger = false, loadedFileLogger = false;
|
||||||
|
/** Whether to show the cliff button in the editor*/
|
||||||
|
public static boolean addCliffButton = false;
|
||||||
/** Maximum extra padding around deployment schematics. */
|
/** Maximum extra padding around deployment schematics. */
|
||||||
public static final int maxLoadoutSchematicPad = 5;
|
public static final int maxLoadoutSchematicPad = 5;
|
||||||
/** Maximum schematic size.*/
|
/** Maximum schematic size.*/
|
||||||
@@ -87,7 +89,7 @@ public class Vars implements Loadable{
|
|||||||
/** duration of time between turns in ticks */
|
/** duration of time between turns in ticks */
|
||||||
public static final float turnDuration = 2 * Time.toMinutes;
|
public static final float turnDuration = 2 * Time.toMinutes;
|
||||||
/** chance of an invasion per turn, 1 = 100% */
|
/** chance of an invasion per turn, 1 = 100% */
|
||||||
public static final float baseInvasionChance = 1f / 15f;
|
public static final float baseInvasionChance = 1f / 25f;
|
||||||
/** how many turns have to pass before invasions start */
|
/** how many turns have to pass before invasions start */
|
||||||
public static final int invasionGracePeriod = 20;
|
public static final int invasionGracePeriod = 20;
|
||||||
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
|
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ public class BaseAI{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update(){
|
public void update(){
|
||||||
if(timer.get(timerSpawn, 60) && data.hasCore()){
|
if(data.team.rules().aiCoreSpawn && timer.get(timerSpawn, 60 * 2.5f) && data.hasCore()){
|
||||||
CoreBlock block = (CoreBlock)data.core().block;
|
CoreBlock block = (CoreBlock)data.core().block;
|
||||||
|
|
||||||
//create AI core unit
|
//create AI core unit
|
||||||
if(!state.isEditor() && !Groups.unit.contains(u -> u.team() == data.team && u.type() == block.unitType)){
|
if(!state.isEditor() && !Groups.unit.contains(u -> u.team() == data.team && u.type == block.unitType)){
|
||||||
Unit unit = block.unitType.create(data.team);
|
Unit unit = block.unitType.create(data.team);
|
||||||
unit.set(data.core());
|
unit.set(data.core());
|
||||||
unit.add();
|
unit.add();
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ public class WaveSpawner{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void spawnEffect(Unit unit){
|
private void spawnEffect(Unit unit){
|
||||||
Call.spawnEffect(unit.x, unit.y, unit.type());
|
Call.spawnEffect(unit.x, unit.y, unit.type);
|
||||||
Time.run(30f, unit::add);
|
Time.run(30f, unit::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class BuilderAI extends AIController{
|
|||||||
float dist = Math.min(cons.dst(unit) - buildingRange, 0);
|
float dist = Math.min(cons.dst(unit) - buildingRange, 0);
|
||||||
|
|
||||||
//make sure you can reach the request in time
|
//make sure you can reach the request in time
|
||||||
if(dist / unit.type().speed < cons.buildCost * 0.9f){
|
if(dist / unit.type.speed < cons.buildCost * 0.9f){
|
||||||
following = b;
|
following = b;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ public class BuilderAI extends AIController{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AIController fallback(){
|
public AIController fallback(){
|
||||||
return unit.type().flying ? new FlyingAI() : new GroundAI();
|
return unit.type.flying ? new FlyingAI() : new GroundAI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class FlyingAI extends AIController{
|
|||||||
@Override
|
@Override
|
||||||
public void updateMovement(){
|
public void updateMovement(){
|
||||||
if(target != null && unit.hasWeapons() && command() == UnitCommand.attack){
|
if(target != null && unit.hasWeapons() && command() == UnitCommand.attack){
|
||||||
if(unit.type().weapons.first().rotate){
|
if(unit.type.weapons.first().rotate){
|
||||||
moveTo(target, unit.range() * 0.8f);
|
moveTo(target, unit.range() * 0.8f);
|
||||||
unit.lookAt(target);
|
unit.lookAt(target);
|
||||||
}else{
|
}else{
|
||||||
@@ -57,7 +57,7 @@ public class FlyingAI extends AIController{
|
|||||||
vec.setAngle(Mathf.slerpDelta(unit.vel().angle(), vec.angle(), 0.6f));
|
vec.setAngle(Mathf.slerpDelta(unit.vel().angle(), vec.angle(), 0.6f));
|
||||||
}
|
}
|
||||||
|
|
||||||
vec.setLength(unit.type().speed);
|
vec.setLength(unit.type.speed);
|
||||||
|
|
||||||
unit.moveAt(vec);
|
unit.moveAt(vec);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ public class FormationAI extends AIController implements FormationMember{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateUnit(){
|
public void updateUnit(){
|
||||||
UnitType type = unit.type();
|
UnitType type = unit.type;
|
||||||
|
|
||||||
if(leader.dead){
|
if(leader.dead){
|
||||||
unit.resetController();
|
unit.resetController();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(unit.type().canBoost && unit.canPassOn()){
|
if(unit.type.canBoost && unit.canPassOn()){
|
||||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public class FormationAI extends AIController implements FormationMember{
|
|||||||
|
|
||||||
unit.aim(leader.aimX(), leader.aimY());
|
unit.aim(leader.aimX(), leader.aimY());
|
||||||
|
|
||||||
if(unit.type().rotateShooting){
|
if(unit.type.rotateShooting){
|
||||||
unit.lookAt(leader.aimX(), leader.aimY());
|
unit.lookAt(leader.aimX(), leader.aimY());
|
||||||
}else if(unit.moving()){
|
}else if(unit.moving()){
|
||||||
unit.lookAt(unit.vel.angle());
|
unit.lookAt(unit.vel.angle());
|
||||||
@@ -65,7 +65,7 @@ public class FormationAI extends AIController implements FormationMember{
|
|||||||
|
|
||||||
CoreBuild core = unit.team.core();
|
CoreBuild core = unit.team.core();
|
||||||
|
|
||||||
if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type().range) && !unit.acceptsItem(com.mineTile().drop())){
|
if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type.range) && !unit.acceptsItem(com.mineTile().drop())){
|
||||||
if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){
|
if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){
|
||||||
Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core);
|
Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core);
|
||||||
|
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ public class GroundAI extends AIController{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(unit.type().canBoost && !unit.onSolid()){
|
if(unit.type.canBoost && !unit.onSolid()){
|
||||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type().rotateShooting){
|
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type.rotateShooting){
|
||||||
if(unit.type().hasWeapons()){
|
if(unit.type.hasWeapons()){
|
||||||
unit.lookAt(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
|
unit.lookAt(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed));
|
||||||
}
|
}
|
||||||
}else if(unit.moving()){
|
}else if(unit.moving()){
|
||||||
unit.lookAt(unit.vel().angle());
|
unit.lookAt(unit.vel().angle());
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class LogicAI extends AIController{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(unit.type().canBoost && !unit.type().flying){
|
if(unit.type.canBoost && !unit.type.flying){
|
||||||
unit.elevation = Mathf.approachDelta(unit.elevation, Mathf.num(boost || unit.onSolid()), 0.08f);
|
unit.elevation = Mathf.approachDelta(unit.elevation, Mathf.num(boost || unit.onSolid()), 0.08f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ public class LogicAI extends AIController{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldShoot(){
|
protected boolean shouldShoot(){
|
||||||
return shoot && !(unit.type().canBoost && boost);
|
return shoot && !(unit.type.canBoost && boost);
|
||||||
}
|
}
|
||||||
|
|
||||||
//always aim for the main target
|
//always aim for the main target
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class MinerAI extends AIController{
|
|||||||
|
|
||||||
if(!(unit instanceof Minerc miner) || core == null) return;
|
if(!(unit instanceof Minerc miner) || core == null) return;
|
||||||
|
|
||||||
if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type().range)){
|
if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type.range)){
|
||||||
miner.mineTile(null);
|
miner.mineTile(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ public class MinerAI extends AIController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//if inventory is full, drop it off.
|
//if inventory is full, drop it off.
|
||||||
if(unit.stack.amount >= unit.type().itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){
|
if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){
|
||||||
mining = false;
|
mining = false;
|
||||||
}else{
|
}else{
|
||||||
if(retarget() && targetItem != null){
|
if(retarget() && targetItem != null){
|
||||||
@@ -44,9 +44,9 @@ public class MinerAI extends AIController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ore != null){
|
if(ore != null){
|
||||||
moveTo(ore, unit.type().range / 2f);
|
moveTo(ore, unit.type.range / 2f);
|
||||||
|
|
||||||
if(unit.within(ore, unit.type().range)){
|
if(unit.within(ore, unit.type.range)){
|
||||||
miner.mineTile(ore);
|
miner.mineTile(ore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ public class MinerAI extends AIController{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(unit.within(core, unit.type().range)){
|
if(unit.within(core, unit.type.range)){
|
||||||
if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){
|
if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){
|
||||||
Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core);
|
Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public class MinerAI extends AIController{
|
|||||||
mining = true;
|
mining = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
circle(core, unit.type().range / 1.8f);
|
circle(core, unit.type.range / 1.8f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class RepairAI extends AIController{
|
|||||||
if(target instanceof Building){
|
if(target instanceof Building){
|
||||||
boolean shoot = false;
|
boolean shoot = false;
|
||||||
|
|
||||||
if(target.within(unit, unit.type().range)){
|
if(target.within(unit, unit.type.range)){
|
||||||
unit.aim(target);
|
unit.aim(target);
|
||||||
shoot = true;
|
shoot = true;
|
||||||
}
|
}
|
||||||
@@ -23,8 +23,8 @@ public class RepairAI extends AIController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(target != null){
|
if(target != null){
|
||||||
if(!target.within(unit, unit.type().range * 0.65f) && target instanceof Building){
|
if(!target.within(unit, unit.type.range * 0.65f) && target instanceof Building){
|
||||||
moveTo(target, unit.type().range * 0.65f);
|
moveTo(target, unit.type.range * 0.65f);
|
||||||
}
|
}
|
||||||
|
|
||||||
unit.lookAt(target);
|
unit.lookAt(target);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class SuicideAI extends GroundAI{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(retarget()){
|
if(retarget()){
|
||||||
target = target(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround);
|
target = target(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround);
|
||||||
}
|
}
|
||||||
|
|
||||||
Building core = unit.closestEnemyCore();
|
Building core = unit.closestEnemyCore();
|
||||||
@@ -30,11 +30,11 @@ public class SuicideAI extends GroundAI{
|
|||||||
|
|
||||||
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.hasWeapons()){
|
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.hasWeapons()){
|
||||||
rotate = true;
|
rotate = true;
|
||||||
shoot = unit.within(target, unit.type().weapons.first().bullet.range() +
|
shoot = unit.within(target, unit.type.weapons.first().bullet.range() +
|
||||||
(target instanceof Building ? ((Building)target).block.size * Vars.tilesize / 2f : ((Hitboxc)target).hitSize() / 2f));
|
(target instanceof Building ? ((Building)target).block.size * Vars.tilesize / 2f : ((Hitboxc)target).hitSize() / 2f));
|
||||||
|
|
||||||
if(unit.type().hasWeapons()){
|
if(unit.type.hasWeapons()){
|
||||||
unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
|
unit.aimLook(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed));
|
||||||
}
|
}
|
||||||
|
|
||||||
//do not move toward walls or transport blocks
|
//do not move toward walls or transport blocks
|
||||||
@@ -65,7 +65,7 @@ public class SuicideAI extends GroundAI{
|
|||||||
if(!blocked){
|
if(!blocked){
|
||||||
moveToTarget = true;
|
moveToTarget = true;
|
||||||
//move towards target directly
|
//move towards target directly
|
||||||
unit.moveAt(vec.set(target).sub(unit).limit(unit.type().speed));
|
unit.moveAt(vec.set(target).sub(unit).limit(unit.type.speed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class PhysicsProcess implements AsyncProcess{
|
|||||||
PhysicRef ref = entity.physref();
|
PhysicRef ref = entity.physref();
|
||||||
|
|
||||||
ref.body.layer =
|
ref.body.layer =
|
||||||
entity.type().allowLegStep ? layerLegs :
|
entity.type.allowLegStep ? layerLegs :
|
||||||
entity.isGrounded() ? layerGround : layerFlying;
|
entity.isGrounded() ? layerGround : layerFlying;
|
||||||
ref.x = entity.x();
|
ref.x = entity.x();
|
||||||
ref.y = entity.y();
|
ref.y = entity.y();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class Fx{
|
|||||||
|
|
||||||
mixcol(Pal.accent, 1f);
|
mixcol(Pal.accent, 1f);
|
||||||
alpha(e.fout());
|
alpha(e.fout());
|
||||||
rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type().icon(Cicon.full), select.x, select.y, block ? 0f : select.rotation - 90f);
|
rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type.icon(Cicon.full), select.x, select.y, block ? 0f : select.rotation - 90f);
|
||||||
alpha(1f);
|
alpha(1f);
|
||||||
Lines.stroke(e.fslope() * 1f);
|
Lines.stroke(e.fslope() * 1f);
|
||||||
Lines.square(select.x, select.y, e.fout() * select.hitSize * 2f, 45);
|
Lines.square(select.x, select.y, e.fout() * select.hitSize * 2f, 45);
|
||||||
@@ -66,7 +66,7 @@ public class Fx{
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
unitDespawn = new Effect(100f, e -> {
|
unitDespawn = new Effect(100f, e -> {
|
||||||
if(!(e.data instanceof Unit) || e.<Unit>data().type() == null) return;
|
if(!(e.data instanceof Unit) || e.<Unit>data().type == null) return;
|
||||||
|
|
||||||
Unit select = e.data();
|
Unit select = e.data();
|
||||||
float scl = e.fout(Interp.pow2Out);
|
float scl = e.fout(Interp.pow2Out);
|
||||||
@@ -74,7 +74,7 @@ public class Fx{
|
|||||||
Draw.scl *= scl;
|
Draw.scl *= scl;
|
||||||
|
|
||||||
mixcol(Pal.accent, 1f);
|
mixcol(Pal.accent, 1f);
|
||||||
rect(select.type().icon(Cicon.full), select.x, select.y, select.rotation - 90f);
|
rect(select.type.icon(Cicon.full), select.x, select.y, select.rotation - 90f);
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
Draw.scl = p;
|
Draw.scl = p;
|
||||||
|
|||||||
@@ -872,7 +872,6 @@ public class UnitTypes implements ContentList{
|
|||||||
drag = 0.01f;
|
drag = 0.01f;
|
||||||
flying = true;
|
flying = true;
|
||||||
health = 75;
|
health = 75;
|
||||||
faceTarget = false;
|
|
||||||
engineOffset = 5.5f;
|
engineOffset = 5.5f;
|
||||||
range = 140f;
|
range = 140f;
|
||||||
|
|
||||||
@@ -1449,13 +1448,13 @@ public class UnitTypes implements ContentList{
|
|||||||
trailMult = 0.8f;
|
trailMult = 0.8f;
|
||||||
hitEffect = Fx.massiveExplosion;
|
hitEffect = Fx.massiveExplosion;
|
||||||
knockback = 1.5f;
|
knockback = 1.5f;
|
||||||
lifetime = 140f;
|
lifetime = 100f;
|
||||||
height = 15.5f;
|
height = 15.5f;
|
||||||
width = 15f;
|
width = 15f;
|
||||||
collidesTiles = false;
|
collidesTiles = false;
|
||||||
ammoMultiplier = 4f;
|
ammoMultiplier = 4f;
|
||||||
splashDamageRadius = 60f;
|
splashDamageRadius = 60f;
|
||||||
splashDamage = 85f;
|
splashDamage = 80f;
|
||||||
backColor = Pal.missileYellowBack;
|
backColor = Pal.missileYellowBack;
|
||||||
frontColor = Pal.missileYellow;
|
frontColor = Pal.missileYellow;
|
||||||
trailEffect = Fx.artilleryTrail;
|
trailEffect = Fx.artilleryTrail;
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ public class Control implements ApplicationListener, Loadable{
|
|||||||
Groups.unit.clear();
|
Groups.unit.clear();
|
||||||
|
|
||||||
Tile spawn = world.tile(sector.info.spawnPosition);
|
Tile spawn = world.tile(sector.info.spawnPosition);
|
||||||
Schematics.placeLoadout(universe.getLastLoadout(), spawn.x, spawn.y);
|
Schematics.placeLaunchLoadout(spawn.x, spawn.y);
|
||||||
|
|
||||||
//set up camera/player locations
|
//set up camera/player locations
|
||||||
player.set(spawn.x * tilesize, spawn.y * tilesize);
|
player.set(spawn.x * tilesize, spawn.y * tilesize);
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import mindustry.maps.*;
|
|||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.type.Weather.*;
|
import mindustry.type.Weather.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
import mindustry.world.blocks.*;
|
|
||||||
import mindustry.world.blocks.ConstructBlock.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -40,32 +38,7 @@ public class Logic implements ApplicationListener{
|
|||||||
//skip null entities or un-rebuildables, for obvious reasons; also skip client since they can't modify these requests
|
//skip null entities or un-rebuildables, for obvious reasons; also skip client since they can't modify these requests
|
||||||
if(tile.build == null || !tile.block().rebuildable || net.client()) return;
|
if(tile.build == null || !tile.block().rebuildable || net.client()) return;
|
||||||
|
|
||||||
if(block instanceof ConstructBlock){
|
tile.build.addPlan(true);
|
||||||
|
|
||||||
ConstructBuild entity = tile.bc();
|
|
||||||
|
|
||||||
//update block to reflect the fact that something was being constructed
|
|
||||||
if(entity.cblock != null && entity.cblock.synthetic()){
|
|
||||||
block = entity.cblock;
|
|
||||||
}else{
|
|
||||||
//otherwise this was a deconstruction that was interrupted, don't want to rebuild that
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TeamData data = state.teams.get(tile.team());
|
|
||||||
|
|
||||||
//remove existing blocks that have been placed here.
|
|
||||||
//painful O(n) iteration + copy
|
|
||||||
for(int i = 0; i < data.blocks.size; i++){
|
|
||||||
BlockPlan b = data.blocks.get(i);
|
|
||||||
if(b.x == tile.x && b.y == tile.y){
|
|
||||||
data.blocks.removeIndex(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)tile.build.rotation, block.id, tile.build.config()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(BlockBuildEndEvent.class, event -> {
|
Events.on(BlockBuildEndEvent.class, event -> {
|
||||||
@@ -123,6 +96,11 @@ public class Logic implements ApplicationListener{
|
|||||||
Events.on(WorldLoadEvent.class, e -> {
|
Events.on(WorldLoadEvent.class, e -> {
|
||||||
//enable infinite ammo for wave team by default
|
//enable infinite ammo for wave team by default
|
||||||
state.rules.waveTeam.rules().infiniteAmmo = true;
|
state.rules.waveTeam.rules().infiniteAmmo = true;
|
||||||
|
if(state.isCampaign()){
|
||||||
|
//enable building AI
|
||||||
|
state.rules.waveTeam.rules().ai = true;
|
||||||
|
state.rules.waveTeam.rules().infiniteResources = true;
|
||||||
|
}
|
||||||
|
|
||||||
//save settings
|
//save settings
|
||||||
Core.settings.manualSave();
|
Core.settings.manualSave();
|
||||||
@@ -201,9 +179,12 @@ public class Logic implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//if there's a "win" wave and no enemies are present, win automatically
|
//if there's a "win" wave and no enemies are present, win automatically
|
||||||
if(state.rules.waves && state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()){
|
if(state.rules.waves && (state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()) ||
|
||||||
|
(state.rules.attackMode && state.rules.waveTeam.cores().isEmpty())){
|
||||||
//the sector has been conquered - waves get disabled
|
//the sector has been conquered - waves get disabled
|
||||||
state.rules.waves = false;
|
state.rules.waves = false;
|
||||||
|
//disable attack mode
|
||||||
|
state.rules.attackMode = false;
|
||||||
|
|
||||||
//fire capture event
|
//fire capture event
|
||||||
Events.fire(new SectorCaptureEvent(state.rules.sector));
|
Events.fire(new SectorCaptureEvent(state.rules.sector));
|
||||||
@@ -291,7 +272,7 @@ public class Logic implements ApplicationListener{
|
|||||||
|
|
||||||
if(state.isGame()){
|
if(state.isGame()){
|
||||||
if(!net.client()){
|
if(!net.client()){
|
||||||
state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type().isCounted);
|
state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type.isCounted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!state.isPaused()){
|
if(!state.isPaused()){
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
shooting = false;
|
shooting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!player.dead() && (player.unit().type().flying || !player.unit().type().canBoost)){
|
if(!player.dead() && (player.unit().type.flying || !player.unit().type.canBoost)){
|
||||||
boosting = false;
|
boosting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
Unit unit = player.unit();
|
Unit unit = player.unit();
|
||||||
|
|
||||||
long elapsed = Time.timeSinceMillis(con.lastReceivedClientTime);
|
long elapsed = Time.timeSinceMillis(con.lastReceivedClientTime);
|
||||||
float maxSpeed = ((player.unit().type().canBoost && player.unit().isFlying()) ? player.unit().type().boostMultiplier : 1f) * player.unit().type().speed;
|
float maxSpeed = ((player.unit().type.canBoost && player.unit().isFlying()) ? player.unit().type.boostMultiplier : 1f) * player.unit().type.speed;
|
||||||
if(unit.isGrounded()){
|
if(unit.isGrounded()){
|
||||||
maxSpeed *= unit.floorSpeedMultiplier();
|
maxSpeed *= unit.floorSpeedMultiplier();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import arc.files.*;
|
|||||||
import arc.func.*;
|
import arc.func.*;
|
||||||
import arc.graphics.*;
|
import arc.graphics.*;
|
||||||
import arc.math.*;
|
import arc.math.*;
|
||||||
|
import arc.math.geom.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import mindustry.content.*;
|
import mindustry.content.*;
|
||||||
import mindustry.editor.DrawOperation.*;
|
import mindustry.editor.DrawOperation.*;
|
||||||
@@ -180,6 +181,52 @@ public class MapEditor{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addCliffs(){
|
||||||
|
for(Tile tile : world.tiles){
|
||||||
|
if(!tile.block().isStatic() || tile.block() == Blocks.cliff) continue;
|
||||||
|
|
||||||
|
int rotation = 0;
|
||||||
|
for(int i = 0; i < 8; i++){
|
||||||
|
Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y);
|
||||||
|
if(other != null && !other.block().isStatic()){
|
||||||
|
rotation |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rotation != 0){
|
||||||
|
tile.setBlock(Blocks.cliff);
|
||||||
|
}
|
||||||
|
|
||||||
|
tile.data = (byte)rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Tile tile : world.tiles){
|
||||||
|
if(tile.block() != Blocks.cliff && tile.block().isStatic()){
|
||||||
|
tile.setBlock(Blocks.air);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFloorCliffs(){
|
||||||
|
for(Tile tile : world.tiles){
|
||||||
|
if(!tile.floor().hasSurface() || tile.block() == Blocks.cliff) continue;
|
||||||
|
|
||||||
|
int rotation = 0;
|
||||||
|
for(int i = 0; i < 8; i++){
|
||||||
|
Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y);
|
||||||
|
if(other != null && !other.floor().hasSurface()){
|
||||||
|
rotation |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rotation != 0){
|
||||||
|
tile.setBlock(Blocks.cliff);
|
||||||
|
}
|
||||||
|
|
||||||
|
tile.data = (byte)rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void drawCircle(int x, int y, Cons<Tile> drawer){
|
public void drawCircle(int x, int y, Cons<Tile> drawer){
|
||||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void build(){
|
public void build(){
|
||||||
float size = 60f;
|
float size = 58f;
|
||||||
|
|
||||||
clearChildren();
|
clearChildren();
|
||||||
table(cont -> {
|
table(cont -> {
|
||||||
@@ -559,10 +559,19 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
|
|
||||||
mid.row();
|
mid.row();
|
||||||
|
|
||||||
mid.table(t -> {
|
if(!mobile){
|
||||||
t.button("@editor.center", Icon.move, Styles.cleart, () -> view.center()).growX().margin(9f);
|
mid.table(t -> {
|
||||||
}).growX().top();
|
t.button("@editor.center", Icon.move, Styles.cleart, view::center).growX().margin(9f);
|
||||||
|
}).growX().top();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addCliffButton){
|
||||||
|
mid.row();
|
||||||
|
|
||||||
|
mid.table(t -> {
|
||||||
|
t.button("Cliffs", Icon.terrain, Styles.cleart, editor::addCliffs).growX().margin(9f);
|
||||||
|
}).growX().top();
|
||||||
|
}
|
||||||
}).margin(0).left().growY();
|
}).margin(0).left().growY();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class Units{
|
|||||||
|
|
||||||
nearby(x, y, width, height, unit -> {
|
nearby(x, y, width, height, unit -> {
|
||||||
if(boolResult) return;
|
if(boolResult) return;
|
||||||
if((unit.isGrounded() && !unit.type().hovering) == ground){
|
if((unit.isGrounded() && !unit.type.hovering) == ground){
|
||||||
unit.hitbox(hitrect);
|
unit.hitbox(hitrect);
|
||||||
|
|
||||||
if(hitrect.overlaps(x, y, width, height)){
|
if(hitrect.overlaps(x, y, width, height)){
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ public class LaserBulletType extends BulletType{
|
|||||||
this(1f);
|
this(1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float estimateDPS(){
|
||||||
|
return super.estimateDPS() * 2f;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(){
|
public void init(){
|
||||||
super.init();
|
super.init();
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public class LiquidBulletType extends BulletType{
|
|||||||
if(liquid != null){
|
if(liquid != null){
|
||||||
this.liquid = liquid;
|
this.liquid = liquid;
|
||||||
this.status = liquid.effect;
|
this.status = liquid.effect;
|
||||||
|
lightColor = liquid.lightColor;
|
||||||
|
lightOpacity = liquid.lightColor.a;
|
||||||
}
|
}
|
||||||
|
|
||||||
ammoMultiplier = 1f;
|
ammoMultiplier = 1f;
|
||||||
|
|||||||
@@ -21,12 +21,14 @@ import mindustry.ctype.*;
|
|||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
import mindustry.game.EventType.*;
|
import mindustry.game.EventType.*;
|
||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
|
import mindustry.game.Teams.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.graphics.*;
|
import mindustry.graphics.*;
|
||||||
import mindustry.logic.*;
|
import mindustry.logic.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.ui.*;
|
import mindustry.ui.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
import mindustry.world.blocks.ConstructBlock.*;
|
||||||
import mindustry.world.blocks.environment.*;
|
import mindustry.world.blocks.environment.*;
|
||||||
import mindustry.world.blocks.payloads.*;
|
import mindustry.world.blocks.payloads.*;
|
||||||
import mindustry.world.blocks.power.*;
|
import mindustry.world.blocks.power.*;
|
||||||
@@ -191,6 +193,36 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
|||||||
//endregion
|
//endregion
|
||||||
//region utility methods
|
//region utility methods
|
||||||
|
|
||||||
|
public void addPlan(boolean checkPrevious){
|
||||||
|
if(!block.rebuildable) return;
|
||||||
|
|
||||||
|
if(self() instanceof ConstructBuild entity){
|
||||||
|
//update block to reflect the fact that something was being constructed
|
||||||
|
if(entity.cblock != null && entity.cblock.synthetic()){
|
||||||
|
block = entity.cblock;
|
||||||
|
}else{
|
||||||
|
//otherwise this was a deconstruction that was interrupted, don't want to rebuild that
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamData data = state.teams.get(team);
|
||||||
|
|
||||||
|
if(checkPrevious){
|
||||||
|
//remove existing blocks that have been placed here.
|
||||||
|
//painful O(n) iteration + copy
|
||||||
|
for(int i = 0; i < data.blocks.size; i++){
|
||||||
|
BlockPlan b = data.blocks.get(i);
|
||||||
|
if(b.x == tile.x && b.y == tile.y){
|
||||||
|
data.blocks.removeIndex(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)rotation, block.id, config()));
|
||||||
|
}
|
||||||
|
|
||||||
/** Configure with the current, local player. */
|
/** Configure with the current, local player. */
|
||||||
public void configure(Object value){
|
public void configure(Object value){
|
||||||
//save last used config
|
//save last used config
|
||||||
@@ -1263,7 +1295,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
|||||||
case type -> block;
|
case type -> block;
|
||||||
case firstItem -> items == null ? null : items.first();
|
case firstItem -> items == null ? null : items.first();
|
||||||
case config -> block.configurations.containsKey(Item.class) || block.configurations.containsKey(Liquid.class) ? config() : null;
|
case config -> block.configurations.containsKey(Item.class) || block.configurations.containsKey(Liquid.class) ? config() : null;
|
||||||
case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type() : getPayload() instanceof BuildPayload p2 ? p2.block() : null;
|
case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type : getPayload() instanceof BuildPayload p2 ? p2.block() : null;
|
||||||
default -> noSensed;
|
default -> noSensed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
|||||||
units.clear();
|
units.clear();
|
||||||
|
|
||||||
Units.nearby(team, x, y, 150f, u -> {
|
Units.nearby(team, x, y, 150f, u -> {
|
||||||
if(u.isAI() && include.get(u) && u != self() && u.type().flying == type.flying && u.hitSize <= hitSize * 1.1f){
|
if(u.isAI() && include.get(u) && u != self() && u.type.flying == type.flying && u.hitSize <= hitSize * 1.1f){
|
||||||
units.add(u);
|
units.add(u);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -82,7 +82,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
|||||||
FormationAI ai;
|
FormationAI ai;
|
||||||
unit.controller(ai = new FormationAI(self(), formation));
|
unit.controller(ai = new FormationAI(self(), formation));
|
||||||
spacing = Math.max(spacing, ai.formationSize());
|
spacing = Math.max(spacing, ai.formationSize());
|
||||||
minFormationSpeed = Math.min(minFormationSpeed, unit.type().speed);
|
minFormationSpeed = Math.min(minFormationSpeed, unit.type.speed);
|
||||||
}
|
}
|
||||||
this.formation = formation;
|
this.formation = formation;
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
|||||||
//reset controlled units
|
//reset controlled units
|
||||||
for(Unit unit : controlling){
|
for(Unit unit : controlling){
|
||||||
if(unit.controller().isBeingControlled(self())){
|
if(unit.controller().isBeingControlled(self())){
|
||||||
unit.controller(unit.type().createController());
|
unit.controller(unit.type.createController());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
|||||||
admin = typing = false;
|
admin = typing = false;
|
||||||
textFadeTime = 0f;
|
textFadeTime = 0f;
|
||||||
if(!dead()){
|
if(!dead()){
|
||||||
unit.controller(unit.type().createController());
|
unit.controller(unit.type.createController());
|
||||||
unit = Nulls.unit;
|
unit = Nulls.unit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
|||||||
|
|
||||||
@Replace
|
@Replace
|
||||||
public float clipSize(){
|
public float clipSize(){
|
||||||
return unit.isNull() ? 20 : unit.type().hitSize * 2f;
|
return unit.isNull() ? 20 : unit.type.hitSize * 2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,7 +123,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
|||||||
deathTimer = 0;
|
deathTimer = 0;
|
||||||
|
|
||||||
//update some basic state to sync things
|
//update some basic state to sync things
|
||||||
if(unit.type().canBoost){
|
if(unit.type.canBoost){
|
||||||
Tile tile = unit.tileOn();
|
Tile tile = unit.tileOn();
|
||||||
unit.elevation = Mathf.approachDelta(unit.elevation, (tile != null && tile.solid()) || boosting ? 1f : 0f, 0.08f);
|
unit.elevation = Mathf.approachDelta(unit.elevation, (tile != null && tile.solid()) || boosting ? 1f : 0f, 0.08f);
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
|||||||
|
|
||||||
if(this.unit != Nulls.unit){
|
if(this.unit != Nulls.unit){
|
||||||
//un-control the old unit
|
//un-control the old unit
|
||||||
this.unit.controller(this.unit.type().createController());
|
this.unit.controller(this.unit.type.createController());
|
||||||
}
|
}
|
||||||
this.unit = unit;
|
this.unit = unit;
|
||||||
if(unit != Nulls.unit){
|
if(unit != Nulls.unit){
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
|||||||
unit.apply(liquid.effect, 60 * 2);
|
unit.apply(liquid.effect, 60 * 2);
|
||||||
|
|
||||||
if(unit.vel.len() > 0.1){
|
if(unit.vel.len() > 0.1){
|
||||||
Fx.ripple.at(unit.x, unit.y, unit.type().rippleScale, liquid.color);
|
Fx.ripple.at(unit.x, unit.y, unit.type.rippleScale, liquid.color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
|||||||
@Import int id;
|
@Import int id;
|
||||||
|
|
||||||
private UnitController controller;
|
private UnitController controller;
|
||||||
private UnitType type;
|
UnitType type;
|
||||||
boolean spawnedByCore;
|
boolean spawnedByCore;
|
||||||
double flag;
|
double flag;
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
|||||||
case firstItem -> stack().amount == 0 ? null : item();
|
case firstItem -> stack().amount == 0 ? null : item();
|
||||||
case payloadType -> self() instanceof Payloadc pay ?
|
case payloadType -> self() instanceof Payloadc pay ?
|
||||||
(pay.payloads().isEmpty() ? null :
|
(pay.payloads().isEmpty() ? null :
|
||||||
pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type() :
|
pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type :
|
||||||
pay.payloads().peek() instanceof BuildPayload p2 ? p2.block() : null) : null;
|
pay.payloads().peek() instanceof BuildPayload p2 ? p2.block() : null) : null;
|
||||||
default -> noSensed;
|
default -> noSensed;
|
||||||
};
|
};
|
||||||
@@ -163,22 +163,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void set(UnitType def, UnitController controller){
|
public void set(UnitType def, UnitController controller){
|
||||||
type(type);
|
if(this.type != def){
|
||||||
|
setType(def);
|
||||||
|
}
|
||||||
controller(controller);
|
controller(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void type(UnitType type){
|
|
||||||
if(this.type == type) return;
|
|
||||||
|
|
||||||
setStats(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UnitType type(){
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return pathfinder path type for calculating costs */
|
/** @return pathfinder path type for calculating costs */
|
||||||
public int pathType(){
|
public int pathType(){
|
||||||
return Pathfinder.costGround;
|
return Pathfinder.costGround;
|
||||||
@@ -208,7 +198,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
|||||||
return Units.getCap(team);
|
return Units.getCap(team);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStats(UnitType type){
|
public void setType(UnitType type){
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.maxHealth = type.health;
|
this.maxHealth = type.health;
|
||||||
this.drag = type.drag;
|
this.drag = type.drag;
|
||||||
@@ -226,7 +216,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
|||||||
@Override
|
@Override
|
||||||
public void afterSync(){
|
public void afterSync(){
|
||||||
//set up type info after reading
|
//set up type info after reading
|
||||||
setStats(this.type);
|
setType(this.type);
|
||||||
controller.unit(self());
|
controller.unit(self());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public class AIController implements UnitController{
|
|||||||
|
|
||||||
if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return;
|
if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return;
|
||||||
|
|
||||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed));
|
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type.speed));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateWeapons(){
|
protected void updateWeapons(){
|
||||||
@@ -105,7 +105,7 @@ public class AIController implements UnitController{
|
|||||||
boolean ret = retarget();
|
boolean ret = retarget();
|
||||||
|
|
||||||
if(ret){
|
if(ret){
|
||||||
target = findTarget(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround);
|
target = findTarget(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(invalid(target)){
|
if(invalid(target)){
|
||||||
@@ -119,7 +119,7 @@ public class AIController implements UnitController{
|
|||||||
float mountX = unit.x + Angles.trnsx(rotation, weapon.x, weapon.y),
|
float mountX = unit.x + Angles.trnsx(rotation, weapon.x, weapon.y),
|
||||||
mountY = unit.y + Angles.trnsy(rotation, weapon.x, weapon.y);
|
mountY = unit.y + Angles.trnsy(rotation, weapon.x, weapon.y);
|
||||||
|
|
||||||
if(unit.type().singleTarget){
|
if(unit.type.singleTarget){
|
||||||
targets[i] = target;
|
targets[i] = target;
|
||||||
}else{
|
}else{
|
||||||
if(ret){
|
if(ret){
|
||||||
@@ -176,7 +176,7 @@ public class AIController implements UnitController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void circle(Position target, float circleLength){
|
protected void circle(Position target, float circleLength){
|
||||||
circle(target, circleLength, unit.type().speed);
|
circle(target, circleLength, unit.type.speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void circle(Position target, float circleLength, float speed){
|
protected void circle(Position target, float circleLength, float speed){
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class DefaultWaves{
|
|||||||
effect = StatusEffects.overdrive;
|
effect = StatusEffects.overdrive;
|
||||||
}},
|
}},
|
||||||
|
|
||||||
new SpawnGroup(mace){{
|
new SpawnGroup(pulsar){{
|
||||||
begin = 120;
|
begin = 120;
|
||||||
spacing = 2;
|
spacing = 2;
|
||||||
unitScaling = 3;
|
unitScaling = 3;
|
||||||
@@ -122,7 +122,7 @@ public class DefaultWaves{
|
|||||||
shieldScaling = 30;
|
shieldScaling = 30;
|
||||||
}},
|
}},
|
||||||
|
|
||||||
new SpawnGroup(dagger){{
|
new SpawnGroup(nova){{
|
||||||
begin = 35;
|
begin = 35;
|
||||||
spacing = 3;
|
spacing = 3;
|
||||||
unitAmount = 4;
|
unitAmount = 4;
|
||||||
@@ -233,7 +233,7 @@ public class DefaultWaves{
|
|||||||
shieldScaling = 20f;
|
shieldScaling = 20f;
|
||||||
}},
|
}},
|
||||||
|
|
||||||
new SpawnGroup(atrax){{
|
new SpawnGroup(toxopid){{
|
||||||
begin = 210;
|
begin = 210;
|
||||||
unitAmount = 1;
|
unitAmount = 1;
|
||||||
unitScaling = 1;
|
unitScaling = 1;
|
||||||
@@ -258,7 +258,7 @@ public class DefaultWaves{
|
|||||||
{nova, pulsar, quasar, vela, corvus},
|
{nova, pulsar, quasar, vela, corvus},
|
||||||
{crawler, atrax, spiroct, arkyid, toxopid},
|
{crawler, atrax, spiroct, arkyid, toxopid},
|
||||||
//{risso, minke, bryde, sei, omura}, //questionable choices
|
//{risso, minke, bryde, sei, omura}, //questionable choices
|
||||||
//{mono, poly, mega, quad, oct}, //do not attack
|
{poly, poly, mega, quad, quad},
|
||||||
{flare, horizon, zenith, antumbra, eclipse}
|
{flare, horizon, zenith, antumbra, eclipse}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ public class Rules{
|
|||||||
public boolean ai;
|
public boolean ai;
|
||||||
/** TODO Tier of blocks/designs that the AI uses for building. [0, 1]*/
|
/** TODO Tier of blocks/designs that the AI uses for building. [0, 1]*/
|
||||||
public float aiTier = 0f;
|
public float aiTier = 0f;
|
||||||
|
/** Whether, when AI is enabled, ships should be spawned from the core. */
|
||||||
|
public boolean aiCoreSpawn = true;
|
||||||
/** If true, blocks don't require power or resources. */
|
/** If true, blocks don't require power or resources. */
|
||||||
public boolean cheat;
|
public boolean cheat;
|
||||||
/** If true, resources are not consumed when building. */
|
/** If true, resources are not consumed when building. */
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ public class SectorInfo{
|
|||||||
public Seq<UnlockableContent> resources = new Seq<>();
|
public Seq<UnlockableContent> resources = new Seq<>();
|
||||||
/** Whether waves are enabled here. */
|
/** Whether waves are enabled here. */
|
||||||
public boolean waves = true;
|
public boolean waves = true;
|
||||||
|
/** Whether attack mode is enabled here. */
|
||||||
|
public boolean attack = false;
|
||||||
/** Wave # from state */
|
/** Wave # from state */
|
||||||
public int wave = 1, winWave = -1;
|
public int wave = 1, winWave = -1;
|
||||||
/** Time between waves. */
|
/** Time between waves. */
|
||||||
@@ -99,20 +101,30 @@ public class SectorInfo{
|
|||||||
|
|
||||||
/** Write contents of meta into main storage. */
|
/** Write contents of meta into main storage. */
|
||||||
public void write(){
|
public void write(){
|
||||||
|
//enable attack mode when there's a core.
|
||||||
|
if(state.rules.waveTeam.core() != null){
|
||||||
|
attack = true;
|
||||||
|
winWave = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if there are infinite waves and no win wave, add a win wave.
|
||||||
|
if(waves && winWave <= 0 && !attack){
|
||||||
|
winWave = 30;
|
||||||
|
}
|
||||||
|
|
||||||
state.wave = wave;
|
state.wave = wave;
|
||||||
state.rules.waves = waves;
|
state.rules.waves = waves;
|
||||||
state.rules.waveSpacing = waveSpacing;
|
state.rules.waveSpacing = waveSpacing;
|
||||||
state.rules.winWave = winWave;
|
state.rules.winWave = winWave;
|
||||||
|
state.rules.attackMode = attack;
|
||||||
|
|
||||||
CoreBuild entity = state.rules.defaultTeam.core();
|
CoreBuild entity = state.rules.defaultTeam.core();
|
||||||
if(entity != null){
|
if(entity != null){
|
||||||
entity.items.clear();
|
entity.items.clear();
|
||||||
entity.items.add(items);
|
entity.items.add(items);
|
||||||
//ensure capacity.
|
//ensure capacity.
|
||||||
entity.items.each((i, a) -> entity.items.set(i, Math.min(a, entity.block.itemCapacity)));
|
entity.items.each((i, a) -> entity.items.set(i, Math.min(a, entity.storageCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO write items.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Prepare data for writing to a save. */
|
/** Prepare data for writing to a save. */
|
||||||
@@ -135,6 +147,7 @@ public class SectorInfo{
|
|||||||
wave = state.wave;
|
wave = state.wave;
|
||||||
winWave = state.rules.winWave;
|
winWave = state.rules.winWave;
|
||||||
waves = state.rules.waves;
|
waves = state.rules.waves;
|
||||||
|
attack = state.rules.attackMode;
|
||||||
hasCore = entity != null;
|
hasCore = entity != null;
|
||||||
bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block;
|
bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block;
|
||||||
storageCapacity = entity != null ? entity.storageCapacity : 0;
|
storageCapacity = entity != null ? entity.storageCapacity : 0;
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import mindustry.gen.*;
|
|||||||
import mindustry.io.legacy.*;
|
import mindustry.io.legacy.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,4 +140,20 @@ public class SpawnGroup implements Serializable{
|
|||||||
", items=" + items +
|
", items=" + items +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o){
|
||||||
|
if(this == o) return true;
|
||||||
|
if(o == null || getClass() != o.getClass()) return false;
|
||||||
|
SpawnGroup group = (SpawnGroup)o;
|
||||||
|
return end == group.end && begin == group.begin && spacing == group.spacing && max == group.max
|
||||||
|
&& Float.compare(group.unitScaling, unitScaling) == 0 && Float.compare(group.shields, shields) == 0
|
||||||
|
&& Float.compare(group.shieldScaling, shieldScaling) == 0 && unitAmount == group.unitAmount &&
|
||||||
|
type == group.type && effect == group.effect && Structs.eq(items, group.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode(){
|
||||||
|
return Arrays.hashCode(new Object[]{type, end, begin, spacing, max, unitScaling, shields, shieldScaling, unitAmount, effect, items});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class Teams{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void count(Unit unit){
|
private void count(Unit unit){
|
||||||
unit.team.data().updateCount(unit.type(), 1);
|
unit.team.data().updateCount(unit.type, 1);
|
||||||
|
|
||||||
if(unit instanceof Payloadc){
|
if(unit instanceof Payloadc){
|
||||||
((Payloadc)unit).payloads().each(p -> {
|
((Payloadc)unit).payloads().each(p -> {
|
||||||
@@ -178,15 +178,15 @@ public class Teams{
|
|||||||
data.units.add(unit);
|
data.units.add(unit);
|
||||||
data.presentFlag = true;
|
data.presentFlag = true;
|
||||||
|
|
||||||
if(data.unitsByType == null || data.unitsByType.length <= unit.type().id){
|
if(data.unitsByType == null || data.unitsByType.length <= unit.type.id){
|
||||||
data.unitsByType = new Seq[content.units().size];
|
data.unitsByType = new Seq[content.units().size];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.unitsByType[unit.type().id] == null){
|
if(data.unitsByType[unit.type.id] == null){
|
||||||
data.unitsByType[unit.type().id] = new Seq<>();
|
data.unitsByType[unit.type.id] = new Seq<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
data.unitsByType[unit.type().id].add(unit);
|
data.unitsByType[unit.type.id].add(unit);
|
||||||
|
|
||||||
count(unit);
|
count(unit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,8 +194,8 @@ public class Universe{
|
|||||||
|
|
||||||
//queue random invasions
|
//queue random invasions
|
||||||
if(!sector.isAttacked() && turn > invasionGracePeriod){
|
if(!sector.isAttacked() && turn > invasionGracePeriod){
|
||||||
//TODO use factors like difficulty for better invasion chance
|
//invasion chance depends on # of nearby bases
|
||||||
if(sector.near().contains(Sector::hasEnemyBase) && Mathf.chance(baseInvasionChance)){
|
if(Mathf.chance(baseInvasionChance * sector.near().count(Sector::hasEnemyBase))){
|
||||||
int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : 0) + Mathf.random(2, 5) * 5;
|
int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : 0) + Mathf.random(2, 5) * 5;
|
||||||
|
|
||||||
//assign invasion-related things
|
//assign invasion-related things
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class BlockRenderer implements Disposable{
|
|||||||
private FrameBuffer dark = new FrameBuffer();
|
private FrameBuffer dark = new FrameBuffer();
|
||||||
private Seq<Building> outArray2 = new Seq<>();
|
private Seq<Building> outArray2 = new Seq<>();
|
||||||
private Seq<Tile> shadowEvents = new Seq<>();
|
private Seq<Tile> shadowEvents = new Seq<>();
|
||||||
private IntSet processedEntities = new IntSet(), processedLinks = new IntSet();
|
private IntSet procEntities = new IntSet(), procLinks = new IntSet(), procLights = new IntSet();
|
||||||
private boolean displayStatus = false;
|
private boolean displayStatus = false;
|
||||||
|
|
||||||
public BlockRenderer(){
|
public BlockRenderer(){
|
||||||
@@ -191,8 +191,9 @@ public class BlockRenderer implements Disposable{
|
|||||||
|
|
||||||
tileview.clear();
|
tileview.clear();
|
||||||
lightview.clear();
|
lightview.clear();
|
||||||
processedEntities.clear();
|
procEntities.clear();
|
||||||
processedLinks.clear();
|
procLinks.clear();
|
||||||
|
procLights.clear();
|
||||||
|
|
||||||
int minx = Math.max(avgx - rangex - expandr, 0);
|
int minx = Math.max(avgx - rangex - expandr, 0);
|
||||||
int miny = Math.max(avgy - rangey - expandr, 0);
|
int miny = Math.max(avgy - rangey - expandr, 0);
|
||||||
@@ -209,25 +210,25 @@ public class BlockRenderer implements Disposable{
|
|||||||
tile = tile.build.tile;
|
tile = tile.build.tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !processedEntities.contains(tile.build.id))){
|
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !procEntities.contains(tile.build.id))){
|
||||||
if(block.expanded || !expanded){
|
if(block.expanded || !expanded){
|
||||||
if(tile.build == null || processedLinks.add(tile.build.id)){
|
if(tile.build == null || procLinks.add(tile.build.id)){
|
||||||
tileview.add(tile);
|
tileview.add(tile);
|
||||||
if(tile.build != null){
|
if(tile.build != null){
|
||||||
processedEntities.add(tile.build.id);
|
procEntities.add(tile.build.id);
|
||||||
processedLinks.add(tile.build.id);
|
procLinks.add(tile.build.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//lights are drawn even in the expanded range
|
//lights are drawn even in the expanded range
|
||||||
if(tile.build != null || tile.block().emitLight){
|
if(((tile.build != null && procLights.add(tile.build.pos())) || tile.block().emitLight)){
|
||||||
lightview.add(tile);
|
lightview.add(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tile.build != null && tile.build.power != null && tile.build.power.links.size > 0){
|
if(tile.build != null && tile.build.power != null && tile.build.power.links.size > 0){
|
||||||
for(Building other : tile.build.getPowerConnections(outArray2)){
|
for(Building other : tile.build.getPowerConnections(outArray2)){
|
||||||
if(other.block instanceof PowerNode && processedLinks.add(other.id)){ //TODO need a generic way to render connections!
|
if(other.block instanceof PowerNode && procLinks.add(other.id)){ //TODO need a generic way to render connections!
|
||||||
tileview.add(other.tile);
|
tileview.add(other.tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,7 +236,7 @@ public class BlockRenderer implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//special case for floors
|
//special case for floors
|
||||||
if(block == Blocks.air && tile.floor().emitLight){
|
if((block == Blocks.air && tile.floor().emitLight) && procLights.add(tile.pos())){
|
||||||
lightview.add(tile);
|
lightview.add(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ public class LightRenderer{
|
|||||||
|
|
||||||
Draw.vert(ledge.texture, vertices, 0, vertices.length);
|
Draw.vert(ledge.texture, vertices, 0, vertices.length);
|
||||||
|
|
||||||
|
|
||||||
Vec2 v3 = Tmp.v2.trnsExact(rot, stroke);
|
Vec2 v3 = Tmp.v2.trnsExact(rot, stroke);
|
||||||
|
|
||||||
u = ledge.u;
|
u = ledge.u;
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class MinimapRenderer implements Disposable{
|
|||||||
|
|
||||||
Draw.mixcol(unit.team().color, 1f);
|
Draw.mixcol(unit.team().color, 1f);
|
||||||
float scale = Scl.scl(1f) / 2f * scaling * 32f;
|
float scale = Scl.scl(1f) / 2f * scaling * 32f;
|
||||||
Draw.rect(unit.type().icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90);
|
Draw.rect(unit.type.icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90);
|
||||||
Draw.reset();
|
Draw.reset();
|
||||||
|
|
||||||
//only disable player names in multiplayer
|
//only disable player names in multiplayer
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public class OverlayRenderer{
|
|||||||
//special selection for block "units"
|
//special selection for block "units"
|
||||||
Fill.square(select.x, select.y, ((BlockUnitc)select).tile().block.size * tilesize/2f);
|
Fill.square(select.x, select.y, ((BlockUnitc)select).tile().block.size * tilesize/2f);
|
||||||
}else{
|
}else{
|
||||||
Draw.rect(select.type().icon(Cicon.full), select.x(), select.y(), select.rotation() - 90);
|
Draw.rect(select.type.icon(Cicon.full), select.x(), select.y(), select.rotation() - 90);
|
||||||
}
|
}
|
||||||
|
|
||||||
Lines.stroke(unitFade);
|
Lines.stroke(unitFade);
|
||||||
|
|||||||
@@ -22,15 +22,16 @@ public class PlanetGrid{
|
|||||||
{5, 3, 10, 1, 4}, {2, 5, 4, 0, 11}, {3, 7, 6, 1, 8}, {7, 2, 9, 0, 6}
|
{5, 3, 10, 1, 4}, {2, 5, 4, 0, 11}, {3, 7, 6, 1, 8}, {7, 2, 9, 0, 6}
|
||||||
};
|
};
|
||||||
|
|
||||||
public final int size;
|
public int size;
|
||||||
public final Ptile[] tiles;
|
public Ptile[] tiles;
|
||||||
public final Corner[] corners;
|
public Corner[] corners;
|
||||||
public final Edge[] edges;
|
public Edge[] edges;
|
||||||
|
|
||||||
PlanetGrid(int size){
|
//this is protected so if you want to make strange grids you should know what you're doing.
|
||||||
|
protected PlanetGrid(int size){
|
||||||
this.size = size;
|
this.size = size;
|
||||||
|
|
||||||
tiles = new Ptile[Buildingount(size)];
|
tiles = new Ptile[tileCount(size)];
|
||||||
for(int i = 0; i < tiles.length; i++){
|
for(int i = 0; i < tiles.length; i++){
|
||||||
tiles[i] = new Ptile(i, i < 12 ? 5 : 6);
|
tiles[i] = new Ptile(i, i < 12 ? 5 : 6);
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ public class PlanetGrid{
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PlanetGrid initialGrid(){
|
public static PlanetGrid initialGrid(){
|
||||||
PlanetGrid grid = new PlanetGrid(0);
|
PlanetGrid grid = new PlanetGrid(0);
|
||||||
|
|
||||||
for(Ptile t : grid.tiles){
|
for(Ptile t : grid.tiles){
|
||||||
@@ -111,7 +112,7 @@ public class PlanetGrid{
|
|||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PlanetGrid subdividedGrid(PlanetGrid prev){
|
public static PlanetGrid subdividedGrid(PlanetGrid prev){
|
||||||
PlanetGrid grid = new PlanetGrid(prev.size + 1);
|
PlanetGrid grid = new PlanetGrid(prev.size + 1);
|
||||||
|
|
||||||
int prevTiles = prev.tiles.length;
|
int prevTiles = prev.tiles.length;
|
||||||
@@ -207,7 +208,7 @@ public class PlanetGrid{
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Buildingount(int size){
|
static int tileCount(int size){
|
||||||
return 10 * Mathf.pow(3, size) + 2;
|
return 10 * Mathf.pow(3, size) + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,12 +221,12 @@ public class PlanetGrid{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Ptile{
|
public static class Ptile{
|
||||||
public final int id;
|
public int id;
|
||||||
public final int edgeCount;
|
public int edgeCount;
|
||||||
|
|
||||||
public final Ptile[] tiles;
|
public Ptile[] tiles;
|
||||||
public final Corner[] corners;
|
public Corner[] corners;
|
||||||
public final Edge[] edges;
|
public Edge[] edges;
|
||||||
|
|
||||||
public Vec3 v = new Vec3();
|
public Vec3 v = new Vec3();
|
||||||
|
|
||||||
@@ -240,11 +241,11 @@ public class PlanetGrid{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Corner{
|
public static class Corner{
|
||||||
public final int id;
|
public int id;
|
||||||
public final Ptile[] tiles = new Ptile[3];
|
public Ptile[] tiles = new Ptile[3];
|
||||||
public final Corner[] corners = new Corner[3];
|
public Corner[] corners = new Corner[3];
|
||||||
public final Edge[] edges = new Edge[3];
|
public Edge[] edges = new Edge[3];
|
||||||
public final Vec3 v = new Vec3();
|
public Vec3 v = new Vec3();
|
||||||
|
|
||||||
public Corner(int id){
|
public Corner(int id){
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -252,9 +253,9 @@ public class PlanetGrid{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Edge{
|
public static class Edge{
|
||||||
public final int id;
|
public int id;
|
||||||
public final Ptile[] tiles = new Ptile[2];
|
public Ptile[] tiles = new Ptile[2];
|
||||||
public final Corner[] corners = new Corner[2];
|
public Corner[] corners = new Corner[2];
|
||||||
|
|
||||||
public Edge(int id){
|
public Edge(int id){
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|||||||
@@ -599,11 +599,11 @@ public class DesktopInput extends InputHandler{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void updateMovement(Unit unit){
|
protected void updateMovement(Unit unit){
|
||||||
boolean omni = unit.type().omniMovement;
|
boolean omni = unit.type.omniMovement;
|
||||||
boolean ground = unit.isGrounded();
|
boolean ground = unit.isGrounded();
|
||||||
|
|
||||||
float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type().strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f);
|
float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type.strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f);
|
||||||
float baseSpeed = unit.type().speed;
|
float baseSpeed = unit.type.speed;
|
||||||
|
|
||||||
//limit speed to minimum formation speed to preserve formation
|
//limit speed to minimum formation speed to preserve formation
|
||||||
if(unit.isCommanding()){
|
if(unit.isCommanding()){
|
||||||
@@ -611,7 +611,7 @@ public class DesktopInput extends InputHandler{
|
|||||||
baseSpeed = unit.minFormationSpeed * 0.95f;
|
baseSpeed = unit.minFormationSpeed * 0.95f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : unit.type().canBoost ? unit.type().boostMultiplier : 1f, unit.elevation) * strafePenalty;
|
float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : unit.type.canBoost ? unit.type.boostMultiplier : 1f, unit.elevation) * strafePenalty;
|
||||||
float xa = Core.input.axis(Binding.move_x);
|
float xa = Core.input.axis(Binding.move_x);
|
||||||
float ya = Core.input.axis(Binding.move_y);
|
float ya = Core.input.axis(Binding.move_y);
|
||||||
boolean boosted = (unit instanceof Mechc && unit.isFlying());
|
boolean boosted = (unit instanceof Mechc && unit.isFlying());
|
||||||
@@ -622,7 +622,7 @@ public class DesktopInput extends InputHandler{
|
|||||||
}
|
}
|
||||||
|
|
||||||
float mouseAngle = Angles.mouseAngle(unit.x, unit.y);
|
float mouseAngle = Angles.mouseAngle(unit.x, unit.y);
|
||||||
boolean aimCursor = omni && player.shooting && unit.type().hasWeapons() && unit.type().faceTarget && !boosted && unit.type().rotateShooting;
|
boolean aimCursor = omni && player.shooting && unit.type.hasWeapons() && unit.type.faceTarget && !boosted && unit.type.rotateShooting;
|
||||||
|
|
||||||
if(aimCursor){
|
if(aimCursor){
|
||||||
unit.lookAt(mouseAngle);
|
unit.lookAt(mouseAngle);
|
||||||
@@ -637,11 +637,11 @@ public class DesktopInput extends InputHandler{
|
|||||||
}else{
|
}else{
|
||||||
unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len()));
|
unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len()));
|
||||||
if(!movement.isZero() && ground){
|
if(!movement.isZero() && ground){
|
||||||
unit.vel.rotateTo(movement.angle(), unit.type().rotateSpeed);
|
unit.vel.rotateTo(movement.angle(), unit.type.rotateSpeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unit.aim(unit.type().faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y));
|
unit.aim(unit.type.faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y));
|
||||||
unit.controlWeapons(true, player.shooting && !boosted);
|
unit.controlWeapons(true, player.shooting && !boosted);
|
||||||
|
|
||||||
player.boosting = Core.input.keyDown(Binding.boost) && !movement.isZero();
|
player.boosting = Core.input.keyDown(Binding.boost) && !movement.isZero();
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
Payloadc pay = (Payloadc)unit;
|
Payloadc pay = (Payloadc)unit;
|
||||||
|
|
||||||
if(target.isAI() && target.isGrounded() && pay.canPickup(target)
|
if(target.isAI() && target.isGrounded() && pay.canPickup(target)
|
||||||
&& target.within(unit, unit.type().hitSize * 2f + target.type().hitSize * 2f)){
|
&& target.within(unit, unit.type.hitSize * 2f + target.type.hitSize * 2f)){
|
||||||
Call.pickedUnitPayload(unit, target);
|
Call.pickedUnitPayload(unit, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,7 +365,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
|
|
||||||
if(commander.isCommanding()){
|
if(commander.isCommanding()){
|
||||||
commander.clearCommand();
|
commander.clearCommand();
|
||||||
}else if(player.unit().type().commandLimit > 0){
|
}else if(player.unit().type.commandLimit > 0){
|
||||||
|
|
||||||
//TODO try out some other formations
|
//TODO try out some other formations
|
||||||
commander.commandNearby(new CircleFormation());
|
commander.commandNearby(new CircleFormation());
|
||||||
@@ -398,17 +398,17 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && player.unit().ammo <= 0){
|
if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && player.unit().ammo <= 0){
|
||||||
player.unit().type().weapons.first().noAmmoSound.at(player.unit());
|
player.unit().type.weapons.first().noAmmoSound.at(player.unit());
|
||||||
}
|
}
|
||||||
|
|
||||||
wasShooting = player.shooting;
|
wasShooting = player.shooting;
|
||||||
|
|
||||||
if(!player.dead()){
|
if(!player.dead()){
|
||||||
controlledType = player.unit().type();
|
controlledType = player.unit().type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(controlledType != null && player.dead()){
|
if(controlledType != null && player.dead()){
|
||||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
|
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type == controlledType && !u.dead);
|
||||||
|
|
||||||
if(unit != null){
|
if(unit != null){
|
||||||
Call.unitControl(player, unit);
|
Call.unitControl(player, unit);
|
||||||
@@ -418,7 +418,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
|
|
||||||
public void checkUnit(){
|
public void checkUnit(){
|
||||||
if(controlledType != null){
|
if(controlledType != null){
|
||||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
|
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type == controlledType && !u.dead);
|
||||||
if(unit == null && controlledType == UnitTypes.block){
|
if(unit == null && controlledType == UnitTypes.block){
|
||||||
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
|
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
|
||||||
}
|
}
|
||||||
@@ -437,7 +437,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
Unit unit = player.unit();
|
Unit unit = player.unit();
|
||||||
if(!(unit instanceof Payloadc pay)) return;
|
if(!(unit instanceof Payloadc pay)) return;
|
||||||
|
|
||||||
Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type().hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type.hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
||||||
if(target != null){
|
if(target != null){
|
||||||
Call.requestUnitPayload(player, target);
|
Call.requestUnitPayload(player, target);
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
if(tile != null && player.team().isEnemy(tile.team)){
|
if(tile != null && player.team().isEnemy(tile.team)){
|
||||||
player.miner().mineTile(null);
|
player.miner().mineTile(null);
|
||||||
target = tile;
|
target = tile;
|
||||||
}else if(tile != null && player.unit().type().canHeal && tile.team == player.team() && tile.damaged()){
|
}else if(tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged()){
|
||||||
player.miner().mineTile(null);
|
player.miner().mineTile(null);
|
||||||
target = tile;
|
target = tile;
|
||||||
}
|
}
|
||||||
@@ -834,10 +834,10 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
protected void updateMovement(Unit unit){
|
protected void updateMovement(Unit unit){
|
||||||
Rect rect = Tmp.r3;
|
Rect rect = Tmp.r3;
|
||||||
|
|
||||||
UnitType type = unit.type();
|
UnitType type = unit.type;
|
||||||
if(type == null) return;
|
if(type == null) return;
|
||||||
|
|
||||||
boolean omni = unit.type().omniMovement;
|
boolean omni = unit.type.omniMovement;
|
||||||
boolean legs = unit.isGrounded();
|
boolean legs = unit.isGrounded();
|
||||||
boolean allowHealing = type.canHeal;
|
boolean allowHealing = type.canHeal;
|
||||||
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&
|
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&
|
||||||
@@ -855,7 +855,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
float attractDst = 15f;
|
float attractDst = 15f;
|
||||||
float strafePenalty = legs ? 1f : Mathf.lerp(1f, type.strafePenalty, Angles.angleDist(unit.vel.angle(), unit.rotation) / 180f);
|
float strafePenalty = legs ? 1f : Mathf.lerp(1f, type.strafePenalty, Angles.angleDist(unit.vel.angle(), unit.rotation) / 180f);
|
||||||
|
|
||||||
float baseSpeed = unit.type().speed;
|
float baseSpeed = unit.type.speed;
|
||||||
|
|
||||||
//limit speed to minimum formation speed to preserve formation
|
//limit speed to minimum formation speed to preserve formation
|
||||||
if(unit.isCommanding()){
|
if(unit.isCommanding()){
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ public class LExecutor{
|
|||||||
if(unit instanceof Payloadc pay){
|
if(unit instanceof Payloadc pay){
|
||||||
//units
|
//units
|
||||||
if(exec.bool(p1)){
|
if(exec.bool(p1)){
|
||||||
Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type().hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type.hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
||||||
|
|
||||||
if(result != null){
|
if(result != null){
|
||||||
Call.pickedUnitPayload(unit, result);
|
Call.pickedUnitPayload(unit, result);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import mindustry.entities.abilities.*;
|
|||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.logic.*;
|
import mindustry.logic.*;
|
||||||
|
import mindustry.type.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
import mindustry.world.blocks.defense.*;
|
import mindustry.world.blocks.defense.*;
|
||||||
import mindustry.world.blocks.defense.turrets.*;
|
import mindustry.world.blocks.defense.turrets.*;
|
||||||
@@ -252,13 +253,16 @@ public class SectorDamage{
|
|||||||
if(unit.isPlayer()) continue;
|
if(unit.isPlayer()) continue;
|
||||||
|
|
||||||
if(unit.team == state.rules.defaultTeam){
|
if(unit.team == state.rules.defaultTeam){
|
||||||
sumHealth += unit.health + unit.shield;
|
//scale health based on armor - yes, this is inaccurate, but better than nothing
|
||||||
sumDps += unit.type().dpsEstimate;
|
float healthMult = 1f + Mathf.clamp(unit.armor / 20f);
|
||||||
|
|
||||||
|
sumHealth += unit.health*healthMult + unit.shield;
|
||||||
|
sumDps += unit.type.dpsEstimate;
|
||||||
if(unit.abilities.find(a -> a instanceof HealFieldAbility) instanceof HealFieldAbility h){
|
if(unit.abilities.find(a -> a instanceof HealFieldAbility) instanceof HealFieldAbility h){
|
||||||
sumRps += h.amount / h.reload * 60f;
|
sumRps += h.amount / h.reload * 60f;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
curEnemyDps += unit.type().dpsEstimate;
|
curEnemyDps += unit.type.dpsEstimate;
|
||||||
curEnemyHealth += unit.health;
|
curEnemyHealth += unit.health;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,10 +281,12 @@ public class SectorDamage{
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(SpawnGroup group : state.rules.spawns){
|
for(SpawnGroup group : state.rules.spawns){
|
||||||
|
float healthMult = 1f + Mathf.clamp(group.type.armor / 20f);
|
||||||
|
StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect);
|
||||||
int spawned = group.getSpawned(wave);
|
int spawned = group.getSpawned(wave);
|
||||||
if(spawned <= 0) continue;
|
if(spawned <= 0) continue;
|
||||||
sumWaveHealth += spawned * (group.getShield(wave) + group.type.health);
|
sumWaveHealth += spawned * (group.getShield(wave) + group.type.health * effect.healthMultiplier * healthMult);
|
||||||
sumWaveDps += spawned * group.type.dpsEstimate;
|
sumWaveDps += spawned * group.type.dpsEstimate * effect.damageMultiplier;
|
||||||
}
|
}
|
||||||
waveDps.add(new Vec2(wave, sumWaveDps));
|
waveDps.add(new Vec2(wave, sumWaveDps));
|
||||||
waveHealth.add(new Vec2(wave, sumWaveHealth));
|
waveHealth.add(new Vec2(wave, sumWaveHealth));
|
||||||
@@ -295,7 +301,8 @@ public class SectorDamage{
|
|||||||
info.waveDpsBase = reg.intercept;
|
info.waveDpsBase = reg.intercept;
|
||||||
info.waveDpsSlope = reg.slope;
|
info.waveDpsSlope = reg.slope;
|
||||||
|
|
||||||
info.sumHealth = sumHealth;
|
//enemy units like to aim for a lot of non-essential things, so increase resulting health slightly
|
||||||
|
info.sumHealth = sumHealth * 1.2f;
|
||||||
info.sumDps = sumDps;
|
info.sumDps = sumDps;
|
||||||
info.sumRps = sumRps;
|
info.sumRps = sumRps;
|
||||||
|
|
||||||
@@ -328,7 +335,7 @@ public class SectorDamage{
|
|||||||
int radius = 3;
|
int radius = 3;
|
||||||
|
|
||||||
//only penetrate a certain % by health, not by distance
|
//only penetrate a certain % by health, not by distance
|
||||||
float totalHealth = path.sumf(t -> {
|
float totalHealth = damage >= 1f ? 1f : path.sumf(t -> {
|
||||||
float s = 0;
|
float s = 0;
|
||||||
for(int dx = -radius; dx <= radius; dx++){
|
for(int dx = -radius; dx <= radius; dx++){
|
||||||
for(int dy = -radius; dy <= radius; dy++){
|
for(int dy = -radius; dy <= radius; dy++){
|
||||||
@@ -345,7 +352,7 @@ public class SectorDamage{
|
|||||||
float healthCount = 0;
|
float healthCount = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
for(int i = 0; i < path.size && healthCount < targetHealth; i++){
|
for(int i = 0; i < path.size && (healthCount < targetHealth || damage >= 1f); i++){
|
||||||
Tile t = path.get(i);
|
Tile t = path.get(i);
|
||||||
|
|
||||||
for(int dx = -radius; dx <= radius; dx++){
|
for(int dx = -radius; dx <= radius; dx++){
|
||||||
@@ -365,7 +372,7 @@ public class SectorDamage{
|
|||||||
|
|
||||||
removal.add(other.build);
|
removal.add(other.build);
|
||||||
|
|
||||||
if(healthCount >= targetHealth){
|
if(healthCount >= targetHealth && damage < 0.999f){
|
||||||
break out;
|
break out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,6 +383,7 @@ public class SectorDamage{
|
|||||||
|
|
||||||
for(Building r : removal){
|
for(Building r : removal){
|
||||||
if(r.tile.build == r){
|
if(r.tile.build == r){
|
||||||
|
r.addPlan(false);
|
||||||
r.tile.remove();
|
r.tile.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,6 +432,7 @@ public class SectorDamage{
|
|||||||
Effect.rubble(other.build.x, other.build.y, other.block().size);
|
Effect.rubble(other.build.x, other.build.y, other.block().size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
other.build.addPlan(false);
|
||||||
other.remove();
|
other.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -412,12 +412,12 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
|||||||
if(sector.hasEnemyBase()){
|
if(sector.hasEnemyBase()){
|
||||||
basegen.generate(tiles, enemies.map(r -> tiles.getn(r.x, r.y)), tiles.get(spawn.x, spawn.y), state.rules.waveTeam, sector, difficulty);
|
basegen.generate(tiles, enemies.map(r -> tiles.getn(r.x, r.y)), tiles.get(spawn.x, spawn.y), state.rules.waveTeam, sector, difficulty);
|
||||||
|
|
||||||
state.rules.attackMode = true;
|
state.rules.attackMode = sector.info.attack = true;
|
||||||
}else{
|
}else{
|
||||||
state.rules.winWave = 15 * (int)Math.max(difficulty * 10, 1);
|
state.rules.winWave = sector.info.winWave = 15 * (int)Math.max(difficulty * 10, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.rules.waves = true;
|
state.rules.waves = sector.info.waves = true;
|
||||||
|
|
||||||
//TODO better waves
|
//TODO better waves
|
||||||
state.rules.spawns = DefaultWaves.generate(difficulty);
|
state.rules.spawns = DefaultWaves.generate(difficulty);
|
||||||
|
|||||||
@@ -260,8 +260,8 @@ public class ContentParser{
|
|||||||
//TODO test this!
|
//TODO test this!
|
||||||
read(() -> {
|
read(() -> {
|
||||||
//add reconstructor type
|
//add reconstructor type
|
||||||
if(value.hasChild("requirements")){
|
if(value.has("requirements")){
|
||||||
JsonValue rec = value.remove("requirements");
|
JsonValue rec = value.remove("requirements");
|
||||||
|
|
||||||
//intermediate class for parsing
|
//intermediate class for parsing
|
||||||
class UnitReq{
|
class UnitReq{
|
||||||
@@ -286,6 +286,17 @@ public class ContentParser{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//read extra default waves
|
||||||
|
if(value.has("waves")){
|
||||||
|
JsonValue waves = value.remove("waves");
|
||||||
|
SpawnGroup[] groups = parser.readValue(SpawnGroup[].class, waves);
|
||||||
|
for(SpawnGroup group : groups){
|
||||||
|
group.type = unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vars.defaultWaves.get().addAll(groups);
|
||||||
|
}
|
||||||
|
|
||||||
readFields(unit, value, true);
|
readFields(unit, value, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ public class AmmoTypes implements ContentList{
|
|||||||
|
|
||||||
if(build.block.consumes.hasPower() && build.block.consumes.getPower().buffered){
|
if(build.block.consumes.hasPower() && build.block.consumes.getPower().buffered){
|
||||||
float amount = closest.build.power.status * build.block.consumes.getPower().capacity;
|
float amount = closest.build.power.status * build.block.consumes.getPower().capacity;
|
||||||
float powerPerAmmo = totalPower / unit.type().ammoCapacity;
|
float powerPerAmmo = totalPower / unit.type.ammoCapacity;
|
||||||
float ammoRequired = unit.type().ammoCapacity - unit.ammo;
|
float ammoRequired = unit.type.ammoCapacity - unit.ammo;
|
||||||
float powerRequired = ammoRequired * powerPerAmmo;
|
float powerRequired = ammoRequired * powerPerAmmo;
|
||||||
float powerTaken = Math.min(amount, powerRequired);
|
float powerTaken = Math.min(amount, powerRequired);
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ public class ItemStack implements Comparable<ItemStack>{
|
|||||||
return item.compareTo(itemStack.item);
|
return item.compareTo(itemStack.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o){
|
||||||
|
if(this == o) return true;
|
||||||
|
if(!(o instanceof ItemStack stack)) return false;
|
||||||
|
return amount == stack.amount && item == stack.item;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
return "ItemStack{" +
|
return "ItemStack{" +
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class SectorPreset extends UnlockableContent{
|
|||||||
super(name);
|
super(name);
|
||||||
this.generator = new FileMapGenerator(name);
|
this.generator = new FileMapGenerator(name);
|
||||||
this.planet = planet;
|
this.planet = planet;
|
||||||
|
sector %= planet.sectors.size;
|
||||||
this.sector = planet.sectors.get(sector);
|
this.sector = planet.sectors.get(sector);
|
||||||
|
|
||||||
planet.preset(sector, this);
|
planet.preset(sector, this);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class StatusEffect extends MappableContent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(effect != Fx.none && Mathf.chanceDelta(effectChance)){
|
if(effect != Fx.none && Mathf.chanceDelta(effectChance)){
|
||||||
Tmp.v1.rnd(unit.type().hitSize /2f);
|
Tmp.v1.rnd(unit.type.hitSize /2f);
|
||||||
effect.at(unit.x + Tmp.v1.x, unit.y + Tmp.v1.y);
|
effect.at(unit.x + Tmp.v1.x, unit.y + Tmp.v1.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ public class UnitType extends UnlockableContent{
|
|||||||
public Unit create(Team team){
|
public Unit create(Team team){
|
||||||
Unit unit = constructor.get();
|
Unit unit = constructor.get();
|
||||||
unit.team = team;
|
unit.team = team;
|
||||||
unit.type(this);
|
unit.setType(this);
|
||||||
unit.ammo = ammoCapacity; //fill up on ammo upon creation
|
unit.ammo = ammoCapacity; //fill up on ammo upon creation
|
||||||
unit.elevation = flying ? 1f : 0;
|
unit.elevation = flying ? 1f : 0;
|
||||||
unit.heal();
|
unit.heal();
|
||||||
|
|||||||
@@ -339,9 +339,6 @@ public class SettingsMenuDialog extends SettingsDialog{
|
|||||||
graphics.checkPref("smoothcamera", true);
|
graphics.checkPref("smoothcamera", true);
|
||||||
graphics.checkPref("position", false);
|
graphics.checkPref("position", false);
|
||||||
graphics.checkPref("fps", false);
|
graphics.checkPref("fps", false);
|
||||||
if(!mobile){
|
|
||||||
graphics.checkPref("blockselectkeys", true);
|
|
||||||
}
|
|
||||||
graphics.checkPref("playerindicators", true);
|
graphics.checkPref("playerindicators", true);
|
||||||
graphics.checkPref("indicators", true);
|
graphics.checkPref("indicators", true);
|
||||||
graphics.checkPref("animatedwater", true);
|
graphics.checkPref("animatedwater", true);
|
||||||
|
|||||||
@@ -712,7 +712,7 @@ public class HudFragment extends Fragment{
|
|||||||
t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
|
t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
|
||||||
t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f);
|
t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f);
|
||||||
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> {
|
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> {
|
||||||
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type().ammoType.color : Pal.health);
|
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color : Pal.health);
|
||||||
});
|
});
|
||||||
|
|
||||||
t.getChildren().get(1).toFront();
|
t.getChildren().get(1).toFront();
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ public class PlacementFragment extends Fragment{
|
|||||||
|
|
||||||
topTable.table(header -> {
|
topTable.table(header -> {
|
||||||
String keyCombo = "";
|
String keyCombo = "";
|
||||||
if(!mobile && Core.settings.getBool("blockselectkeys")){
|
if(!mobile){
|
||||||
Seq<Block> blocks = getByCategory(currentCategory);
|
Seq<Block> blocks = getByCategory(currentCategory);
|
||||||
for(int i = 0; i < blocks.size; i++){
|
for(int i = 0; i < blocks.size; i++){
|
||||||
if(blocks.get(i) == displayBlock && (i + 1) / 10 - 1 < blockSelect.length){
|
if(blocks.get(i) == displayBlock && (i + 1) / 10 - 1 < blockSelect.length){
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ public class LaunchPad extends Block{
|
|||||||
public void display(Table table){
|
public void display(Table table){
|
||||||
super.display(table);
|
super.display(table);
|
||||||
|
|
||||||
|
if(!state.isCampaign()) return;
|
||||||
|
|
||||||
table.row();
|
table.row();
|
||||||
table.label(() -> {
|
table.label(() -> {
|
||||||
Sector dest = state.secinfo.getRealDestination();
|
Sector dest = state.secinfo.getRealDestination();
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public class TractorBeamTurret extends Block{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//look at target
|
//look at target
|
||||||
if(target != null && target.within(this, range) && target.team() != team && target.type().flying && efficiency() > 0.01f){
|
if(target != null && target.within(this, range) && target.team() != team && target.type.flying && efficiency() > 0.01f){
|
||||||
any = true;
|
any = true;
|
||||||
float dest = angleTo(target);
|
float dest = angleTo(target);
|
||||||
rotation = Angles.moveToward(rotation, dest, rotateSpeed * edelta());
|
rotation = Angles.moveToward(rotation, dest, rotateSpeed * edelta());
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class UnitPayload implements Payload{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean dump(){
|
public boolean dump(){
|
||||||
if(!Units.canCreate(unit.team, unit.type())){
|
if(!Units.canCreate(unit.team, unit.type)){
|
||||||
deactiveTime = 1f;
|
deactiveTime = 1f;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ public class UnitPayload implements Payload{
|
|||||||
@Override
|
@Override
|
||||||
public void draw(){
|
public void draw(){
|
||||||
Drawf.shadow(unit.x, unit.y, 20);
|
Drawf.shadow(unit.x, unit.y, 20);
|
||||||
Draw.rect(unit.type().icon(Cicon.full), unit.x, unit.y, unit.rotation - 90);
|
Draw.rect(unit.type.icon(Cicon.full), unit.x, unit.y, unit.rotation - 90);
|
||||||
|
|
||||||
//draw warning
|
//draw warning
|
||||||
if(deactiveTime > 0){
|
if(deactiveTime > 0){
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ public class CoreBlock extends StorageBlock{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void itemTaken(Item item){
|
public void itemTaken(Item item){
|
||||||
if(state.isCampaign()){
|
if(state.isCampaign() && team == state.rules.defaultTeam){
|
||||||
//update item taken amount
|
//update item taken amount
|
||||||
state.secinfo.handleCoreItem(item, -1);
|
state.secinfo.handleCoreItem(item, -1);
|
||||||
}
|
}
|
||||||
@@ -342,7 +342,9 @@ public class CoreBlock extends StorageBlock{
|
|||||||
@Override
|
@Override
|
||||||
public void handleItem(Building source, Item item){
|
public void handleItem(Building source, Item item){
|
||||||
if(net.server() || !net.active()){
|
if(net.server() || !net.active()){
|
||||||
state.secinfo.handleCoreItem(item, 1);
|
if(team == state.rules.defaultTeam){
|
||||||
|
state.secinfo.handleCoreItem(item, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if(items.get(item) >= getMaximumAccepted(item)){
|
if(items.get(item) >= getMaximumAccepted(item)){
|
||||||
//create item incineration effect at random intervals
|
//create item incineration effect at random intervals
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class StorageBlock extends Block{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void incinerateEffect(Building self, Building source){
|
public static void incinerateEffect(Building self, Building source){
|
||||||
if(Mathf.chance(0.1)){
|
if(Mathf.chance(0.3)){
|
||||||
Tile edge = Edges.getFacingEdge(source, self);
|
Tile edge = Edges.getFacingEdge(source, self);
|
||||||
Tile edge2 = Edges.getFacingEdge(self, source);
|
Tile edge2 = Edges.getFacingEdge(self, source);
|
||||||
if(edge != null && edge2 != null){
|
if(edge != null && edge2 != null){
|
||||||
@@ -46,7 +46,9 @@ public class StorageBlock extends Block{
|
|||||||
@Override
|
@Override
|
||||||
public void handleItem(Building source, Item item){
|
public void handleItem(Building source, Item item){
|
||||||
if(linkedCore != null){
|
if(linkedCore != null){
|
||||||
incinerateEffect(this, source);
|
if(linkedCore.items.get(item) >= ((CoreBuild)linkedCore).storageCapacity){
|
||||||
|
incinerateEffect(this, source);
|
||||||
|
}
|
||||||
((CoreBuild)linkedCore).noEffect = true;
|
((CoreBuild)linkedCore).noEffect = true;
|
||||||
linkedCore.handleItem(source, item);
|
linkedCore.handleItem(source, item);
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class Reconstructor extends UnitBlock{
|
|||||||
return this.payload == null
|
return this.payload == null
|
||||||
&& relativeTo(source) != rotation
|
&& relativeTo(source) != rotation
|
||||||
&& payload instanceof UnitPayload
|
&& payload instanceof UnitPayload
|
||||||
&& hasUpgrade(((UnitPayload)payload).unit.type());
|
&& hasUpgrade(((UnitPayload)payload).unit.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -114,9 +114,9 @@ public class Reconstructor extends UnitBlock{
|
|||||||
if(constructing() && hasArrived()){
|
if(constructing() && hasArrived()){
|
||||||
Draw.draw(Layer.blockOver, () -> {
|
Draw.draw(Layer.blockOver, () -> {
|
||||||
Draw.alpha(1f - progress/ constructTime);
|
Draw.alpha(1f - progress/ constructTime);
|
||||||
Draw.rect(payload.unit.type().icon(Cicon.full), x, y, rotdeg() - 90);
|
Draw.rect(payload.unit.type.icon(Cicon.full), x, y, rotdeg() - 90);
|
||||||
Draw.reset();
|
Draw.reset();
|
||||||
Drawf.construct(this, upgrade(payload.unit.type()), rotdeg() - 90f, progress / constructTime, speedScl, time);
|
Drawf.construct(this, upgrade(payload.unit.type), rotdeg() - 90f, progress / constructTime, speedScl, time);
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
Draw.z(Layer.blockOver);
|
Draw.z(Layer.blockOver);
|
||||||
@@ -135,7 +135,7 @@ public class Reconstructor extends UnitBlock{
|
|||||||
|
|
||||||
if(payload != null){
|
if(payload != null){
|
||||||
//check if offloading
|
//check if offloading
|
||||||
if(!hasUpgrade(payload.unit.type())){
|
if(!hasUpgrade(payload.unit.type)){
|
||||||
moveOutPayload();
|
moveOutPayload();
|
||||||
}else{ //update progress
|
}else{ //update progress
|
||||||
if(moveInPayload()){
|
if(moveInPayload()){
|
||||||
@@ -146,7 +146,7 @@ public class Reconstructor extends UnitBlock{
|
|||||||
|
|
||||||
//upgrade the unit
|
//upgrade the unit
|
||||||
if(progress >= constructTime){
|
if(progress >= constructTime){
|
||||||
payload.unit = upgrade(payload.unit.type()).create(payload.unit.team());
|
payload.unit = upgrade(payload.unit.type).create(payload.unit.team());
|
||||||
progress = 0;
|
progress = 0;
|
||||||
Effect.shake(2f, 3f, this);
|
Effect.shake(2f, 3f, this);
|
||||||
Fx.producesmoke.at(this);
|
Fx.producesmoke.at(this);
|
||||||
@@ -168,12 +168,12 @@ public class Reconstructor extends UnitBlock{
|
|||||||
public UnitType unit(){
|
public UnitType unit(){
|
||||||
if(payload == null) return null;
|
if(payload == null) return null;
|
||||||
|
|
||||||
UnitType t = upgrade(payload.unit.type());
|
UnitType t = upgrade(payload.unit.type);
|
||||||
return t != null && t.unlockedNow() ? t : null;
|
return t != null && t.unlockedNow() ? t : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean constructing(){
|
public boolean constructing(){
|
||||||
return payload != null && hasUpgrade(payload.unit.type());
|
return payload != null && hasUpgrade(payload.unit.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasUpgrade(UnitType type){
|
public boolean hasUpgrade(UnitType type){
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ public class ResupplyPoint extends Block{
|
|||||||
public static boolean resupply(Team team, float x, float y, float range, float ammoAmount, Color ammoColor, Boolf<Unit> valid){
|
public static boolean resupply(Team team, float x, float y, float range, float ammoAmount, Color ammoColor, Boolf<Unit> valid){
|
||||||
if(!state.rules.unitAmmo) return false;
|
if(!state.rules.unitAmmo) return false;
|
||||||
|
|
||||||
Unit unit = Units.closest(team, x, y, range, u -> u.type().ammoType instanceof ItemAmmoType && u.ammo <= u.type().ammoCapacity - ammoAmount && valid.get(u));
|
Unit unit = Units.closest(team, x, y, range, u -> u.type.ammoType instanceof ItemAmmoType && u.ammo <= u.type.ammoCapacity - ammoAmount && valid.get(u));
|
||||||
if(unit != null){
|
if(unit != null){
|
||||||
Fx.itemTransfer.at(x, y, ammoAmount / 2f, ammoColor, unit);
|
Fx.itemTransfer.at(x, y, ammoAmount / 2f, ammoColor, unit);
|
||||||
unit.ammo = Math.min(unit.ammo + ammoAmount, unit.type().ammoCapacity);
|
unit.ammo = Math.min(unit.ammo + ammoAmount, unit.type.ammoCapacity);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import mindustry.entities.*;
|
|||||||
import mindustry.entities.units.*;
|
import mindustry.entities.units.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.graphics.*;
|
import mindustry.graphics.*;
|
||||||
|
import mindustry.logic.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.ui.*;
|
import mindustry.ui.*;
|
||||||
import mindustry.world.blocks.*;
|
import mindustry.world.blocks.*;
|
||||||
@@ -122,6 +123,12 @@ public class UnitFactory extends UnitBlock{
|
|||||||
return currentPlan == -1 ? 0 : progress / plans.get(currentPlan).time;
|
return currentPlan == -1 ? 0 : progress / plans.get(currentPlan).time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object senseObject(LAccess sensor){
|
||||||
|
if(sensor == LAccess.config) return currentPlan == -1 ? null : plans.get(currentPlan).unit;
|
||||||
|
return super.senseObject(sensor);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void buildConfiguration(Table table){
|
public void buildConfiguration(Table table){
|
||||||
Seq<UnitType> units = Seq.with(plans).map(u -> u.unit).filter(u -> u.unlockedNow());
|
Seq<UnitType> units = Seq.with(plans).map(u -> u.unit).filter(u -> u.unlockedNow());
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class SStats implements SteamUserStatsCallback{
|
|||||||
// active10Phantoms.complete();
|
// active10Phantoms.complete();
|
||||||
//}
|
//}
|
||||||
|
|
||||||
if(Groups.unit.count(u -> u.type() == UnitTypes.crawler && u.team() == player.team()) >= 50){
|
if(Groups.unit.count(u -> u.type == UnitTypes.crawler && u.team() == player.team()) >= 50){
|
||||||
active50Crawlers.complete();
|
active50Crawlers.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||