Ammo system redesign

This commit is contained in:
Anuken 2021-07-25 17:15:39 -04:00
parent 9cd19c0470
commit 7a1f332731
16 changed files with 107 additions and 171 deletions

View File

@ -2003,17 +2003,6 @@ public class Blocks implements ContentList{
acceptCoolant = true;
}};
resupplyPoint = new ResupplyPoint("resupply-point"){{
requirements(Category.units, BuildVisibility.ammoOnly, with(Items.lead, 20, Items.copper, 15, Items.silicon, 15));
size = 2;
range = 80f;
itemCapacity = 20;
ammoAmount = 5;
consumes.item(Items.copper, 1);
}};
//endregion
//region payloads

View File

@ -52,8 +52,8 @@ public class UnitTypes implements ContentList{
//air + payload, legacy
public static @EntityDef(value = {Unitc.class, Payloadc.class}, legacy = true) UnitType quad;
//air + payload + ammo distribution
public static @EntityDef({Unitc.class, Payloadc.class, AmmoDistributec.class}) UnitType oct;
//air + payload + legacy (different branch)
public static @EntityDef(value = {Unitc.class, Payloadc.class}, legacy = true) UnitType oct;
//air, legacy
public static @EntityDef(value = {Unitc.class}, legacy = true) UnitType alpha, beta, gamma;
@ -89,6 +89,7 @@ public class UnitTypes implements ContentList{
hitSize = 10f;
health = 540;
armor = 4f;
ammoType = AmmoTypes.coal;
immunities.add(StatusEffects.burning);
@ -123,6 +124,7 @@ public class UnitTypes implements ContentList{
health = 900;
armor = 9f;
mechFrontSway = 0.55f;
ammoType = AmmoTypes.graphite;
weapons.add(new Weapon("artillery"){{
top = false;
@ -156,6 +158,7 @@ public class UnitTypes implements ContentList{
armor = 10f;
canDrown = false;
mechFrontSway = 1f;
ammoType = AmmoTypes.thorium;
mechStepParticles = true;
mechStepShake = 0.15f;
@ -220,6 +223,7 @@ public class UnitTypes implements ContentList{
canDrown = false;
mechFrontSway = 1.9f;
mechSideSway = 0.6f;
ammoType = AmmoTypes.thorium;
weapons.add(
new Weapon("reign-weapon"){{
@ -558,6 +562,7 @@ public class UnitTypes implements ContentList{
health = 200;
mechSideSway = 0.25f;
range = 40f;
ammoType = AmmoTypes.coal;
weapons.add(new Weapon(){{
reload = 24f;
@ -595,6 +600,7 @@ public class UnitTypes implements ContentList{
legMoveSpace = 1.4f;
hovering = true;
armor = 3f;
ammoType = AmmoTypes.coal;
allowLegStep = true;
visualElevation = 0.2f;
@ -965,6 +971,7 @@ public class UnitTypes implements ContentList{
targetFlags = new BlockFlag[]{BlockFlag.factory, null};
commandLimit = 5;
circleTarget = true;
ammoType = AmmoTypes.graphite;
weapons.add(new Weapon(){{
minShootVelocity = 0.75f;
@ -1003,6 +1010,7 @@ public class UnitTypes implements ContentList{
targetFlags = new BlockFlag[]{BlockFlag.launchPad, BlockFlag.storage, BlockFlag.battery, null};
engineOffset = 12f;
engineSize = 3f;
ammoType = AmmoTypes.graphite;
weapons.add(new Weapon("zenith-missiles"){{
reload = 40f;
@ -1048,6 +1056,7 @@ public class UnitTypes implements ContentList{
engineSize = 5.3f;
hitSize = 46f;
targetFlags = new BlockFlag[]{BlockFlag.generator, BlockFlag.core, null};
ammoType = AmmoTypes.thorium;
BulletType missiles = new MissileBulletType(2.7f, 14){{
width = 8f;
@ -1123,6 +1132,7 @@ public class UnitTypes implements ContentList{
destructibleWreck = false;
armor = 13f;
targetFlags = new BlockFlag[]{BlockFlag.reactor, BlockFlag.core, null};
ammoType = AmmoTypes.thorium;
BulletType fragBullet = new FlakBulletType(4f, 5){{
shootEffect = Fx.shootBig;
@ -1407,9 +1417,7 @@ public class UnitTypes implements ContentList{
commandLimit = 6;
lowAltitude = true;
buildBeamOffset = 43;
ammoCapacity = 1300;
ammoResupplyAmount = 20;
ammoCapacity = 1;
abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new RepairFieldAbility(130f, 60f * 2, 140f));
}};
@ -1477,6 +1485,7 @@ public class UnitTypes implements ContentList{
accel = 0.3f;
rotateSpeed = 2.6f;
rotateShooting = false;
ammoType = AmmoTypes.graphite;
trailLength = 20;
trailX = 5.5f;
@ -1520,6 +1529,7 @@ public class UnitTypes implements ContentList{
hitSize = 20f;
armor = 7f;
rotateShooting = false;
ammoType = AmmoTypes.graphite;
trailLength = 22;
trailX = 7f;
@ -1583,6 +1593,7 @@ public class UnitTypes implements ContentList{
inaccuracy = 5f;
velocityRnd = 0.1f;
shootSound = Sounds.missile;
ammoType = AmmoTypes.thorium;
ejectEffect = Fx.none;
bullet = new MissileBulletType(2.7f, 12){{
@ -1616,6 +1627,7 @@ public class UnitTypes implements ContentList{
accel = 0.2f;
rotateSpeed = 1.3f;
rotateShooting = false;
ammoType = AmmoTypes.thorium;
trailLength = 50;
trailX = 18f;
@ -1701,6 +1713,7 @@ public class UnitTypes implements ContentList{
accel = 0.19f;
rotateSpeed = 0.9f;
rotateShooting = false;
ammoType = AmmoTypes.powerHigh;
float spawnTime = 60f * 15f;
@ -1758,6 +1771,7 @@ public class UnitTypes implements ContentList{
trailScl = 1.3f;
rotateShooting = false;
range = 100f;
ammoType = AmmoTypes.power;
armor = 3f;
@ -1835,6 +1849,7 @@ public class UnitTypes implements ContentList{
trailX = 5.5f;
trailY = -4f;
trailScl = 1.9f;
ammoType = AmmoTypes.coal;
buildSpeed = 2f;
@ -1903,6 +1918,7 @@ public class UnitTypes implements ContentList{
hitSize = 20f;
armor = 6f;
rotateShooting = false;
ammoType = AmmoTypes.graphite;
trailLength = 23;
trailX = 9f;
@ -2038,6 +2054,7 @@ public class UnitTypes implements ContentList{
accel = 0.2f;
rotateSpeed = 1.4f;
rotateShooting = false;
ammoType = AmmoTypes.powerHigh;
//clip size is massive due to energy field
clipSize = 250f;
@ -2081,6 +2098,7 @@ public class UnitTypes implements ContentList{
accel = 0.2f;
rotateSpeed = 1.1f;
rotateShooting = false;
ammoType = AmmoTypes.powerHigh;
trailLength = 70;
trailX = 23f;

View File

@ -22,6 +22,7 @@ public class Units{
private static float cdist;
private static boolean boolResult;
private static int intResult;
private static Building buildResult;
@Remote(called = Loc.server)
public static void unitCapDeath(Unit unit){
@ -157,6 +158,26 @@ public class Units{
return indexer.findEnemyTile(team, x, y, range, pred);
}
/** @return the closest building of the provided team that matches the predicate. */
public static @Nullable Building closestBuilding(Team team, float wx, float wy, float range, Boolf<Building> pred){
buildResult = null;
cdist = 0f;
var buildings = team.data().buildings;
if(buildings == null) return null;
buildings.intersect(wx - range, wy - range, range*2f, range*2f, b -> {
if(pred.get(b)){
float dst = b.dst(wx, wy) - b.hitSize()/2f;
if(dst <= range && (buildResult == null || dst <= cdist)){
cdist = dst;
buildResult = b;
}
}
});
return buildResult;
}
/** Iterates through all buildings in a range. */
public static void nearbyBuildings(float x, float y, float range, Cons<Building> cons){
indexer.allBuildings(x, y, range, cons);

View File

@ -1,28 +0,0 @@
package mindustry.entities.comp;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.blocks.units.*;
@Component
abstract class AmmoDistributeComp implements Unitc{
@Import float x, y;
@Import UnitType type;
@Import Team team;
@Import float ammo;
private transient float ammoCooldown;
@Override
public void update(){
if(ammoCooldown > 0f) ammoCooldown -= Time.delta;
if(ammo > 0 && ammoCooldown <= 0f && ResupplyPoint.resupply(team, x, y, type.ammoResupplyRange, Math.min(type.ammoResupplyAmount, ammo), type.ammoType.color, u -> u != self())){
ammo -= Math.min(type.ammoResupplyAmount, ammo);
ammoCooldown = 5f;
}
}
}

View File

@ -325,8 +325,6 @@ public class ClassMap{
classes.put("ReconstructorBuild", mindustry.world.blocks.units.Reconstructor.ReconstructorBuild.class);
classes.put("RepairPoint", mindustry.world.blocks.units.RepairPoint.class);
classes.put("RepairPointBuild", mindustry.world.blocks.units.RepairPoint.RepairPointBuild.class);
classes.put("ResupplyPoint", mindustry.world.blocks.units.ResupplyPoint.class);
classes.put("ResupplyPointBuild", mindustry.world.blocks.units.ResupplyPoint.ResupplyPointBuild.class);
classes.put("UnitBlock", mindustry.world.blocks.units.UnitBlock.class);
classes.put("UnitBuild", mindustry.world.blocks.units.UnitBlock.UnitBuild.class);
classes.put("UnitFactory", mindustry.world.blocks.units.UnitFactory.class);

View File

@ -29,6 +29,7 @@ import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.mod.Mods.*;
import mindustry.type.*;
import mindustry.type.AmmoTypes.*;
import mindustry.type.weather.*;
import mindustry.world.*;
import mindustry.world.blocks.units.*;
@ -95,6 +96,16 @@ public class ContentParser{
readFields(result, data);
return result;
});
put(AmmoType.class, (type, data) -> {
if(data.isString()){
return field(AmmoTypes.class, data);
}
var bc = resolve(data.getString("type", ""), ItemAmmoType.class);
data.remove("type");
AmmoType result = make(bc);
readFields(result, data);
return result;
});
put(DrawBlock.class, (type, data) -> {
if(data.isString()){
//try to instantiate
@ -346,6 +357,16 @@ public class ContentParser{
if(!value.has("sector") || !value.get("sector").isNumber()) throw new RuntimeException("SectorPresets must have a sector number.");
return new SectorPreset(name, locate(ContentType.planet, value.getString("planet", "serpulo")), value.getInt("sector"));
},
ContentType.ammo, (TypeParser<AmmoType>)(mod, name, value) -> {
if(value.isString()){
return (AmmoType)field(AmmoTypes.class, value.asString());
}
AmmoType item = make(resolve(value.getString("type", null), ItemAmmoType.class));
currentContent = item;
read(() -> readFields(item, value));
return item;
}
);
@ -746,7 +767,7 @@ public class ContentParser{
/** Tries to resolve a class from the class type map. */
<T> Class<T> resolve(String base, Class<T> def){
//no base class specified
if(base.isEmpty() && def != null) return def;
if((base == null || base.isEmpty()) && def != null) return def;
//return mapped class if found in the global map
var out = ClassMap.classes.get(!base.isEmpty() && Character.isLowerCase(base.charAt(0)) ? Strings.capitalize(base) : base);

View File

@ -96,7 +96,7 @@ public class Net{
String type = t.getClass().toString().toLowerCase();
boolean isError = false;
if(e instanceof BufferUnderflowException || e instanceof BufferOverflowException){
if(e instanceof BufferUnderflowException || e instanceof BufferOverflowException || e.getCause() instanceof EOFException){
error = Core.bundle.get("error.io");
}else if(error.equals("mismatch") || e instanceof LZ4Exception || (e instanceof IndexOutOfBoundsException && e.getStackTrace()[0].getClassName().contains("java.nio"))){
error = Core.bundle.get("error.mismatch");

View File

@ -10,6 +10,7 @@ public class AmmoType extends Content{
public String icon = Iconc.itemCopper + "";
public Color color = Pal.ammo;
public Color barColor = Pal.ammo;
public float range = 85f;
public AmmoType(char icon, Color color){
this.icon = icon + "";

View File

@ -1,12 +1,10 @@
package mindustry.type;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.world.*;
import mindustry.world.meta.*;
public class AmmoTypes implements ContentList{
public static AmmoType
@ -14,6 +12,8 @@ public class AmmoTypes implements ContentList{
power,
powerHigh,
copper,
graphite,
coal,
thorium;
@Override
@ -22,6 +22,8 @@ public class AmmoTypes implements ContentList{
power = new PowerAmmoType(1000);
powerHigh = new PowerAmmoType(2000);
copper = new ItemAmmoType(Items.copper);
graphite = new ItemAmmoType(Items.graphite);
coal = new ItemAmmoType(Items.coal);
thorium = new ItemAmmoType(Items.thorium);
}
@ -40,31 +42,29 @@ public class AmmoTypes implements ContentList{
@Override
public void resupply(Unit unit){
float range = unit.hitSize + 60f;
Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.battery);
float range = unit.hitSize + this.range;
if(closest != null && closest.build != null && unit.within(closest.build, range) && closest.build.power != null){
var build = closest.build;
Building build = Units.closestBuilding(unit.team, unit.x, unit.y, range, u -> u.block.hasPower && u.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 powerPerAmmo = totalPower / unit.type.ammoCapacity;
float ammoRequired = unit.type.ammoCapacity - unit.ammo;
float powerRequired = ammoRequired * powerPerAmmo;
float powerTaken = Math.min(amount, powerRequired);
if(build != null){
float amount = build.power.status * build.block.consumes.getPower().capacity;
float powerPerAmmo = totalPower / unit.type.ammoCapacity;
float ammoRequired = unit.type.ammoCapacity - unit.ammo;
float powerRequired = ammoRequired * powerPerAmmo;
float powerTaken = Math.min(amount, powerRequired);
if(powerTaken > 1){
closest.build.power.status -= powerTaken / build.block.consumes.getPower().capacity;
unit.ammo += powerTaken / powerPerAmmo;
if(powerTaken > 1){
build.power.status -= powerTaken / build.block.consumes.getPower().capacity;
unit.ammo += powerTaken / powerPerAmmo;
Fx.itemTransfer.at(build.x, build.y, Math.max(powerTaken / 100f, 1f), Pal.power, unit);
}
Fx.itemTransfer.at(build.x, build.y, Math.max(powerTaken / 100f, 1f), Pal.power, unit);
}
}
}
}
public static class ItemAmmoType extends AmmoType{
public int ammoPerItem = 15;
public Item item;
public ItemAmmoType(Item item){
@ -75,6 +75,22 @@ public class AmmoTypes implements ContentList{
public ItemAmmoType(){
}
@Override
public void resupply(Unit unit){
//do not resupply when it would waste resources
if(unit.type.ammoCapacity - unit.ammo < ammoPerItem) return;
float range = unit.hitSize + this.range;
Building build = Units.closestBuilding(unit.team, unit.x, unit.y, range, u -> u.block.allowResupply && u.items.has(item));
if(build != null){
Fx.itemTransfer.at(build.x, build.y, ammoPerItem / 2f, item.color, unit);
unit.ammo = Math.min(unit.ammo + ammoPerItem, unit.type.ammoCapacity);
build.items.remove(item, 1);
}
}
@Override
public void load(){
if(item != null){

View File

@ -89,9 +89,6 @@ public class UnitType extends UnlockableContent{
public float legSplashDamage = 0f, legSplashRange = 5;
public boolean flipBackLegs = true;
public int ammoResupplyAmount = 10;
public float ammoResupplyRange = 100f;
public float mechSideSway = 0.54f, mechFrontSway = 0.1f;
public float mechStride = -1f;
public float mechStepShake = -1f;

View File

@ -70,8 +70,10 @@ public class Block extends UnlockableContent{
public boolean update;
/** whether this block has health and can be destroyed */
public boolean destructible;
/** whether unloaders work on this block*/
/** whether unloaders work on this block */
public boolean unloadable = true;
/** whether units can resupply by taking items from this block */
public boolean allowResupply = false;
/** whether this is solid */
public boolean solid;
/** whether this block CAN be solid. */

View File

@ -22,7 +22,6 @@ import mindustry.logic.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
@ -42,7 +41,6 @@ public class LaunchPad extends Block{
solid = true;
update = true;
configurable = true;
drawDisabled = false;
flags = EnumSet.of(BlockFlag.launchPad);
}
@ -73,12 +71,6 @@ public class LaunchPad extends Block{
return !state.isCampaign() || net.client() ? SystemCursor.arrow : super.getCursor();
}
//cannot be disabled
@Override
public float efficiency(){
return power != null && (block.consumes.has(ConsumeType.power) && !block.consumes.getPower().buffered) ? power.status : 1f;
}
@Override
public boolean shouldConsume(){
return true;

View File

@ -20,7 +20,6 @@ import mindustry.logic.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.blocks.units.*;
import mindustry.world.meta.*;
import mindustry.world.modules.*;
@ -32,13 +31,7 @@ public class CoreBlock extends StorageBlock{
public UnitType unitType = UnitTypes.alpha;
public final int timerResupply = timers++;
public int ammoAmount = 5;
public float resupplyRate = 10f;
public float resupplyRange = 60f;
public float captureInvicibility = 60f * 15f;
public Item resupplyItem = Items.copper;
public CoreBlock(String name){
super(name);
@ -240,13 +233,7 @@ public class CoreBlock extends StorageBlock{
@Override
public void updateTile(){
iframes -= Time.delta;
//resupply nearby units
if(items.has(resupplyItem) && timer(timerResupply, resupplyRate) && ResupplyPoint.resupply(this, resupplyRange, ammoAmount, resupplyItem.color)){
items.remove(resupplyItem, 1);
}
}
@Override

View File

@ -22,6 +22,7 @@ public class StorageBlock extends Block{
destructible = true;
group = BlockGroup.transportation;
flags = EnumSet.of(BlockFlag.storage);
allowResupply = true;
}
@Override

View File

@ -1,77 +0,0 @@
package mindustry.world.blocks.units;
import arc.func.*;
import arc.graphics.*;
import arc.struct.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.AmmoTypes.*;
import mindustry.world.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class ResupplyPoint extends Block{
public final int timerResupply = timers++;
public int ammoAmount = 10;
public float resupplyRate = 5f;
public float range = 60f;
public Color ammoColor = Items.copper.color;
public ResupplyPoint(String name){
super(name);
solid = update = true;
hasItems = true;
flags = EnumSet.of(BlockFlag.resupply);
}
@Override
public boolean outputsItems(){
return false;
}
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.placing);
}
public class ResupplyPointBuild extends Building{
@Override
public void drawSelect(){
Drawf.dashCircle(x, y, range, team.color);
}
@Override
public void updateTile(){
if(consValid() && timer(timerResupply, resupplyRate / timeScale) && resupply(this, range, ammoAmount, ammoColor)){
consume();
}
}
}
/** Tries to resupply nearby units.
* @return whether resupplying was successful. If unit ammo is disabled, always returns false. */
public static boolean resupply(Building tile, float range, float ammoAmount, Color ammoColor){
return resupply(tile.team, tile.x, tile.y, range, ammoAmount, ammoColor, u -> true);
}
/** Tries to resupply nearby units.
* @return whether resupplying was successful. If unit ammo is disabled, always returns false. */
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;
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){
Fx.itemTransfer.at(x, y, ammoAmount / 2f, ammoColor, unit);
unit.ammo = Math.min(unit.ammo + ammoAmount, unit.type.ammoCapacity);
return true;
}
return false;
}
}

View File

@ -18,8 +18,6 @@ public enum BlockFlag{
rally,
/** Block that stored power for resupply. */
battery,
/** Block used for resupply. */
resupply,
/** Any reactor block. */
reactor,
/** This flag is unused, and will be removed. */
@ -33,5 +31,5 @@ public enum BlockFlag{
public final static BlockFlag[] all = values();
/** Values for logic only. Filters out some internal flags. */
public final static BlockFlag[] allLogic = {core, storage, generator, turret, factory, repair, rally, battery, resupply, reactor};
public final static BlockFlag[] allLogic = {core, storage, generator, turret, factory, repair, rally, battery, reactor};
}