Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features
Conflicts: core/src/mindustry/type/UnitType.java gradle.properties
This commit is contained in:
@@ -25,7 +25,7 @@ public class MinerAI extends AIController{
|
||||
|
||||
if(mining){
|
||||
if(timer.get(timerTarget2, 60 * 4) || targetItem == null){
|
||||
targetItem = unit.team.data().mineItems.min(i -> indexer.hasOre(i) && unit.canMine(i), i -> core.items.get(i));
|
||||
targetItem = unit.type.mineItems.min(i -> indexer.hasOre(i) && unit.canMine(i), i -> core.items.get(i));
|
||||
}
|
||||
|
||||
//core full of the target item, do nothing
|
||||
@@ -75,4 +75,4 @@ public class MinerAI extends AIController{
|
||||
circle(core, unit.type.range / 1.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -2138,7 +2139,7 @@ public class Blocks implements ContentList{
|
||||
size = 4;
|
||||
shootCone = 2f;
|
||||
shootSound = Sounds.railgun;
|
||||
unitSort = (u, x, y) -> -u.maxHealth + Mathf.dst2(u.x, u.y, x, y) / 6400f;
|
||||
unitSort = UnitSorts.strongest;
|
||||
|
||||
coolantMultiplier = 0.4f;
|
||||
|
||||
@@ -2638,4 +2639,4 @@ public class Blocks implements ContentList{
|
||||
|
||||
//endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,33 +260,10 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
//a command was sent, now get the output
|
||||
if(response.type != ResponseType.valid){
|
||||
String text;
|
||||
|
||||
//send usage
|
||||
if(response.type == ResponseType.manyArguments){
|
||||
text = "[scarlet]Too many arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else if(response.type == ResponseType.fewArguments){
|
||||
text = "[scarlet]Too few arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else{ //unknown command
|
||||
int minDst = 0;
|
||||
Command closest = null;
|
||||
|
||||
for(Command command : netServer.clientCommands.getCommandList()){
|
||||
int dst = Strings.levenshtein(command.text, response.runCommand);
|
||||
if(dst < 3 && (closest == null || dst < minDst)){
|
||||
minDst = dst;
|
||||
closest = command;
|
||||
}
|
||||
}
|
||||
|
||||
if(closest != null){
|
||||
text = "[scarlet]Unknown command. Did you mean \"[lightgray]" + closest.text + "[]\"?";
|
||||
}else{
|
||||
text = "[scarlet]Unknown command. Check [lightgray]/help[scarlet].";
|
||||
}
|
||||
String text = netServer.invalidHandler.handle(player, response);
|
||||
if(text != null){
|
||||
player.sendMessage(text);
|
||||
}
|
||||
|
||||
player.sendMessage(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,32 @@ public class NetServer implements ApplicationListener{
|
||||
/** Converts a message + NULLABLE player sender into a single string. Override for custom prefixes/suffixes. */
|
||||
public ChatFormatter chatFormatter = (player, message) -> player == null ? message : "[coral][[" + player.coloredName() + "[coral]]:[white] " + message;
|
||||
|
||||
/** Handles an incorrect command response. Returns text that will be sent to player. Override for customisation. */
|
||||
public InvalidCommandHandler invalidHandler = (player, response) -> {
|
||||
if(response.type == ResponseType.manyArguments){
|
||||
return "[scarlet]Too many arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else if(response.type == ResponseType.fewArguments){
|
||||
return "[scarlet]Too few arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else{ //unknown command
|
||||
int minDst = 0;
|
||||
Command closest = null;
|
||||
|
||||
for(Command command : netServer.clientCommands.getCommandList()){
|
||||
int dst = Strings.levenshtein(command.text, response.runCommand);
|
||||
if(dst < 3 && (closest == null || dst < minDst)){
|
||||
minDst = dst;
|
||||
closest = command;
|
||||
}
|
||||
}
|
||||
|
||||
if(closest != null){
|
||||
return "[scarlet]Unknown command. Did you mean \"[lightgray]" + closest.text + "[]\"?";
|
||||
}else{
|
||||
return "[scarlet]Unknown command. Check [lightgray]/help[scarlet].";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean closing = false;
|
||||
private Interval timer = new Interval();
|
||||
|
||||
@@ -994,4 +1020,8 @@ public class NetServer implements ApplicationListener{
|
||||
/** @return text to be placed before player name */
|
||||
String format(@Nullable Player player, String message);
|
||||
}
|
||||
|
||||
public interface InvalidCommandHandler{
|
||||
String handle(Player player, CommandResponse response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,11 +461,12 @@ public class Damage{
|
||||
}
|
||||
|
||||
private static void completeDamage(Team team, float x, float y, float radius, float damage){
|
||||
|
||||
int trad = (int)(radius / tilesize);
|
||||
for(int dx = -trad; dx <= trad; dx++){
|
||||
for(int dy = -trad; dy <= trad; dy++){
|
||||
Tile tile = world.tile(Math.round(x / tilesize) + dx, Math.round(y / tilesize) + dy);
|
||||
if(tile != null && tile.build != null && (team == null ||team.isEnemy(tile.team())) && dx*dx + dy*dy <= trad){
|
||||
if(tile != null && tile.build != null && (team == null ||team.isEnemy(tile.team())) && dx*dx + dy*dy <= trad*trad){
|
||||
tile.build.damage(team, damage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@ class GroupDefs<G>{
|
||||
@GroupDef(value = Firec.class) G fire;
|
||||
@GroupDef(value = Puddlec.class) G puddle;
|
||||
@GroupDef(value = WeatherStatec.class) G weather;
|
||||
@GroupDef(value = WorldLabelc.class, mapping = true) G label;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.math.*;
|
||||
import mindustry.entities.Units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class UnitSorts{
|
||||
public static Sortf
|
||||
|
||||
closest = Unit::dst2,
|
||||
farthest = (u, x, y) -> -u.dst2(x, y),
|
||||
strongest = (u, x, y) -> -u.maxHealth + Mathf.dst2(u.x, u.y, x, y) / 6400f,
|
||||
weakest = (u, x, y) -> u.maxHealth + Mathf.dst2(u.x, u.y, x, y) / 6400f;
|
||||
}
|
||||
@@ -81,6 +81,12 @@ public class Units{
|
||||
return Math.max(0, state.rules.unitCapVariable ? state.rules.unitCap + team.data().unitCap : state.rules.unitCap);
|
||||
}
|
||||
|
||||
/** @return unit cap as a string, substituting the infinity symbol instead of MAX_VALUE */
|
||||
public static String getStringCap(Team team){
|
||||
int cap = getCap(team);
|
||||
return cap >= Integer.MAX_VALUE - 1 ? "∞" : cap + "";
|
||||
}
|
||||
|
||||
/** @return whether this player can interact with a specific tile. if either of these are null, returns true.*/
|
||||
public static boolean canInteract(Player player, Building tile){
|
||||
return player == null || tile == null || tile.interactable(player.team());
|
||||
|
||||
@@ -61,6 +61,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
transient boolean updateFlow;
|
||||
transient byte cdump;
|
||||
transient int rotation;
|
||||
transient float payloadRotation;
|
||||
transient boolean enabled = true;
|
||||
transient float enabledControlTime;
|
||||
transient String lastAccessed;
|
||||
@@ -1293,16 +1294,18 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Called right before this building is picked up. */
|
||||
public void pickedUp(){
|
||||
|
||||
}
|
||||
|
||||
/** Called right after this building is picked up. */
|
||||
public void afterPickedUp(){
|
||||
if(power != null){
|
||||
if(power.graph != null){
|
||||
power.graph.removeList(self());
|
||||
power.graph = new PowerGraph();
|
||||
}
|
||||
power.graph = new PowerGraph();
|
||||
power.links.clear();
|
||||
power.status = 0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void removeFromProximity(){
|
||||
|
||||
@@ -93,6 +93,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
void pickup(Building tile){
|
||||
tile.pickedUp();
|
||||
tile.tile.remove();
|
||||
tile.afterPickedUp();
|
||||
addPayload(new BuildPayload(tile));
|
||||
Fx.unitPickup.at(tile);
|
||||
Events.fire(new PickupEvent(self(), tile));
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
/** Component/entity for labels in world space. Useful for servers. Does not save in files - create only on world load. */
|
||||
@EntityDef(value = {WorldLabelc.class}, serialize = false)
|
||||
@Component(base = true)
|
||||
public abstract class WorldLabelComp implements Posc, Drawc, Syncc{
|
||||
@Import int id;
|
||||
@Import float x, y;
|
||||
|
||||
public static final byte flagBackground = 1, flagOutline = 2;
|
||||
|
||||
public String text = "sample text";
|
||||
public float fontSize = 1f, z = Layer.playerName + 1;
|
||||
/** Flags are packed into a byte for sync efficiency; see the flag static values. */
|
||||
public byte flags = flagBackground | flagOutline;
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return text.length() * 10f * fontSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(z);
|
||||
float z = Drawf.text();
|
||||
|
||||
Font font = (flags & flagOutline) != 0 ? Fonts.outline : Fonts.def;
|
||||
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
|
||||
boolean ints = font.usesIntegerPositions();
|
||||
font.setUseIntegerPositions(false);
|
||||
font.getData().setScale(0.25f / Scl.scl(1f) * fontSize);
|
||||
layout.setText(font, text);
|
||||
|
||||
if((flags & flagBackground) != 0){
|
||||
Draw.color(0f, 0f, 0f, 0.3f);
|
||||
Fill.rect(x, y - layout.height / 2, layout.width + 2, layout.height + 3);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
font.setColor(Color.white);
|
||||
font.draw(text, x, y, 0, Align.center, false);
|
||||
|
||||
Draw.reset();
|
||||
Pools.free(layout);
|
||||
font.getData().setScale(1f);
|
||||
font.setColor(Color.white);
|
||||
font.setUseIntegerPositions(ints);
|
||||
|
||||
Draw.z(z);
|
||||
}
|
||||
|
||||
/** This MUST be called instead of remove()! */
|
||||
public void hide(){
|
||||
remove();
|
||||
Call.removeWorldLabel(id);
|
||||
}
|
||||
}
|
||||
@@ -234,8 +234,6 @@ public class Teams{
|
||||
public Queue<BlockPlan> blocks = new Queue<>();
|
||||
/** The current command for units to follow. */
|
||||
public UnitCommand command = UnitCommand.attack;
|
||||
/** Target items to mine. */
|
||||
public Seq<Item> mineItems = Seq.with(Items.copper, Items.lead, Items.titanium, Items.thorium);
|
||||
|
||||
/** Quadtree for all buildings of this team. Null if not active. */
|
||||
@Nullable
|
||||
@@ -373,4 +371,4 @@ public class Teams{
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import mindustry.content.*;
|
||||
import mindustry.content.TechTree.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.Units.*;
|
||||
import mindustry.entities.abilities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.effect.*;
|
||||
@@ -61,6 +62,7 @@ public class ContentParser{
|
||||
readFields(result, data);
|
||||
return result;
|
||||
});
|
||||
put(Sortf.class, (type, data) -> field(UnitSorts.class, data));
|
||||
put(Interp.class, (type, data) -> field(Interp.class, data));
|
||||
put(CacheLayer.class, (type, data) -> field(CacheLayer.class, data));
|
||||
put(Attribute.class, (type, data) -> Attribute.get(data.asString()));
|
||||
@@ -826,4 +828,4 @@ public class ContentParser{
|
||||
void parsed(Class<?> type, JsonValue jsonData, Object result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,8 @@ public class UnitType extends UnlockableContent{
|
||||
public BlockFlag[] targetFlags = {null};
|
||||
/** targetFlags, as an override for "non-AI" teams. By default, units of this type will rush the core. */
|
||||
public BlockFlag[] playerTargetFlags = {BlockFlag.core, null};
|
||||
/** Target items to mine. Used in MinerAI */
|
||||
public Seq<Item> mineItems = Seq.with(Items.copper, Items.lead, Items.titanium, Items.thorium);
|
||||
|
||||
public Color outlineColor = Pal.darkerMetal;
|
||||
public int outlineRadius = 3;
|
||||
@@ -1059,4 +1061,4 @@ public class UnitType extends UnlockableContent{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -94,9 +94,7 @@ public class Menus{
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void labelReliable(String message, float duration, float worldx, float worldy){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showLabel(message, duration, worldx, worldy);
|
||||
label(message, duration, worldx, worldy);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
@@ -113,6 +111,15 @@ public class Menus{
|
||||
ui.hudfrag.showToast(Fonts.getGlyph(Fonts.icon, (char)unicode), text);
|
||||
}
|
||||
|
||||
//internal use only
|
||||
@Remote
|
||||
public static void removeWorldLabel(int id){
|
||||
var label = Groups.label.getByID(id);
|
||||
if(label != null){
|
||||
label.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public interface MenuListener{
|
||||
void get(Player player, int option);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class Block extends UnlockableContent{
|
||||
public boolean solid;
|
||||
/** whether this block CAN be solid. */
|
||||
public boolean solidifes;
|
||||
/** whether this is rotateable */
|
||||
/** whether this is rotatable */
|
||||
public boolean rotate;
|
||||
/** number of different variant regions to use */
|
||||
public int variants = 0;
|
||||
|
||||
@@ -29,8 +29,7 @@ public class Thruster extends Wall{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
Draw.rect(block.region, x, y);
|
||||
Draw.rect(topRegion, x, y, rotdeg());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class Turret extends ReloadTurret{
|
||||
public Effect chargeBeginEffect = Fx.none;
|
||||
public Sound chargeSound = Sounds.none;
|
||||
|
||||
public Sortf unitSort = Unit::dst2;
|
||||
public Sortf unitSort = UnitSorts.closest;
|
||||
|
||||
protected Vec2 tr = new Vec2();
|
||||
protected Vec2 tr2 = new Vec2();
|
||||
@@ -519,4 +519,4 @@ public class Turret extends ReloadTurret{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ public class PayloadConveyor extends Block{
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
Building accept = nearby(Geometry.d4(rotation).x * size, Geometry.d4(rotation).y * size);
|
||||
Building accept = nearby(Geometry.d4(rotation).x * (size/2+1), Geometry.d4(rotation).y * (size/2+1));
|
||||
//next block must be aligned and of the same size
|
||||
if(accept != null && (
|
||||
//same size
|
||||
|
||||
@@ -153,6 +153,11 @@ public class StackConveyor extends Block implements Autotiler{
|
||||
Draw.z(Layer.block - 0.15f);
|
||||
super.drawCracks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void payloadDraw(){
|
||||
Draw.rect(block.fullIcon, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
|
||||
@@ -80,6 +80,7 @@ public class BuildPayload implements Payload{
|
||||
@Override
|
||||
public void set(float x, float y, float rotation){
|
||||
build.set(x, y);
|
||||
build.payloadRotation = rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -64,8 +64,11 @@ public class Unloader extends Block{
|
||||
float loadFactor;
|
||||
boolean canLoad;
|
||||
boolean canUnload;
|
||||
int index;
|
||||
}
|
||||
|
||||
public int[] lastUsed;
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(((unloadTimer += delta()) < speed) || (proximity.size < 2)) return;
|
||||
@@ -80,14 +83,14 @@ public class Unloader extends Block{
|
||||
for(int i = tmp; i < proximity.size; i++){
|
||||
possibleBlocks.set(i, new ContainerStat());
|
||||
}
|
||||
lastUsed = new int[proximity.size];
|
||||
}
|
||||
|
||||
if(sortItem != null){
|
||||
item = sortItem;
|
||||
|
||||
for(int j = 0; j < proximity.size; j++){
|
||||
int pos = (offset + j) % proximity.size;
|
||||
var other = proximity.get(j);
|
||||
for(int pos = 0; pos < proximity.size; pos++){
|
||||
var other = proximity.get(pos);
|
||||
boolean interactable = other.interactable(team);
|
||||
|
||||
//set the stats of all buildings in possibleBlocks
|
||||
@@ -95,6 +98,7 @@ public class Unloader extends Block{
|
||||
pb.building = other;
|
||||
pb.canUnload = interactable && other.canUnload() && other.items != null && other.items.has(sortItem);
|
||||
pb.canLoad = interactable && !(other.block instanceof StorageBlock) && other.acceptItem(this, sortItem);
|
||||
pb.index = pos;
|
||||
}
|
||||
}else{
|
||||
//select the next item for nulloaders
|
||||
@@ -106,9 +110,8 @@ public class Unloader extends Block{
|
||||
boolean isDistinct = false;
|
||||
Item possibleItem = content.item(total);
|
||||
|
||||
for(int j = 0; j < proximity.size; j++){
|
||||
int pos = (offset + j) % proximity.size;
|
||||
var other = proximity.get(j);
|
||||
for(int pos = 0; pos < proximity.size; pos++){
|
||||
var other = proximity.get(pos);
|
||||
boolean interactable = other.interactable(team);
|
||||
|
||||
//set the stats of all buildings in possibleBlocks while we are at it
|
||||
@@ -116,6 +119,7 @@ public class Unloader extends Block{
|
||||
pb.building = other;
|
||||
pb.canUnload = interactable && other.canUnload() && other.items != null && other.items.has(possibleItem);
|
||||
pb.canLoad = interactable && !(other.block instanceof StorageBlock) && other.acceptItem(this, possibleItem);
|
||||
pb.index = pos;
|
||||
|
||||
//the part handling framerate issues and slow conveyor belts, to avoid skipping items
|
||||
if(hasProvider && pb.canLoad) isDistinct = true;
|
||||
@@ -138,11 +142,13 @@ public class Unloader extends Block{
|
||||
pb.loadFactor = (other.getMaximumAccepted(item) == 0) || (other.items == null) ? 0 : other.items.get(item) / (float)other.getMaximumAccepted(item);
|
||||
}
|
||||
|
||||
//sort so it gives full priority to blocks that can give but not receive (stackConveyors and Storage), and then by load
|
||||
//sort so it gives full priority to blocks that can give but not receive (stackConveyors and Storage), and then by load, and then by last use
|
||||
possibleBlocks.sort(Structs.comps(
|
||||
Structs.comparingBool(e -> e.building.block.highUnloadPriority),
|
||||
Structs.comparingFloat(e -> e.loadFactor)
|
||||
));
|
||||
Structs.comps(
|
||||
Structs.comparingFloat(e -> e.loadFactor),
|
||||
Structs.comparingInt(e -> -lastUsed[e.index])
|
||||
)));
|
||||
|
||||
ContainerStat dumpingFrom = null;
|
||||
ContainerStat dumpingTo = null;
|
||||
@@ -163,10 +169,17 @@ public class Unloader extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
//increment the priority if not used
|
||||
for(int i = 0; i < possibleBlocks.size; i++){
|
||||
lastUsed[i] = (lastUsed[i] + 1 ) % 2147483647;
|
||||
}
|
||||
|
||||
//trade the items
|
||||
if(dumpingFrom != null && dumpingTo != null && (dumpingFrom.loadFactor != dumpingTo.loadFactor || dumpingFrom.building.block.highUnloadPriority)){
|
||||
dumpingTo.building.handleItem(this, item);
|
||||
dumpingFrom.building.removeStack(item, 1);
|
||||
lastUsed[dumpingFrom.index] = 0;
|
||||
lastUsed[dumpingTo.index] = 0;
|
||||
any = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ public class UnitFactory extends UnitBlock{
|
||||
Core.bundle.format("bar.unitcap",
|
||||
Fonts.getUnicodeStr(e.unit().name),
|
||||
e.team.data().countType(e.unit()),
|
||||
Units.getCap(e.team)
|
||||
Units.getStringCap(e.team)
|
||||
),
|
||||
() -> Pal.power,
|
||||
() -> e.unit() == null ? 0f : (float)e.team.data().countType(e.unit()) / Units.getCap(e.team)
|
||||
|
||||
Reference in New Issue
Block a user