Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features

 Conflicts:
	core/src/mindustry/type/UnitType.java
This commit is contained in:
Anuken
2021-10-01 09:41:33 -04:00
20 changed files with 236 additions and 58 deletions

View File

@@ -122,7 +122,7 @@ committingchanges = Committing Changes
done = Done done = Done
feature.unsupported = Your device does not support this feature. feature.unsupported = Your device does not support this feature.
mods.initfailed = [red]⚠[] The previous Mindustry instance failed to initialize. This was likely caused by misbehaving mods.\n\nTo prevent a crash loop, [red]all mods have been disabled.[]\n\nTo disable this feature, turn it off in [accent]Settings->Game->Disable Mods On Startup Crash[]. mods.initfailed = [red]⚠[] The previous Mindustry instance failed to initialize. This was likely caused by misbehaving mods.\n\nTo prevent a crash loop, [red]all mods have been disabled.[]
mods = Mods mods = Mods
mods.none = [lightgray]No mods found! mods.none = [lightgray]No mods found!
mods.guide = Modding Guide mods.guide = Modding Guide

View File

@@ -445,7 +445,7 @@ editor.overwrite = [accent]Внимание!\nЭто перезапишет уж
editor.overwrite.confirm = [scarlet]Осторожно![] Карта с таким названием уже существует. Вы действительно хотите её перезаписать?\n«[accent]{0}[]» editor.overwrite.confirm = [scarlet]Осторожно![] Карта с таким названием уже существует. Вы действительно хотите её перезаписать?\n«[accent]{0}[]»
editor.exists = Карта с таким именем уже существует. editor.exists = Карта с таким именем уже существует.
editor.selectmap = Выберите карту для загрузки: editor.selectmap = Выберите карту для загрузки:
editor.cliffs = Стены на скалы editor.cliffs = Создать скалы из стен
toolmode.replace = Заменить toolmode.replace = Заменить
toolmode.replace.description = Рисует только\nна сплошных блоках. toolmode.replace.description = Рисует только\nна сплошных блоках.
@@ -540,6 +540,7 @@ configure = Конфигурация выгрузки
loadout = Груз loadout = Груз
resources = Ресурсы resources = Ресурсы
bannedblocks = Запрещённые блоки bannedblocks = Запрещённые блоки
bannedunits = Запрещённые единицы
addall = Добавить всё addall = Добавить всё
launch.from = Запуск из: [accent]{0} launch.from = Запуск из: [accent]{0}
launch.destination = Место назначения: {0} launch.destination = Место назначения: {0}
@@ -564,10 +565,13 @@ weather.sandstorm.name = Пыльная буря
weather.sporestorm.name = Споровая буря weather.sporestorm.name = Споровая буря
weather.fog.name = Туман weather.fog.name = Туман
sectorlist = Секторы
sectorlist.attacked = {0} под атакой
sectors.unexplored = [lightgray]Не исследовано sectors.unexplored = [lightgray]Не исследовано
sectors.resources = Ресурсы: sectors.resources = Ресурсы:
sectors.production = Производит: sectors.production = Производит:
sectors.export = Экспорт: sectors.export = Экспорт:
sectors.import = Импорт:
sectors.time = Время: sectors.time = Время:
sectors.threat = Угроза: sectors.threat = Угроза:
sectors.wave = Волна: sectors.wave = Волна:
@@ -995,6 +999,7 @@ mode.attack.name = Атака
mode.attack.description = Уничтожьте вражескую базу.\n[gray]Для игры требуется красное ядро на карте. mode.attack.description = Уничтожьте вражескую базу.\n[gray]Для игры требуется красное ядро на карте.
mode.custom = Пользовательские правила mode.custom = Пользовательские правила
rules.cleanupdeadteams = Очистка строений побежденных команд (PvP)
rules.infiniteresources = Бесконечные ресурсы (Игрок) rules.infiniteresources = Бесконечные ресурсы (Игрок)
rules.reactorexplosions = Взрывы реакторов rules.reactorexplosions = Взрывы реакторов
rules.coreincinerates = Ядро сжигает избыток ресурсов rules.coreincinerates = Ядро сжигает избыток ресурсов
@@ -1009,6 +1014,7 @@ rules.enemyCheat = Бесконечные ресурсы ИИ (красная к
rules.blockhealthmultiplier = Множитель прочности блоков rules.blockhealthmultiplier = Множитель прочности блоков
rules.blockdamagemultiplier = Множитель урона блоков rules.blockdamagemultiplier = Множитель урона блоков
rules.unitbuildspeedmultiplier = Множитель скорости производства боев. ед. rules.unitbuildspeedmultiplier = Множитель скорости производства боев. ед.
rules.aitier = Уровень ИИ
rules.unithealthmultiplier = Множитель прочности боев. ед. rules.unithealthmultiplier = Множитель прочности боев. ед.
rules.unitdamagemultiplier = Множитель урона боев. ед. rules.unitdamagemultiplier = Множитель урона боев. ед.
rules.unitcapvariable = Ядра увеличивают лимит единиц rules.unitcapvariable = Ядра увеличивают лимит единиц
@@ -1030,6 +1036,8 @@ rules.title.environment = Окружение
rules.lighting = Освещение rules.lighting = Освещение
rules.enemyLights = Вражеские огни rules.enemyLights = Вражеские огни
rules.fire = Огонь rules.fire = Огонь
rules.enemyteam = Команда Врагов
rules.playerteam = Команда Игрока
rules.explosions = Урон от взрывов блоков/единиц rules.explosions = Урон от взрывов блоков/единиц
rules.ambientlight = Окружающий свет rules.ambientlight = Окружающий свет
rules.weather = Погода rules.weather = Погода

View File

@@ -1885,6 +1885,7 @@ public class Blocks implements ContentList{
recoilAmount = 3f; recoilAmount = 3f;
shootShake = 1f; shootShake = 1f;
burstSpacing = 3f; burstSpacing = 3f;
spread = 0f;
shots = 4; shots = 4;
ammoUseEffect = Fx.casing2; ammoUseEffect = Fx.casing2;
health = 240 * size * size; health = 240 * size * size;

View File

@@ -120,7 +120,7 @@ public interface Platform{
}else{ }else{
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
Fi result = Core.files.local(name+ "." + extension); Fi result = Core.files.local(name + "." + extension);
writer.write(result); writer.write(result);
platform.shareFile(result); platform.shareFile(result);
}catch(Throwable e){ }catch(Throwable e){

View File

@@ -30,6 +30,8 @@ public class Effect{
public float lifetime = 50f; public float lifetime = 50f;
/** Clip size. */ /** Clip size. */
public float clip; public float clip;
/** Time delay before the effect starts */
public float startDelay;
/** Amount added to rotation */ /** Amount added to rotation */
public float baseRotation; public float baseRotation;
/** If true, parent unit is data are followed. */ /** If true, parent unit is data are followed. */
@@ -58,6 +60,11 @@ public class Effect{
all.add(this); all.add(this);
} }
public Effect startDelay(float d){
startDelay = d;
return this;
}
public void init(){} public void init(){}
public Effect followParent(boolean follow){ public Effect followParent(boolean follow){
@@ -168,21 +175,29 @@ public class Effect{
effect.init(); effect.init();
} }
EffectState entity = EffectState.create(); if(effect.startDelay <= 0f){
entity.effect = effect; inst(effect, x, y, rotation, color, data);
entity.rotation = effect.baseRotation + rotation; }else{
entity.data = data; Time.runTask(effect.startDelay, () -> inst(effect, x, y, rotation, color, data));
entity.lifetime = effect.lifetime;
entity.set(x, y);
entity.color.set(color);
if(effect.followParent && data instanceof Posc p){
entity.parent = p;
entity.rotWithParent = effect.rotWithParent;
} }
entity.add();
} }
} }
private static void inst(Effect effect, float x, float y, float rotation, Color color, Object data){
EffectState entity = EffectState.create();
entity.effect = effect;
entity.rotation = effect.baseRotation + rotation;
entity.data = data;
entity.lifetime = effect.lifetime;
entity.set(x, y);
entity.color.set(color);
if(effect.followParent && data instanceof Posc p){
entity.parent = p;
entity.rotWithParent = effect.rotWithParent;
}
entity.add();
}
public static void decal(TextureRegion region, float x, float y, float rotation){ public static void decal(TextureRegion region, float x, float y, float rotation){
decal(region, x, y, rotation, 3600f, Pal.rubble); decal(region, x, y, rotation, 3600f, Pal.rubble);
} }

View File

@@ -49,6 +49,8 @@ public class BulletType extends Content implements Cloneable{
public Effect despawnEffect = Fx.hitBulletSmall; public Effect despawnEffect = Fx.hitBulletSmall;
/** Effect created when shooting. */ /** Effect created when shooting. */
public Effect shootEffect = Fx.shootSmall; public Effect shootEffect = Fx.shootSmall;
/** Effect created when charging completes; only usable in single-shot weapons with a firstShotDelay / shotDelay. */
public Effect chargeShootEffect = Fx.none;
/** Extra smoke effect created when shooting. */ /** Extra smoke effect created when shooting. */
public Effect smokeEffect = Fx.shootSmallSmoke; public Effect smokeEffect = Fx.shootSmallSmoke;
/** Sound made when hitting something or getting removed.*/ /** Sound made when hitting something or getting removed.*/

View File

@@ -12,6 +12,8 @@ public class WeaponMount{
public float reload; public float reload;
/** rotation relative to the unit this mount is on */ /** rotation relative to the unit this mount is on */
public float rotation; public float rotation;
/** weapon recoil */
public float recoil;
/** destination rotation; do not modify! */ /** destination rotation; do not modify! */
public float targetRotation; public float targetRotation;
/** current heat, 0 to 1*/ /** current heat, 0 to 1*/

View File

@@ -265,10 +265,16 @@ public class Waves{
} }
public static Seq<SpawnGroup> generate(float difficulty, Rand rand, boolean attack, boolean airOnly){ public static Seq<SpawnGroup> generate(float difficulty, Rand rand, boolean attack, boolean airOnly){
return generate(difficulty, rand, attack, airOnly, false);
}
public static Seq<SpawnGroup> generate(float difficulty, Rand rand, boolean attack, boolean airOnly, boolean naval){
UnitType[][] species = { UnitType[][] species = {
{dagger, mace, fortress, scepter, reign}, {dagger, mace, fortress, scepter, reign},
{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},
{risso, oxynoe, cyerce, aegires, navanax}, //retusa intentionally left out as it cannot damage the core properly
{flare, horizon, zenith, rand.chance(0.5) ? quad : antumbra, rand.chance(0.1) ? quad : eclipse} {flare, horizon, zenith, rand.chance(0.5) ? quad : antumbra, rand.chance(0.1) ? quad : eclipse}
}; };
@@ -276,6 +282,12 @@ public class Waves{
species = Structs.filter(UnitType[].class, species, v -> v[0].flying); species = Structs.filter(UnitType[].class, species, v -> v[0].flying);
} }
if(naval){
species = Structs.filter(UnitType[].class, species, v -> v[0].flying || v[0].naval);
}else{
species = Structs.filter(UnitType[].class, species, v -> v[0].flying && !v[0].naval);
}
UnitType[][] fspec = species; UnitType[][] fspec = species;
//required progression: //required progression:

View File

@@ -217,6 +217,7 @@ public class LCanvas extends Table{
e.setSize(width, e.getPrefHeight()); e.setSize(width, e.getPrefHeight());
e.setPosition(0, height - cy, Align.topLeft); e.setPosition(0, height - cy, Align.topLeft);
((StatementElem)e).updateAddress(i);
cy += e.getPrefHeight() + space; cy += e.getPrefHeight() + space;
seq.add(e); seq.add(e);
@@ -316,6 +317,8 @@ public class LCanvas extends Table{
public class StatementElem extends Table{ public class StatementElem extends Table{
public LStatement st; public LStatement st;
public int index;
Label addressLabel;
public StatementElem(LStatement st){ public StatementElem(LStatement st){
this.st = st; this.st = st;
@@ -333,9 +336,11 @@ public class LCanvas extends Table{
t.margin(6f); t.margin(6f);
t.touchable = Touchable.enabled; t.touchable = Touchable.enabled;
t.add(st.name()).style(Styles.outlineLabel).color(color).padRight(8); t.add(st.name()).style(Styles.outlineLabel).name("statement-name").color(color).padRight(8);
t.add().growX(); t.add().growX();
addressLabel = t.add(index + "").style(Styles.outlineLabel).color(color).padRight(8).get();
t.button(Icon.copy, Styles.logici, () -> { t.button(Icon.copy, Styles.logici, () -> {
}).size(24f).padRight(6).get().tapped(this::copy); }).size(24f).padRight(6).get().tapped(this::copy);
@@ -395,6 +400,11 @@ public class LCanvas extends Table{
marginBottom(7); marginBottom(7);
} }
public void updateAddress(int index){
this.index = index;
addressLabel.setText(index + "");
}
public void copy(){ public void copy(){
st.saveUI(); st.saveUI();
LStatement copy = st.copy(); LStatement copy = st.copy();

View File

@@ -1,5 +1,6 @@
package mindustry.logic; package mindustry.logic;
import arc.*;
import arc.func.*; import arc.func.*;
import arc.graphics.*; import arc.graphics.*;
import arc.scene.style.*; import arc.scene.style.*;
@@ -758,6 +759,20 @@ public class LStatements{
table.add().growX(); table.add().growX();
table.add(new JumpButton(() -> dest, s -> dest = s)).size(30).right().padLeft(-8); table.add(new JumpButton(() -> dest, s -> dest = s)).size(30).right().padLeft(-8);
String name = name();
//hack way of finding the title label...
Core.app.post(() -> {
//must be delayed because parent is added later
if(table.parent != null){
Label title = table.parent.find("statement-name");
if(title != null){
title.update(() -> title.setText((dest != null ? name + " -> " + dest.index : name)));
}
}
});
} }
void rebuild(Table table){ void rebuild(Table table){

View File

@@ -76,7 +76,7 @@ public abstract class PlanetGenerator extends BasicGenerator implements HexMeshe
//sort counts in descending order //sort counts in descending order
Seq<Entry<Block>> entries = floorc.entries().toArray(); Seq<Entry<Block>> entries = floorc.entries().toArray();
entries.sort(e -> -e.value); entries.sort(e -> -e.value);
//remove all blocks occuring < 30 times - unimportant //remove all blocks occurring < 30 times - unimportant
entries.removeAll(e -> e.value < 30); entries.removeAll(e -> e.value < 30);
Block[] floors = new Block[entries.size]; Block[] floors = new Block[entries.size];
@@ -84,7 +84,7 @@ public abstract class PlanetGenerator extends BasicGenerator implements HexMeshe
floors[i] = entries.get(i).key; floors[i] = entries.get(i).key;
} }
//TODO bad code //bad contains() code, but will likely never be fixed
boolean hasSnow = floors.length > 0 && (floors[0].name.contains("ice") || floors[0].name.contains("snow")); boolean hasSnow = floors.length > 0 && (floors[0].name.contains("ice") || floors[0].name.contains("snow"));
boolean hasRain = floors.length > 0 && !hasSnow && content.contains(Liquids.water) && !floors[0].name.contains("sand"); boolean hasRain = floors.length > 0 && !hasSnow && content.contains(Liquids.water) && !floors[0].name.contains("sand");
boolean hasDesert = floors.length > 0 && !hasSnow && !hasRain && floors[0] == Blocks.sand; boolean hasDesert = floors.length > 0 && !hasSnow && !hasRain && floors[0] == Blocks.sand;

View File

@@ -14,6 +14,7 @@ import mindustry.graphics.g3d.PlanetGrid.*;
import mindustry.maps.generators.*; import mindustry.maps.generators.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.environment.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -165,14 +166,14 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
connected.add(this); connected.add(this);
} }
void con(int x1, int y1, int x2, int y2){ void join(int x1, int y1, int x2, int y2){
float nscl = rand.random(100f, 140f) * 6f; float nscl = rand.random(100f, 140f) * 6f;
int stroke = rand.random(3, 9); int stroke = rand.random(3, 9);
brush(pathfind(x1, y1, x2, y2, tile -> (tile.solid() ? 50f : 0f) + noise(tile.x, tile.y, 2, 0.4f, 1f / nscl) * 500, Astar.manhattan), stroke); brush(pathfind(x1, y1, x2, y2, tile -> (tile.solid() ? 50f : 0f) + noise(tile.x, tile.y, 2, 0.4f, 1f / nscl) * 500, Astar.manhattan), stroke);
} }
void connect(Room to){ void connect(Room to){
if(!connected.add(to)) return; if(!connected.add(to) || to == this) return;
Vec2 midpoint = Tmp.v1.set(to.x, to.y).add(x, y).scl(0.5f); Vec2 midpoint = Tmp.v1.set(to.x, to.y).add(x, y).scl(0.5f);
rand.nextFloat(); rand.nextFloat();
@@ -188,8 +189,52 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
int mx = (int)midpoint.x, my = (int)midpoint.y; int mx = (int)midpoint.x, my = (int)midpoint.y;
con(x, y, mx, my); join(x, y, mx, my);
con(mx, my, to.x, to.y); join(mx, my, to.x, to.y);
}
void joinLiquid(int x1, int y1, int x2, int y2){
float nscl = rand.random(100f, 140f) * 6f;
int rad = rand.random(5, 10);
int avoid = 2 + rad;
var path = pathfind(x1, y1, x2, y2, tile -> (tile.solid() || !tile.floor().isLiquid ? 70f : 0f) + noise(tile.x, tile.y, 2, 0.4f, 1f / nscl) * 500, Astar.manhattan);
path.each(t -> {
//don't place liquid paths near the core
if(Mathf.dst2(t.x, t.y, x2, y2) <= avoid * avoid){
return;
}
for(int x = -rad; x <= rad; x++){
for(int y = -rad; y <= rad; y++){
int wx = t.x + x, wy = t.y + y;
if(Structs.inBounds(wx, wy, width, height) && Mathf.within(x, y, rad)){
Tile other = tiles.getn(wx, wy);
other.setBlock(Blocks.air);
if(Mathf.within(x, y, rad - 1) && !other.floor().isLiquid){
Floor floor = other.floor();
//TODO does not respect tainted floors
other.setFloor((Floor)(floor == Blocks.sand || floor == Blocks.salt ? Blocks.sandWater : Blocks.darksandTaintedWater));
}
}
}
}
});
}
void connectLiquid(Room to){
if(to == this) return;
Vec2 midpoint = Tmp.v1.set(to.x, to.y).add(x, y).scl(0.5f);
rand.nextFloat();
//add randomized offset to avoid straight lines
midpoint.add(Tmp.v2.setToRandomDirection(rand).scl(Tmp.v1.dst(x, y)));
midpoint.sub(width/2f, height/2f).limit(width / 2f / Mathf.sqrt3).add(width/2f, height/2f);
int mx = (int)midpoint.x, my = (int)midpoint.y;
joinLiquid(x, y, mx, my);
joinLiquid(mx, my, to.x, to.y);
} }
} }
@@ -250,10 +295,12 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
} }
} }
//clear radius around each room
for(Room room : roomseq){ for(Room room : roomseq){
erase(room.x, room.y, room.radius); erase(room.x, room.y, room.radius);
} }
//randomly connect rooms together
int connections = rand.random(Math.max(rooms - 1, 1), rooms + 3); int connections = rand.random(Math.max(rooms - 1, 1), rooms + 3);
for(int i = 0; i < connections; i++){ for(int i = 0; i < connections; i++){
roomseq.random(rand).connect(roomseq.random(rand)); roomseq.random(rand).connect(roomseq.random(rand));
@@ -267,30 +314,27 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
cells(1); cells(1);
//shoreline setup int tlen = tiles.width * tiles.height;
int deepRadius = 4; int total = 0, waters = 0;
pass((x, y) -> { for(int i = 0; i < tlen; i++){
if(floor.asFloor().isLiquid && !floor.asFloor().isDeep()){ Tile tile = tiles.geti(i);
if(tile.block() == Blocks.air){
for(int cx = -deepRadius; cx <= deepRadius; cx++){ total ++;
for(int cy = -deepRadius; cy <= deepRadius; cy++){ if(tile.floor().liquidDrop == Liquids.water){
waters ++;
if((cx) * (cx) + (cy) * (cy) <= deepRadius * deepRadius){
int wx = cx + x, wy = cy + y;
Tile tile = tiles.get(wx, wy);
if(tile != null && (!tile.floor().isLiquid || tile.block() != Blocks.air)){
//found something solid, skip replacing anything
return;
}
}
}
} }
floor = floor == Blocks.darksandTaintedWater ? Blocks.taintedWater : Blocks.water;
} }
}); }
boolean naval = (float)waters / total >= 0.26f;
//create water pathway if the map is flooded
if(naval){
for(Room room : enemies){
room.connectLiquid(spawn);
}
}
distort(10f, 6f); distort(10f, 6f);
@@ -318,6 +362,56 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
} }
}); });
//shoreline setup
pass((x, y) -> {
int deepRadius = 3;
if(floor.asFloor().isLiquid && floor.asFloor().shallow){
for(int cx = -deepRadius; cx <= deepRadius; cx++){
for(int cy = -deepRadius; cy <= deepRadius; cy++){
if((cx) * (cx) + (cy) * (cy) <= deepRadius * deepRadius){
int wx = cx + x, wy = cy + y;
Tile tile = tiles.get(wx, wy);
if(tile != null && (!tile.floor().isLiquid || tile.block() != Blocks.air)){
//found something solid, skip replacing anything
return;
}
}
}
}
floor = floor == Blocks.darksandTaintedWater ? Blocks.taintedWater : Blocks.water;
}
});
if(naval){
int deepRadius = 2;
//TODO code is very similar, but annoying to extract into a separate function
pass((x, y) -> {
if(floor.asFloor().isLiquid && !floor.asFloor().isDeep() && !floor.asFloor().shallow){
for(int cx = -deepRadius; cx <= deepRadius; cx++){
for(int cy = -deepRadius; cy <= deepRadius; cy++){
if((cx) * (cx) + (cy) * (cy) <= deepRadius * deepRadius){
int wx = cx + x, wy = cy + y;
Tile tile = tiles.get(wx, wy);
if(tile != null && (tile.floor().shallow || !tile.floor().isLiquid)){
//found something shallow, skip replacing anything
return;
}
}
}
}
floor = floor == Blocks.water ? Blocks.deepwater : Blocks.deepTaintedWater;
}
});
}
Seq<Block> ores = Seq.with(Blocks.oreCopper, Blocks.oreLead); Seq<Block> ores = Seq.with(Blocks.oreCopper, Blocks.oreLead);
float poles = Math.abs(sector.tile.v.y); float poles = Math.abs(sector.tile.v.y);
float nmag = 0.5f; float nmag = 0.5f;
@@ -555,7 +649,7 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
state.rules.enemyCoreBuildRadius = 600f; state.rules.enemyCoreBuildRadius = 600f;
//spawn air only when spawn is blocked //spawn air only when spawn is blocked
state.rules.spawns = Waves.generate(difficulty, new Rand(sector.id), state.rules.attackMode, state.rules.attackMode && spawner.countGroundSpawns() == 0); state.rules.spawns = Waves.generate(difficulty, new Rand(sector.id), state.rules.attackMode, state.rules.attackMode && spawner.countGroundSpawns() == 0, naval);
} }
@Override @Override

View File

@@ -115,7 +115,7 @@ public class UnitType extends UnlockableContent{
/** This is a VERY ROUGH estimate of unit DPS. */ /** This is a VERY ROUGH estimate of unit DPS. */
public float dpsEstimate = -1; public float dpsEstimate = -1;
public float clipSize = -1; public float clipSize = -1;
public boolean canDrown = true; public boolean canDrown = true, naval = false;
public float drownTimeMultiplier = 1f; public float drownTimeMultiplier = 1f;
public float engineOffset = 5f, engineSize = 2.5f; public float engineOffset = 5f, engineSize = 2.5f;
public float strafePenalty = 0.5f; public float strafePenalty = 0.5f;
@@ -333,6 +333,7 @@ public class UnitType extends UnlockableContent{
//water preset //water preset
if(example instanceof WaterMovec){ if(example instanceof WaterMovec){
naval = true;
canDrown = false; canDrown = false;
omniMovement = false; omniMovement = false;
immunities.add(StatusEffects.wet); immunities.add(StatusEffects.wet);
@@ -398,6 +399,7 @@ public class UnitType extends UnlockableContent{
//add mirrored weapon variants //add mirrored weapon variants
Seq<Weapon> mapped = new Seq<>(); Seq<Weapon> mapped = new Seq<>();
for(Weapon w : weapons){ for(Weapon w : weapons){
if(w.recoilTime < 0) w.recoilTime = w.reload;
mapped.add(w); mapped.add(w);
//mirrors are copies with X values negated //mirrors are copies with X values negated
@@ -408,7 +410,9 @@ public class UnitType extends UnlockableContent{
copy.flipSprite = !copy.flipSprite; copy.flipSprite = !copy.flipSprite;
mapped.add(copy); mapped.add(copy);
//since there are now two weapons, the reload time must be doubled //since there are now two weapons, the reload and recoil time must be doubled
w.recoilTime *= 2f;
copy.recoilTime *= 2f;
w.reload *= 2f; w.reload *= 2f;
copy.reload *= 2f; copy.reload *= 2f;

View File

@@ -67,6 +67,8 @@ public class Weapon implements Cloneable{
public float shake = 0f; public float shake = 0f;
/** visual weapon knockback. */ /** visual weapon knockback. */
public float recoil = 1.5f; public float recoil = 1.5f;
/** the time it returns back to its original position in ticks. uses reload time by default */
public float recoilTime = -1f;
/** projectile/effect offsets from center of weapon */ /** projectile/effect offsets from center of weapon */
public float shootX = 0f, shootY = 3f; public float shootX = 0f, shootY = 3f;
/** offsets of weapon position on unit */ /** offsets of weapon position on unit */
@@ -144,9 +146,8 @@ public class Weapon implements Cloneable{
float float
rotation = unit.rotation - 90, rotation = unit.rotation - 90,
weaponRotation = rotation + (rotate ? mount.rotation : 0), weaponRotation = rotation + (rotate ? mount.rotation : 0),
recoil = -((mount.reload) / reload * this.recoil), wx = unit.x + Angles.trnsx(rotation, x, y) + Angles.trnsx(weaponRotation, 0, -mount.recoil),
wx = unit.x + Angles.trnsx(rotation, x, y) + Angles.trnsx(weaponRotation, 0, recoil), wy = unit.y + Angles.trnsy(rotation, x, y) + Angles.trnsy(weaponRotation, 0, -mount.recoil);
wy = unit.y + Angles.trnsy(rotation, x, y) + Angles.trnsy(weaponRotation, 0, recoil);
if(outlineRegion.found()){ if(outlineRegion.found()){
Draw.rect(outlineRegion, Draw.rect(outlineRegion,
@@ -161,9 +162,8 @@ public class Weapon implements Cloneable{
float float
rotation = unit.rotation - 90, rotation = unit.rotation - 90,
weaponRotation = rotation + (rotate ? mount.rotation : 0), weaponRotation = rotation + (rotate ? mount.rotation : 0),
recoil = -((mount.reload) / reload * this.recoil), wx = unit.x + Angles.trnsx(rotation, x, y) + Angles.trnsx(weaponRotation, 0, -mount.recoil),
wx = unit.x + Angles.trnsx(rotation, x, y) + Angles.trnsx(weaponRotation, 0, recoil), wy = unit.y + Angles.trnsy(rotation, x, y) + Angles.trnsy(weaponRotation, 0, -mount.recoil);
wy = unit.y + Angles.trnsy(rotation, x, y) + Angles.trnsy(weaponRotation, 0, recoil);
if(shadow > 0){ if(shadow > 0){
Drawf.shadow(wx, wy, shadow); Drawf.shadow(wx, wy, shadow);
@@ -198,7 +198,9 @@ public class Weapon implements Cloneable{
public void update(Unit unit, WeaponMount mount){ public void update(Unit unit, WeaponMount mount){
boolean can = unit.canShoot(); boolean can = unit.canShoot();
float lastReload = mount.reload;
mount.reload = Math.max(mount.reload - Time.delta * unit.reloadMultiplier, 0); mount.reload = Math.max(mount.reload - Time.delta * unit.reloadMultiplier, 0);
mount.recoil = Math.max(mount.recoil - (Time.delta * recoil * unit.reloadMultiplier) / recoilTime, 0);
//rotate if applicable //rotate if applicable
if(rotate && (mount.rotate || mount.shoot) && can){ if(rotate && (mount.rotate || mount.shoot) && can){
@@ -260,6 +262,7 @@ public class Weapon implements Cloneable{
mount.bullet.rotation(weaponRotation + 90); mount.bullet.rotation(weaponRotation + 90);
mount.bullet.set(bulletX, bulletY); mount.bullet.set(bulletX, bulletY);
mount.reload = reload; mount.reload = reload;
mount.recoil = recoil;
unit.vel.add(Tmp.v1.trns(unit.rotation + 180f, mount.bullet.type.recoil)); unit.vel.add(Tmp.v1.trns(unit.rotation + 180f, mount.bullet.type.recoil));
if(shootSound != Sounds.none && !headless){ if(shootSound != Sounds.none && !headless){
if(mount.sound == null) mount.sound = new SoundLoop(shootSound, 1f); if(mount.sound == null) mount.sound = new SoundLoop(shootSound, 1f);
@@ -275,9 +278,9 @@ public class Weapon implements Cloneable{
} }
} }
//flip weapon shoot side for alternating weapons at half reload //flip weapon shoot side for alternating weapons
if(otherSide != -1 && alternate && mount.side == flipSprite && boolean wasFlipped = mount.side;
mount.reload + Time.delta * unit.reloadMultiplier > reload/2f && mount.reload <= reload/2f){ if(otherSide != -1 && alternate && mount.side == flipSprite && mount.reload <= reload / 2f && lastReload > reload / 2f){
unit.mounts[otherSide].side = !unit.mounts[otherSide].side; unit.mounts[otherSide].side = !unit.mounts[otherSide].side;
mount.side = !mount.side; mount.side = !mount.side;
} }
@@ -286,7 +289,7 @@ public class Weapon implements Cloneable{
if(mount.shoot && //must be shooting if(mount.shoot && //must be shooting
can && //must be able to shoot can && //must be able to shoot
(!useAmmo || unit.ammo > 0 || !state.rules.unitAmmo || unit.team.rules().infiniteAmmo) && //check ammo (!useAmmo || unit.ammo > 0 || !state.rules.unitAmmo || unit.team.rules().infiniteAmmo) && //check ammo
(!alternate || mount.side == flipSprite) && (!alternate || wasFlipped == flipSprite) &&
unit.vel.len() >= minShootVelocity && //check velocity requirements unit.vel.len() >= minShootVelocity && //check velocity requirements
mount.reload <= 0.0001f && //reload has to be 0 mount.reload <= 0.0001f && //reload has to be 0
Angles.within(rotate ? mount.rotation : unit.rotation, mount.targetRotation, shootCone) //has to be within the cone Angles.within(rotate ? mount.rotation : unit.rotation, mount.targetRotation, shootCone) //has to be within the cone
@@ -343,14 +346,17 @@ public class Weapon implements Cloneable{
unit.vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil)); unit.vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil));
Effect.shake(shake, shake, shootX, shootY); Effect.shake(shake, shake, shootX, shootY);
mount.recoil = recoil;
mount.heat = 1f; mount.heat = 1f;
if(!continuous){ if(!continuous){
shootSound.at(shootX, shootY, Mathf.random(soundPitchMin, soundPitchMax)); shootSound.at(shootX, shootY, Mathf.random(soundPitchMin, soundPitchMax));
} }
ammo.chargeShootEffect.at(shootX + unit.x - baseX, shootY + unit.y - baseY, rotation, parentize ? unit : null);
}); });
}else{ }else{
unit.vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil)); unit.vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil));
Effect.shake(shake, shake, shootX, shootY); Effect.shake(shake, shake, shootX, shootY);
mount.recoil = recoil;
mount.heat = 1f; mount.heat = 1f;
} }

View File

@@ -778,6 +778,7 @@ public class HudFragment extends Fragment{
} }
private void addInfoTable(Table table){ private void addInfoTable(Table table){
table.name = "infotable";
table.left(); table.left();
var count = new float[]{-1}; var count = new float[]{-1};

View File

@@ -426,13 +426,14 @@ public class Turret extends ReloadTurret{
//when burst spacing is enabled, use the burst pattern //when burst spacing is enabled, use the burst pattern
}else if(burstSpacing > 0.0001f){ }else if(burstSpacing > 0.0001f){
for(int i = 0; i < shots; i++){ for(int i = 0; i < shots; i++){
int ii = i;
Time.run(burstSpacing * i, () -> { Time.run(burstSpacing * i, () -> {
if(!isValid() || !hasAmmo()) return; if(!isValid() || !hasAmmo()) return;
recoil = recoilAmount; recoil = recoilAmount;
tr.trns(rotation, shootLength, Mathf.range(xRand)); tr.trns(rotation, shootLength, Mathf.range(xRand));
bullet(type, rotation + Mathf.range(inaccuracy)); bullet(type, rotation + Mathf.range(inaccuracy + type.inaccuracy) + (ii - (int)(shots / 2f)) * spread);
effects(); effects();
useAmmo(); useAmmo();
recoil = recoilAmount; recoil = recoilAmount;

View File

@@ -47,6 +47,8 @@ public class Floor extends Block{
public float liquidMultiplier = 1f; public float liquidMultiplier = 1f;
/** whether this block is liquid. */ /** whether this block is liquid. */
public boolean isLiquid; public boolean isLiquid;
/** shallow water flag used for generation */
public boolean shallow = false;
/** if true, this block cannot be mined by players. useful for annoying things like sand. */ /** if true, this block cannot be mined by players. useful for annoying things like sand. */
public boolean playerUnmineable = false; public boolean playerUnmineable = false;
/** Group of blocks that this block does not draw edges on. */ /** Group of blocks that this block does not draw edges on. */

View File

@@ -24,5 +24,6 @@ public class ShallowLiquid extends Floor{
status = liquidBase.status; status = liquidBase.status;
liquidDrop = liquidBase.liquidDrop; liquidDrop = liquidBase.liquidDrop;
cacheLayer = liquidBase.cacheLayer; cacheLayer = liquidBase.cacheLayer;
shallow = true;
} }
} }

View File

@@ -1,5 +1,5 @@
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m \ org.gradle.jvmargs=-Xms256m -Xmx1024m --illegal-access=permit \
--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ --add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED \ --add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED \
@@ -24,4 +24,4 @@ android.useAndroidX=true
#used for slow jitpack builds; TODO see if this actually works #used for slow jitpack builds; TODO see if this actually works
org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000 org.gradle.internal.http.connectionTimeout=100000
archash=57b15a1b73cf0d6f19a50d2111f43d993e57c7f1 archash=585f498bf43d02c04ee129df65372380f31d4a03

View File

@@ -114,5 +114,9 @@
{ {
"name": "NukeDustry", "name": "NukeDustry",
"address": ["nukedustry.tk", "nukedustry.tk:6568"] "address": ["nukedustry.tk", "nukedustry.tk:6568"]
},
{
"name": "MindustryBR",
"address": ["mindustryptbr.ddns.net", "mindustryptbr.myddns.me", "mindustryptbr.myddns.me:6566"]
} }
] ]