Research system progress

This commit is contained in:
Anuken
2020-06-19 19:31:14 -04:00
parent 08ac91359b
commit 6bcc103cf7
59 changed files with 393 additions and 415 deletions

View File

@@ -170,7 +170,7 @@ public class Vars implements Loadable{
public static Net net;
public static ContentLoader content;
public static GameState state;
public static GlobalData data;
public static CampaignData data;
public static EntityCollisions collisions;
public static DefaultWaves defaultWaves;
public static mindustry.audio.LoopControl loops;
@@ -256,7 +256,7 @@ public class Vars implements Loadable{
bases = new BaseRegistry();
state = new GameState();
data = new GlobalData();
data = new CampaignData();
mobile = Core.app.isMobile() || testMobile;
ios = Core.app.isIOS();

View File

@@ -1848,6 +1848,7 @@ public class Blocks implements ContentList{
requirements(Category.effect, BuildVisibility.campaignOnly, ItemStack.with(Items.copper, 200, Items.lead, 100));
size = 3;
alwaysUnlocked = true;
}};
//endregion campaign

View File

@@ -1,5 +1,6 @@
package mindustry.content;
import arc.*;
import arc.math.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
@@ -11,12 +12,15 @@ import mindustry.world.*;
import static mindustry.content.Blocks.*;
public class TechTree implements ContentList{
private static ObjectMap<UnlockableContent, TechNode> map = new ObjectMap<>();
public static Seq<TechNode> all;
public static TechNode root;
@Override
public void load(){
TechNode.context = null;
map = new ObjectMap<>();
all = new Seq<>();
root = node(coreShard, () -> {
@@ -312,6 +316,14 @@ public class TechTree implements ContentList{
return node(block, () -> {});
}
public static @Nullable TechNode get(UnlockableContent content){
return map.get(content);
}
public static TechNode getNotNull(UnlockableContent content){
return map.getThrow(content, () -> new RuntimeException(content + " does not have a tech node"));
}
public static class TechNode{
private static TechNode context;
@@ -325,12 +337,14 @@ public class TechTree implements ContentList{
public ItemStack[] requirements;
/** Extra objectives needed to research this. TODO implement */
public Objective[] objectives = {};
/** Research turns required to research this content. */
public int turns = 3; //TODO keep track of turns that have been used so far
/** Time required to research this content, in seconds. */
public float time = 60; //TODO implement
/** Nodes that depend on this node. */
public final Seq<TechNode> children = new Seq<>();
/** Research progress, in seconds. */
public float progress;
TechNode(TechNode ccontext, UnlockableContent content, ItemStack[] requirements, Runnable children){
TechNode(@Nullable TechNode ccontext, UnlockableContent content, ItemStack[] requirements, Runnable children){
if(ccontext != null){
ccontext.children.add(this);
}
@@ -339,7 +353,9 @@ public class TechTree implements ContentList{
this.content = content;
this.requirements = requirements;
this.depth = parent == null ? 0 : parent.depth + 1;
this.progress = Core.settings.getFloat("research-" + content.name, 0f);
map.put(content, this);
context = this;
children.run();
context = ccontext;
@@ -349,5 +365,10 @@ public class TechTree implements ContentList{
TechNode(UnlockableContent content, ItemStack[] requirements, Runnable children){
this(context, content, requirements, children);
}
/** Flushes research progress to settings. */
public void save(){
Core.settings.put("research-" + content.name, progress);
}
}
}

View File

@@ -459,9 +459,6 @@ public class Control implements ApplicationListener, Loadable{
input.updateState();
//autosave global data if it's modified
data.checkSave();
music.update();
loops.update();
Time.updateGlobal();

View File

@@ -6,7 +6,6 @@ import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.core.GameState.*;
import mindustry.ctype.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.game.Teams.*;
@@ -114,13 +113,6 @@ public class Logic implements ApplicationListener{
}
/** Handles the event of content being used by either the player or some block. */
public void handleContent(UnlockableContent content){
if(!headless){
data.unlockContent(content);
}
}
/** Adds starting items, resets wave time, and sets state to playing. */
public void play(){
state.set(State.playing);
@@ -251,8 +243,9 @@ public class Logic implements ApplicationListener{
Time.runTask(30f, () -> {
for(Tilec entity : state.teams.playerCores()){
for(Item item : content.items()){
data.addItem(item, entity.items().get(item));
Events.fire(new LaunchItemEvent(new ItemStack(item, entity.items().get(item))));
//TODO where do the items go?
//data.addItem(item, entity.items().get(item));
//Events.fire(new LaunchItemEvent(new ItemStack(item, entity.items().get(item))));
}
entity.tile().remove();
}

View File

@@ -1,13 +1,14 @@
package mindustry.ctype;
import arc.*;
import arc.util.ArcAnnotate.*;
import mindustry.annotations.Annotations.*;
import arc.graphics.g2d.*;
import arc.scene.ui.layout.*;
import mindustry.*;
import arc.util.ArcAnnotate.*;
import mindustry.annotations.Annotations.*;
import mindustry.graphics.*;
import mindustry.ui.Cicon;
import mindustry.ui.*;
import static mindustry.Vars.*;
/** Base interface for an unlockable content type. */
public abstract class UnlockableContent extends MappableContent{
@@ -63,12 +64,12 @@ public abstract class UnlockableContent extends MappableContent{
}
public final boolean unlocked(){
return Vars.data.isUnlocked(this);
return data.isUnlocked(this);
}
/** @return whether this content is unlocked, or the player is in a custom game. */
public final boolean unlockedCur(){
return Vars.data.isUnlocked(this) || !Vars.state.isCampaign();
/** @return whether this content is unlocked, or the player is in a custom (non-campaign) game. */
public final boolean unlockedNow(){
return data.isUnlocked(this) || !state.isCampaign();
}
public final boolean locked(){

View File

@@ -1,22 +1,18 @@
package mindustry.editor;
import arc.*;
import arc.struct.IntSet;
import arc.struct.IntSet.IntSetIterator;
import arc.graphics.Color;
import arc.graphics.Texture;
import arc.graphics.g2d.Draw;
import arc.graphics.g2d.TextureRegion;
import arc.math.Mathf;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.struct.*;
import arc.util.*;
import mindustry.content.Blocks;
import mindustry.content.*;
import mindustry.game.EventType.*;
import mindustry.game.Team;
import mindustry.graphics.IndexedRenderer;
import mindustry.world.Block;
import mindustry.world.Tile;
import mindustry.game.*;
import mindustry.graphics.*;
import mindustry.world.*;
import static mindustry.Vars.tilesize;
import static mindustry.Vars.*;
public class MapRenderer implements Disposable{
private static final int chunkSize = 64;
@@ -62,13 +58,7 @@ public class MapRenderer implements Disposable{
public void draw(float tx, float ty, float tw, float th){
Draw.flush();
IntSetIterator it = updates.iterator();
while(it.hasNext){
int i = it.next();
int x = i % width;
int y = i / width;
render(x, y);
}
updates.each(i -> render(i % width, i / width));
updates.clear();
updates.addAll(delayedUpdates);

View File

@@ -18,7 +18,6 @@ import arc.util.io.*;
import mindustry.annotations.Annotations.*;
import mindustry.audio.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
@@ -552,8 +551,6 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTree
public void offload(Item item){
int dump = this.dump;
useContent(item);
for(int i = 0; i < proximity.size; i++){
incrementDump(proximity.size);
Tilec other = proximity.get((i + dump) % proximity.size);
@@ -571,7 +568,6 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTree
*/
public boolean put(Item item){
int dump = this.dump;
useContent(item);
for(int i = 0; i < proximity.size; i++){
incrementDump(proximity.size);
@@ -814,14 +810,6 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTree
public void unitRemoved(Unitc unit){
}
/** Call when some content is produced. This unlocks the content if it is applicable. */
public void useContent(UnlockableContent content){
//only unlocks content in zones
if(!headless && team() == player.team() && state.isCampaign()){
logic.handleContent(content);
}
}
/** Called when arbitrary configuration is applied to a tile. */
public void configured(@Nullable Playerc player, @Nullable Object value){
//null is of type void.class; anonymous classes use their superclass.

View File

@@ -0,0 +1,47 @@
package mindustry.game;
import arc.*;
import arc.struct.*;
import mindustry.ctype.*;
import mindustry.game.EventType.*;
/** Stores unlocks and tech tree state. */
public class CampaignData{
private ObjectSet<String> unlocked = new ObjectSet<>();
/** @return whether or not this piece of content is unlocked yet. */
public boolean isUnlocked(UnlockableContent content){
return content.alwaysUnlocked || unlocked.contains(content.name);
}
/**
* Makes this piece of content 'unlocked', if possible.
* If this piece of content is already unlocked, nothing changes.
*/
public void unlockContent(UnlockableContent content){
if(content.alwaysUnlocked) return;
//fire unlock event so other classes can use it
if(unlocked.add(content.name)){
content.onUnlock();
Events.fire(new UnlockEvent(content));
save();
}
}
/** Clears all unlocked content. Automatically saves. */
public void reset(){
save();
}
@SuppressWarnings("unchecked")
public void load(){
unlocked = Core.settings.getJson("unlocked-content", ObjectSet.class, ObjectSet::new);
}
public void save(){
Core.settings.putJson("unlocked-content", String.class, unlocked);
}
}

View File

@@ -1,183 +0,0 @@
package mindustry.game;
import arc.*;
import arc.files.*;
import arc.math.*;
import arc.struct.*;
import arc.util.io.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.game.EventType.*;
import mindustry.type.*;
import java.io.*;
import java.util.zip.*;
import static mindustry.Vars.*;
/** Stores player unlocks. Clientside only. */
public class GlobalData{
private ObjectSet<String> unlocked = new ObjectSet<>();
private ObjectIntMap<Item> items = new ObjectIntMap<>();
private boolean modified;
public void exportData(Fi file) throws IOException{
Seq<Fi> files = new Seq<>();
files.add(Core.settings.getSettingsFile());
files.addAll(customMapDirectory.list());
files.addAll(saveDirectory.list());
files.addAll(screenshotDirectory.list());
files.addAll(modDirectory.list());
files.addAll(schematicDirectory.list());
String base = Core.settings.getDataDirectory().path();
try(OutputStream fos = file.write(false, 2048); ZipOutputStream zos = new ZipOutputStream(fos)){
for(Fi add : files){
if(add.isDirectory()) continue;
zos.putNextEntry(new ZipEntry(add.path().substring(base.length())));
Streams.copy(add.read(), zos);
zos.closeEntry();
}
}
}
public void importData(Fi file){
Fi dest = Core.files.local("zipdata.zip");
file.copyTo(dest);
Fi zipped = new ZipFi(dest);
Fi base = Core.settings.getDataDirectory();
if(!zipped.child("settings.bin").exists()){
throw new IllegalArgumentException("Not valid save data.");
}
//purge existing tmp data, keep everything else
tmpDirectory.deleteDirectory();
zipped.walk(f -> f.copyTo(base.child(f.path())));
dest.delete();
}
public void modified(){
modified = true;
}
public int getItem(Item item){
return items.get(item, 0);
}
public void addItem(Item item, int amount){
if(amount > 0){
unlockContent(item);
}
amount = Math.max(amount, 0);
items.getAndIncrement(item, 0, amount);
state.stats.itemsDelivered.getAndIncrement(item, 0, amount);
//clamp to capacity
items.put(item, Mathf.clamp(items.get(item), 0, getItemCapacity()));
//clamp overflow
if(state.stats.itemsDelivered.get(item, 0) < 0) state.stats.itemsDelivered.put(item, Integer.MAX_VALUE);
modified = true;
}
public boolean hasItems(Seq<ItemStack> stacks){
return !stacks.contains(s -> items.get(s.item, 0) < s.amount);
}
public boolean hasItems(ItemStack[] stacks){
for(ItemStack stack : stacks){
if(!has(stack.item, stack.amount)){
return false;
}
}
return true;
}
public void removeItems(ItemStack[] stacks){
for(ItemStack stack : stacks){
remove(stack.item, stack.amount);
}
}
public void removeItems(Seq<ItemStack> stacks){
for(ItemStack stack : stacks){
remove(stack.item, stack.amount);
}
}
public void remove(Item item, int amount){
items.getAndIncrement(item, 0, -amount);
modified = true;
}
public boolean has(Item item, int amount){
return items.get(item, 0) >= amount;
}
//TODO: make it upgradeable
public int getItemCapacity(){
return 10000;
}
/** Returns whether or not this piece of content is unlocked yet. */
public boolean isUnlocked(UnlockableContent content){
return content.alwaysUnlocked || unlocked.contains(content.name);
}
/**
* Makes this piece of content 'unlocked', if possible.
* If this piece of content is already unlocked, nothing changes.
* Results are not saved until you call {@link #save()}.
*/
public void unlockContent(UnlockableContent content){
if(content.alwaysUnlocked) return;
//fire unlock event so other classes can use it
if(unlocked.add(content.name)){
modified = true;
content.onUnlock();
Events.fire(new UnlockEvent(content));
}
}
/** Clears all unlocked content. Automatically saves. */
public void reset(){
save();
}
public void checkSave(){
if(modified){
save();
modified = false;
}
}
@SuppressWarnings("unchecked")
public void load(){
items.clear();
unlocked = Core.settings.getJson("unlocked-content", ObjectSet.class, ObjectSet::new);
for(Item item : Vars.content.items()){
items.put(item, Core.settings.getInt("item-" + item.name, 0));
}
//set up default values
if(!Core.settings.has("item-" + Items.copper.name)){
addItem(Items.copper, 50);
}
}
public void save(){
Core.settings.putJson("unlocked-content", String.class, unlocked);
for(Item item : Vars.content.items()){
Core.settings.put("item-" + item.name, items.get(item, 0));
}
}
}

View File

@@ -278,7 +278,7 @@ public class Schematics implements Loadable{
/** Creates an array of build requests from a schematic's data, centered on the provided x+y coordinates. */
public Seq<BuildPlan> toRequests(Schematic schem, int x, int y){
return schem.tiles.map(t -> new BuildPlan(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block).original(t.x, t.y, schem.width, schem.height).configure(t.config))
.removeAll(s -> !s.block.isVisible() || !s.block.unlockedCur());
.removeAll(s -> !s.block.isVisible() || !s.block.unlockedNow());
}
/** Adds a schematic to the list, also copying it into the files.*/

View File

@@ -24,6 +24,7 @@ public class Universe{
load();
}
/** Update regardless of whether the player is in the campaign. */
public void updateGlobal(){
//currently only updates one solar system
updatePlanet(Planets.sun);
@@ -40,6 +41,7 @@ public class Universe{
}
}
/** Update planet rotations, global time and relevant state. */
public void update(){
secondCounter += Time.delta() / 60f;
@@ -119,9 +121,11 @@ public class Universe{
}
//calculate passive item generation
//TODO make exports only update for sector with items
//TODO items should be added directly to cores!
int[] exports = getTotalExports();
for(int i = 0; i < exports.length; i++){
data.addItem(content.item(i), exports[i]);
//data.addItem(content.item(i), exports[i]);
}
Events.fire(new TurnEvent());

View File

@@ -180,7 +180,7 @@ public class DesktopInput extends InputHandler{
}
if((player.dead() || state.isPaused()) && !ui.chatfrag.shown()){
if(!(scene.getKeyboardFocus() instanceof TextField)){
if(!(scene.getKeyboardFocus() instanceof TextField) && !scene.hasDialog()){
//move camera around
float camSpeed = !Core.input.keyDown(Binding.boost) ? 3f : 8f;
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta() * camSpeed));

View File

@@ -651,7 +651,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(Core.settings.getBool("blockreplace")){
lineRequests.each(req -> {
Block replace = req.block.getReplacement(req, lineRequests);
if(replace.unlockedCur()){
if(replace.unlockedNow()){
req.block = replace;
}
});

View File

@@ -6,6 +6,7 @@ import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.core.*;
import mindustry.ctype.*;
import mindustry.game.*;
@@ -75,6 +76,11 @@ public abstract class SaveVersion extends SaveFileReader{
state.secinfo.prepare();
}
//flush tech node progress
for(TechNode node : TechTree.all){
node.save();
}
writeStringMap(stream, StringMap.of(
"saved", Time.millis(),
"playtime", headless ? 0 : control.saves.getTotalPlaytime(),

View File

@@ -7,6 +7,8 @@ import arc.util.io.*;
import arc.util.pooling.*;
import mindustry.ai.types.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.ctype.*;
import mindustry.entities.bullet.*;
import mindustry.entities.units.*;
@@ -67,6 +69,11 @@ public class TypeIO{
for(int i = 0; i < ((Point2[])object).length; i++){
write.i(((Point2[])object)[i].pack());
}
}else if(object instanceof TechNode){
TechNode map = (TechNode)object;
write.b(9);
write.b((byte)map.content.getContentType().ordinal());
write.s(map.content.id);
}else{
throw new IllegalArgumentException("Unknown object type: " + object.getClass());
}
@@ -84,6 +91,7 @@ public class TypeIO{
case 6: short length = read.s(); IntSeq arr = new IntSeq(); for(int i = 0; i < length; i ++) arr.add(read.i()); return arr;
case 7: return new Point2(read.i(), read.i());
case 8: byte len = read.b(); Point2[] out = new Point2[len]; for(int i = 0; i < len; i ++) out[i] = Point2.unpack(read.i()); return out;
case 9: return TechTree.getNotNull(content.getByID(ContentType.all[read.b()], read.s()));
default: throw new IllegalArgumentException("Unknown object type: " + type);
}
}

View File

@@ -80,7 +80,6 @@ public class Map implements Comparable<Map>, Publishable{
public void setHighScore(int score){
Core.settings.put("hiscore" + file.nameWithoutExtension(), score);
Vars.data.modified();
}
/** Returns the result of applying this map's rules to the specified gamemode.*/

View File

@@ -48,7 +48,7 @@ public class SectorPreset extends UnlockableContent{
}
public boolean canUnlock(){
return data.isUnlocked(this) || !requirements.contains(r -> !r.complete());
return unlocked() || !requirements.contains(r -> !r.complete());
}
public Seq<ItemStack> getLaunchCost(){
@@ -74,7 +74,6 @@ public class SectorPreset extends UnlockableContent{
public void setLaunched(){
updateObjectives(() -> {
Core.settings.put(name + "-launched", true);
data.modified();
});
}
@@ -84,7 +83,6 @@ public class SectorPreset extends UnlockableContent{
if(value < wave){
updateObjectives(() -> {
Core.settings.put(name + "-wave", wave);
data.modified();
});
}
}
@@ -133,7 +131,6 @@ public class SectorPreset extends UnlockableContent{
stacks.sort();
launchCost = stacks;
data.modified();
}
/** Whether this zone has met its condition; if true, the player can leave. */
@@ -148,7 +145,6 @@ public class SectorPreset extends UnlockableContent{
@Override
public void init(){
for(ItemStack stack : startingItems){
defaultStartingItems.add(new ItemStack(stack.item, stack.amount));
}

View File

@@ -113,7 +113,7 @@ public abstract class Weather extends MappableContent{
private static final float fadeTime = 60 * 4;
Weather weather;
float intensity = 1f, opacity = 1f, life;
float intensity = 1f, opacity = 0f, life;
void init(Weather weather){
this.weather = weather;

View File

@@ -30,7 +30,7 @@ public class ItemsDisplay extends Table{
t.marginRight(30f);
t.left();
for(Item item : content.items()){
if(item.type == ItemType.material && data.isUnlocked(item)){
if(item.type == ItemType.material && item.unlocked()){
t.label(() -> format(item)).left();
t.image(item.icon(Cicon.small)).size(8 * 3).padLeft(4).padRight(4);
t.add(item.localizedName).color(Color.lightGray).left();
@@ -51,10 +51,11 @@ public class ItemsDisplay extends Table{
private String format(Item item){
builder.setLength(0);
builder.append(ui.formatAmount(data.getItem(item)));
if(state.isGame() && player.team().data().hasCore() && player.team().core().items().get(item) > 0){
builder.append("[TODO implement]");
//builder.append(ui.formatAmount(data.getItem(item)));
if(state.isGame() && player.team().data().hasCore() && player.team().core().items.get(item) > 0){
builder.append(" [unlaunched]+ ");
builder.append(ui.formatAmount(state.teams.get(player.team()).core().items().get(item)));
builder.append(ui.formatAmount(state.teams.get(player.team()).core().items.get(item)));
}
return builder.toString();
}

View File

@@ -30,7 +30,7 @@ public class LanguageDialog extends BaseDialog{
Table langs = new Table();
langs.marginRight(24f).marginLeft(24f);
ScrollPane pane = new ScrollPane(langs);
pane.setFadeScrollBars(false);
pane.setScrollingDisabled(true, false);
ButtonGroup<TextButton> group = new ButtonGroup<>();

View File

@@ -40,11 +40,12 @@ public class PausedDialog extends BaseDialog{
cont.button("$back", Icon.left, this::hide).colspan(2).width(dw * 2 + 20f);
cont.row();
if(state.isCampaign()){
cont.button("$techtree", Icon.tree, ui.tech::show);
}else{
cont.button("$database", Icon.book, ui.database::show);
}
//if(state.isCampaign()){
// cont.button("$techtree", Icon.tree, ui.tech::show);
//}else{
// cont.button("$database", Icon.book, ui.database::show);
//}
cont.button("placeholder", Icon.warning, () -> ui.showInfo("go away"));
cont.button("$settings", Icon.settings, ui.settings::show);
if(!state.rules.tutorial){

View File

@@ -1,6 +1,5 @@
package mindustry.ui.dialogs;
import arc.graphics.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.ui.*;
@@ -23,14 +22,15 @@ public class ResourcesDialog extends BaseDialog{
t.margin(10f);
int[] exports = universe.getTotalExports();
for(Item item : content.items()){
if(exports[item.id] > 0 || data.getItem(item) > 0){
//TODO display total items
if(exports[item.id] > 0){
t.image(item.icon(Cicon.small)).padRight(4);
t.add(ui.formatAmount(data.getItem(item))).color(Color.lightGray);
if(exports[item.id] > 0){
//t.add(ui.formatAmount(data.getItem(item))).color(Color.lightGray);
//if(exports[item.id] > 0){
t.add("+ [accent]" + ui.formatAmount(exports[item.id]) + " [lightgray]/T");
}else{
t.add();
}
//}else{
// t.add();
//}
t.row();
}
}

View File

@@ -13,6 +13,7 @@ import arc.scene.ui.TextButton.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.core.GameState.*;
import mindustry.core.*;
import mindustry.game.EventType.*;
@@ -21,7 +22,11 @@ import mindustry.graphics.*;
import mindustry.input.*;
import mindustry.ui.*;
import static arc.Core.bundle;
import java.io.*;
import java.util.zip.*;
import static arc.Core.*;
import static mindustry.Vars.net;
import static mindustry.Vars.*;
public class SettingsMenuDialog extends SettingsDialog{
@@ -107,7 +112,7 @@ public class SettingsMenuDialog extends SettingsDialog{
if(ios){
Fi file = Core.files.local("mindustry-data-export.zip");
try{
data.exportData(file);
exportData(file);
}catch(Exception e){
ui.showException(e);
}
@@ -115,7 +120,7 @@ public class SettingsMenuDialog extends SettingsDialog{
}else{
platform.showFileChooser(false, "zip", file -> {
try{
data.exportData(file);
exportData(file);
ui.showInfo("$data.exported");
}catch(Exception e){
e.printStackTrace();
@@ -129,7 +134,7 @@ public class SettingsMenuDialog extends SettingsDialog{
t.button("$data.import", Icon.download, style, () -> ui.showConfirm("$confirm", "$data.import.confirm", () -> platform.showFileChooser(true, "zip", file -> {
try{
data.importData(file);
importData(file);
Core.app.exit();
}catch(IllegalArgumentException e){
ui.showErrorMessage("$data.invalid");
@@ -365,6 +370,43 @@ public class SettingsMenuDialog extends SettingsDialog{
graphics.checkPref("flow", true);
}
public void exportData(Fi file) throws IOException{
Seq<Fi> files = new Seq<>();
files.add(Core.settings.getSettingsFile());
files.addAll(customMapDirectory.list());
files.addAll(saveDirectory.list());
files.addAll(screenshotDirectory.list());
files.addAll(modDirectory.list());
files.addAll(schematicDirectory.list());
String base = Core.settings.getDataDirectory().path();
try(OutputStream fos = file.write(false, 2048); ZipOutputStream zos = new ZipOutputStream(fos)){
for(Fi add : files){
if(add.isDirectory()) continue;
zos.putNextEntry(new ZipEntry(add.path().substring(base.length())));
Streams.copy(add.read(), zos);
zos.closeEntry();
}
}
}
public void importData(Fi file){
Fi dest = Core.files.local("zipdata.zip");
file.copyTo(dest);
Fi zipped = new ZipFi(dest);
Fi base = Core.settings.getDataDirectory();
if(!zipped.child("settings.bin").exists()){
throw new IllegalArgumentException("Not valid save data.");
}
//purge existing tmp data, keep everything else
tmpDirectory.deleteDirectory();
zipped.walk(f -> f.copyTo(base.child(f.path())));
dest.delete();
}
private void back(){
rebuildMenu();
prefs.clearChildren();

View File

@@ -1,6 +1,7 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.func.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.input.*;
@@ -35,15 +36,17 @@ public class TechTreeDialog extends BaseDialog{
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
private TechTreeNode root = new TechTreeNode(TechTree.root, null);
private Rect bounds = new Rect();
private ItemsDisplay items;
private View view;
private Cons<TechNode> selector = c -> {};
public TechTreeDialog(){
super("");
titleTable.remove();
margin(0f).marginBottom(8);
Stack stack = cont.stack(view = new View(), items = new ItemsDisplay()).grow().get();
Stack stack = cont.stack(view = new View()/*, items = new ItemsDisplay()*/).grow().get();
shouldPause = true;
Events.on(ContentReloadEvent.class, e -> {
nodes.clear();
@@ -72,7 +75,7 @@ public class TechTreeDialog extends BaseDialog{
addListener(new InputListener(){
@Override
public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY){
view.setScale(Mathf.clamp(view.getScaleX() - amountY / 40f, 0.25f, 1f));
view.setScale(Mathf.clamp(view.getScaleX() - amountY / 10f * view.getScaleX(), 0.25f, 1f));
view.setOrigin(Align.center);
view.setTransform(true);
return true;
@@ -112,6 +115,15 @@ public class TechTreeDialog extends BaseDialog{
});
}
public Dialog show(Cons<TechNode> selector){
this.selector = selector;
return super.show();
}
public Dialog show(){
return show(c -> {});
}
void treeLayout(){
float spacing = 20f;
LayoutNode node = new LayoutNode(root, null);
@@ -163,8 +175,6 @@ public class TechTreeDialog extends BaseDialog{
l.visible = !locked;
checkNodes(l);
}
items.rebuild();
}
void showToast(String info){
@@ -247,9 +257,13 @@ public class TechTreeDialog extends BaseDialog{
}
});
}
}else if(data.hasItems(node.node.requirements) && locked(node.node)){
unlock(node.node);
}else if(locked(node.node)){
selector.get(node.node);
}
//TODO select it
//else if(data.hasItems(node.node.requirements) && locked(node.node)){
// unlock(node.node);
//}
});
button.hovered(() -> {
if(!mobile && hoverNode != button && node.visible){
@@ -269,7 +283,7 @@ public class TechTreeDialog extends BaseDialog{
button.update(() -> {
float offset = (Core.graphics.getHeight() % 2) / 2f;
button.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f + offset, Align.center);
button.getStyle().up = !locked(node.node) ? Tex.buttonOver : !data.hasItems(node.node.requirements) ? Tex.buttonRed : Tex.button;
button.getStyle().up = !locked(node.node) ? Tex.buttonOver : Tex.button;
((TextureRegionDrawable)button.getStyle().imageUp)
.setRegion(node.visible ? node.node.content.icon(Cicon.medium) : Icon.lock.getRegion());
button.getImage().setColor(!locked(node.node) ? Color.white : Color.gray);
@@ -304,9 +318,11 @@ public class TechTreeDialog extends BaseDialog{
panY = ry - bounds.y - oy;
}
/*
void unlock(TechNode node){
data.unlockContent(node.content);
data.removeItems(node.requirements);
//TODO this should not happen
//data.removeItems(node.requirements);
showToast(Core.bundle.format("researched", node.content.localizedName));
checkNodes(root);
hoverNode = null;
@@ -315,7 +331,7 @@ public class TechTreeDialog extends BaseDialog{
Core.scene.act();
Sounds.unlock.play();
Events.fire(new ResearchEvent(node.content));
}
}*/
void rebuild(){
ImageButton button = hoverNode;
@@ -357,8 +373,8 @@ public class TechTreeDialog extends BaseDialog{
list.left();
list.image(req.item.icon(Cicon.small)).size(8 * 3).padRight(3);
list.add(req.item.localizedName).color(Color.lightGray);
list.label(() -> " " + Math.min(data.getItem(req.item), req.amount) + " / " + req.amount)
.update(l -> l.setColor(data.has(req.item, req.amount) ? Color.lightGray : Color.scarlet));
list.label(() -> " " + (player.team().core() != null ? Math.min(player.team().core().items.get(req.item), req.amount) + " / " : "") + req.amount)
.update(l -> {}/*l.setColor(data.has(req.item, req.amount) ? Color.lightGray : Color.scarlet)*/);//TODO
}).fillX().left();
t.row();
}
@@ -383,11 +399,13 @@ public class TechTreeDialog extends BaseDialog{
}
}).pad(9);
//TODO research select button
/*
if(mobile && locked(node)){
b.row();
b.button("$research", Icon.ok, Styles.nodet, () -> unlock(node))
.disabled(i -> !data.hasItems(node.requirements)).growX().height(44f).colspan(3);
}
}*/
});
infoTable.row();

View File

@@ -426,7 +426,7 @@ public class PlacementFragment extends Fragment{
}
boolean unlocked(Block block){
return !state.isCampaign() || data.isUnlocked(block);
return block.unlockedNow();
}
boolean hasInfoBox(){

View File

@@ -388,12 +388,12 @@ public class Block extends UnlockableContent{
}
/** Configure when a null value is passed.*/
public void configClear(Cons<Tilec> cons){
configurations.put(void.class, (tile, value) -> cons.get((Tilec)tile));
public <E extends Tilec> void configClear(Cons<E> cons){
configurations.put(void.class, (tile, value) -> cons.get((E)tile));
}
/** Listen for a config by class type. */
public <T> void config(Class<T> type, Cons2<Tilec, T> config){
public <T, E extends Tilec> void config(Class<T> type, Cons2<E, T> config){
configurations.put(type, config);
}

View File

@@ -24,7 +24,7 @@ public class ItemSelection{
int i = 0;
for(T item : items){
if(!data.isUnlocked(item) && state.isCampaign()) continue;
if(!item.unlockedNow()) continue;
ImageButton button = cont.button(Tex.whiteui, Styles.clearToggleTransi, 24, () -> control.input.frag.config.hideConfig()).group(group).get();
button.changed(() -> consumer.get(button.isChecked() ? item : null));

View File

@@ -177,7 +177,8 @@ public class LaunchPad extends Block{
//actually launch the items upon removal
if(team() == Vars.state.rules.defaultTeam){
for(ItemStack stack : stacks){
Vars.data.addItem(stack.item, stack.amount);
//TODO where do the items go?
//Vars.data.addItem(stack.item, stack.amount);
Events.fire(new LaunchItemEvent(stack));
}
}

View File

@@ -1,25 +1,27 @@
package mindustry.world.blocks.campaign;
import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.style.*;
import arc.scene.ui.layout.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.blocks.storage.*;
import mindustry.world.consumers.*;
import static mindustry.Vars.*;
public class ResearchBlock extends StorageBlock{
public class ResearchBlock extends Block{
public float researchSpeed = 1f;
public @Load("@-top") TextureRegion topRegion;
@@ -31,7 +33,11 @@ public class ResearchBlock extends StorageBlock{
hasPower = true;
hasItems = true;
configurable = true;
itemCapacity = 0;
itemCapacity = 100;
//TODO requirements shrink as time goes on
consumes.add(new ConsumeItemDynamic((ResearchBlockEntity entity) -> entity.researching != null ? entity.researching.requirements : ItemStack.empty));
config(TechNode.class, ResearchBlockEntity::setTo);
}
@Override
@@ -40,33 +46,100 @@ public class ResearchBlock extends StorageBlock{
}
@Override
public boolean canPlaceOn(Tile tile, Team team){
if(tile == null) return false;
public void setBars(){
super.setBars();
//only allow placing next to cores
for(Point2 edge : Edges.getEdges(size)){
Tile other = tile.getNearby(edge);
if(other != null && other.block() instanceof CoreBlock && other.team() == team){
return true;
}
}
return false;
bars.add("progress", (ResearchBlockEntity e) -> new Bar("bar.progress", Pal.ammo, () -> e.researching == null ? 0f : e.researching.progress / e.researching.time));
}
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
boolean hasCore = canPlaceOn(world.tile(x, y), player.team());
if(!hasCore){
drawPlaceText(Core.bundle.get("bar.corereq"), x, y, valid);
}
}
public class ResearchBlockEntity extends TileEntity{
public @Nullable TechNode researching;
public class ResearchBlockEntity extends StorageBlockEntity{
public @Nullable UnlockableContent researching;
public double[] accumulator;
public double[] totalAccumulator;
@Override
public void updateTile(){
if(researching != null){
double totalTicks = researching.time * 60.0;
double amount = researchSpeed * edelta() / totalTicks;
double maxProgress = checkRequired(amount, false);
for(int i = 0; i < researching.requirements.length; i++){
int reqamount = Math.round(state.rules.buildCostMultiplier * researching.requirements[i].amount);
accumulator[i] += Math.min(reqamount * maxProgress, reqamount - totalAccumulator[i] + 0.00001); //add min amount progressed to the accumulator
totalAccumulator[i] = Math.min(totalAccumulator[i] + reqamount * maxProgress, reqamount);
}
maxProgress = checkRequired(maxProgress, true);
float increment = (float)(maxProgress * researching.time);
researching.progress += increment;
//check if it has been researched
if(researching.progress >= researching.time){
data.unlockContent(researching.content);
setTo(null);
}
}
}
private double checkRequired(double amount, boolean remove){
double maxProgress = amount;
for(int i = 0; i < researching.requirements.length; i++){
int ramount = researching.requirements[i].amount;
int required = (int)(accumulator[i]); //calculate items that are required now
if(!items.has(researching.requirements[i].item) && ramount > 0){
maxProgress = 0f;
}else if(required > 0){ //if this amount is positive...
//calculate how many items it can actually use
int maxUse = Math.min(required, items.get(researching.requirements[i].item));
//get this as a fraction
double fraction = maxUse / (double)required;
//move max progress down if this fraction is less than 1
maxProgress = Math.min(maxProgress, maxProgress * fraction);
accumulator[i] -= maxUse;
//remove stuff that is actually used
if(remove){
items.remove(researching.requirements[i].item, maxUse);
}
}
//else, no items are required yet, so just keep going
}
return maxProgress;
}
private void setTo(@Nullable TechNode value){
researching = value;
if(value != null){
accumulator = new double[researching.requirements.length];
totalAccumulator = new double[researching.requirements.length];
}
}
@Override
public void display(Table table){
super.display(table);
TextureRegionDrawable reg = new TextureRegionDrawable();
table.row();
table.table(t -> {
t.image().update(i -> {
i.setDrawable(researching == null ? Icon.cancel : reg.set(researching.content.icon(Cicon.medium)));
i.setScaling(Scaling.fit);
i.setColor(researching == null ? Color.lightGray : Color.white);
}).size(32).pad(3);
t.label(() -> researching == null ? "$none" : researching.content.localizedName).color(Color.lightGray);
}).left().padTop(4);
}
@Override
@@ -85,14 +158,25 @@ public class ResearchBlock extends StorageBlock{
@Override
public boolean acceptItem(Tilec source, Item item){
//research blocks can only transfer items to the core
return linkedCore != null && super.acceptItem(source, item);
return items.get(item) < getMaximumAccepted(item);
}
@Override
public int getMaximumAccepted(Item item){
if(researching == null) return 0;
for(int i = 0; i < researching.requirements.length; i++){
if(researching.requirements[i].item == item) return researching.requirements[i].amount;
}
return 0;
}
@Override
public boolean configTapped(){
//TODO select target
Vars.ui.tech.show();
//configure with tech node
ui.tech.show(node -> {
configure(node);
ui.tech.hide();
});
return false;
}
@@ -102,8 +186,8 @@ public class ResearchBlock extends StorageBlock{
super.write(write);
if(researching != null){
write.b(researching.getContentType().ordinal());
write.s(researching.id);
write.b(researching.content.getContentType().ordinal());
write.s(researching.content.id);
}else{
write.b(-1);
}
@@ -115,7 +199,7 @@ public class ResearchBlock extends StorageBlock{
byte type = read.b();
if(type != -1){
researching = Vars.content.getByID(ContentType.all[type], read.s());
setTo(TechTree.get(content.getByID(ContentType.all[type], read.s())));
}else{
researching = null;
}

View File

@@ -28,12 +28,11 @@ public class Door extends Wall{
solidifes = true;
consumesTap = true;
config(Boolean.class, (entity, open) -> {
DoorEntity door = (DoorEntity)entity;
door.open = open;
pathfinder.updateTile(door.tile());
(open ? closefx : openfx).at(door);
Sounds.door.at(door);
config(Boolean.class, (DoorEntity entity, Boolean open) -> {
entity.open = open;
pathfinder.updateTile(entity.tile());
(open ? closefx : openfx).at(entity);
Sounds.door.at(entity);
});
}

View File

@@ -42,7 +42,7 @@ public class ItemTurret extends Turret{
@Override
public void build(Tilec tile, Table table){
MultiReqImage image = new MultiReqImage();
content.items().each(i -> filter.get(i) && (!state.isCampaign() || data.isUnlocked(i)), item -> image.add(new ReqImage(new ItemImage(item.icon(Cicon.medium)),
content.items().each(i -> filter.get(i) && i.unlockedNow(), item -> image.add(new ReqImage(new ItemImage(item.icon(Cicon.medium)),
() -> tile != null && !((ItemTurretEntity)tile).ammo.isEmpty() && ((ItemEntry)((ItemTurretEntity)tile).ammo.peek()).item == item)));
table.add(image).size(8 * 4);

View File

@@ -44,9 +44,9 @@ public class ItemBridge extends Block{
canOverdrive = false;
//point2 config is relative
config(Point2.class, (tile, i) -> ((ItemBridgeEntity)tile).link = Point2.pack(i.x + tile.tileX(), i.y + tile.tileY()));
config(Point2.class, (ItemBridgeEntity tile, Point2 i) -> tile.link = Point2.pack(i.x + tile.tileX(), i.y + tile.tileY()));
//integer is not
config(Integer.class, (tile, i) -> ((ItemBridgeEntity)tile).link = i);
config(Integer.class, (ItemBridgeEntity tile, Integer i) -> tile.link = i);
}
@Override

View File

@@ -42,8 +42,8 @@ public class MassDriver extends Block{
hasPower = true;
outlineIcon = true;
//point2 is relative
config(Point2.class, (tile, point) -> ((MassDriverEntity)tile).link = Point2.pack(point.x + tile.tileX(), point.y + tile.tileY()));
config(Integer.class, (tile, point) -> ((MassDriverEntity)tile).link = point);
config(Point2.class, (MassDriverEntity tile, Point2 point) -> tile.link = Point2.pack(point.x + tile.tileX(), point.y + tile.tileY()));
config(Integer.class, (MassDriverEntity tile, Integer point) -> tile.link = point);
}
@Override

View File

@@ -28,8 +28,8 @@ public class Sorter extends Block{
unloadable = false;
saveConfig = true;
config(Item.class, (tile, item) -> ((SorterEntity)tile).sortItem = item);
configClear(tile -> ((SorterEntity)tile).sortItem = null);
config(Item.class, (SorterEntity tile, Item item) -> tile.sortItem = item);
configClear((SorterEntity tile) -> tile.sortItem = null);
}
@Override

View File

@@ -33,17 +33,9 @@ public class BlockForge extends PayloadAcceptor{
hasPower = true;
rotate = true;
config(Block.class, (tile, block) -> ((BlockForgeEntity)tile).recipe = block);
config(Block.class, (BlockForgeEntity tile, Block block) -> tile.recipe = block);
consumes.add(new ConsumeItemDynamic(e -> {
BlockForgeEntity entity = (BlockForgeEntity)e;
if(entity.recipe != null){
return entity.recipe.requirements;
}
return ItemStack.empty;
}));
consumes.add(new ConsumeItemDynamic((BlockForgeEntity e) -> e.recipe != null ? e.recipe.requirements : ItemStack.empty));
}
@Override

View File

@@ -43,11 +43,11 @@ public class ImpactReactor extends PowerGenerator{
public void setBars(){
super.setBars();
bars.add("poweroutput", entity -> new Bar(() ->
bars.add("poweroutput", (GeneratorEntity entity) -> new Bar(() ->
Core.bundle.format("bar.poweroutput",
Strings.fixed(Math.max(entity.getPowerProduction() - consumes.getPower().usage, 0) * 60 * entity.timeScale(), 1)),
() -> Pal.powerBar,
() -> ((GeneratorEntity)entity).productionEfficiency));
() -> entity.productionEfficiency));
}
@Override

View File

@@ -24,7 +24,7 @@ public class LightBlock extends Block{
configurable = true;
saveConfig = true;
config(Integer.class, (tile, value) -> ((LightEntity)tile).color = value);
config(Integer.class, (LightEntity tile, Integer value) -> tile.color = value);
}
public class LightEntity extends TileEntity{

View File

@@ -60,7 +60,7 @@ public class NuclearReactor extends PowerGenerator{
@Override
public void setBars(){
super.setBars();
bars.add("heat", entity -> new Bar("bar.heat", Pal.lightOrange, () -> ((NuclearReactorEntity)entity).heat));
bars.add("heat", (NuclearReactorEntity entity) -> new Bar("bar.heat", Pal.lightOrange, () -> entity.heat));
}
public class NuclearReactorEntity extends GeneratorEntity{

View File

@@ -32,11 +32,11 @@ public class PowerGenerator extends PowerDistributor{
super.setBars();
if(hasPower && outputsPower && !consumes.hasPower()){
bars.add("power", entity -> new Bar(() ->
bars.add("power", (GeneratorEntity entity) -> new Bar(() ->
Core.bundle.format("bar.poweroutput",
Strings.fixed(entity.getPowerProduction() * 60 * entity.timeScale(), 1)),
() -> Pal.powerBar,
() -> ((GeneratorEntity)entity).productionEfficiency));
() -> entity.productionEfficiency));
}
}

View File

@@ -31,11 +31,11 @@ public class Cultivator extends GenericCrafter{
@Override
public void setBars(){
super.setBars();
bars.add("multiplier", entity -> new Bar(() ->
bars.add("multiplier", (CultivatorEntity entity) -> new Bar(() ->
Core.bundle.formatFloat("bar.efficiency",
((((CultivatorEntity)entity).boost + 1f) * ((CultivatorEntity)entity).warmup) * 100f, 1),
((entity.boost + 1f) * entity.warmup) * 100f, 1),
() -> Pal.ammo,
() -> ((CultivatorEntity)entity).warmup));
() -> entity.warmup));
}
@Override

View File

@@ -86,11 +86,8 @@ public class Drill extends Block{
public void setBars(){
super.setBars();
bars.add("drillspeed", e -> {
DrillEntity entity = (DrillEntity)e;
return new Bar(() -> Core.bundle.format("bar.drillspeed", Strings.fixed(entity.lastDrillSpeed * 60 * entity.timeScale(), 2)), () -> Pal.ammo, () -> entity.warmup);
});
bars.add("drillspeed", (DrillEntity e) ->
new Bar(() -> Core.bundle.format("bar.drillspeed", Strings.fixed(e.lastDrillSpeed * 60 * e.timeScale(), 2)), () -> Pal.ammo, () -> e.warmup));
}
public Item getDrop(Tile tile){
@@ -285,7 +282,6 @@ public class Drill extends Block{
if(dominantItems > 0 && progress >= delay && items.total() < itemCapacity){
offload(dominantItem);
useContent(dominantItem);
index ++;
progress %= delay;

View File

@@ -116,14 +116,12 @@ public class GenericCrafter extends Block{
consume();
if(outputItem != null){
useContent(outputItem.item);
for(int i = 0; i < outputItem.amount; i++){
offload(outputItem.item);
}
}
if(outputLiquid != null){
useContent(outputLiquid.liquid);
handleLiquid(this, outputLiquid.liquid, outputLiquid.amount);
}

View File

@@ -45,7 +45,6 @@ public class LiquidConverter extends GenericCrafter{
if(cons.valid()){
float use = Math.min(cl.amount * edelta(), liquidCapacity - liquids.get(outputLiquid.liquid));
useContent(outputLiquid.liquid);
progress += use / cl.amount;
liquids.add(outputLiquid.liquid, use);
if(progress >= craftTime){

View File

@@ -13,8 +13,6 @@ import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class Pump extends LiquidBlock{
public final int timerContentCheck = timers++;
/** Pump amount, total. */
protected float pumpAmount = 1f;
@@ -115,10 +113,6 @@ public class Pump extends LiquidBlock{
liquids.add(liquidDrop, maxPump);
}
if(liquids.currentAmount() > 0f && timer(timerContentCheck, 10)){
useContent(liquids.current());
}
dumpLiquid(liquids.current());
}
}

View File

@@ -43,11 +43,11 @@ public class SolidPump extends Pump{
@Override
public void setBars(){
super.setBars();
bars.add("efficiency", entity -> new Bar(() ->
bars.add("efficiency", (SolidPumpEntity entity) -> new Bar(() ->
Core.bundle.formatFloat("bar.pumpspeed",
((SolidPumpEntity)entity).lastPump / Time.delta() * 60, 1),
entity.lastPump / Time.delta() * 60, 1),
() -> Pal.ammo,
() -> ((SolidPumpEntity)entity).warmup));
() -> entity.warmup));
}
@Override
@@ -124,7 +124,6 @@ public class SolidPump extends Pump{
liquids.add(result, maxPump);
lastPump = maxPump;
warmup = Mathf.lerpDelta(warmup, 1f, 0.02f);
if(timer(timerContentCheck, 10)) useContent(result);
if(Mathf.chance(delta() * updateEffectChance))
updateEffect.at(getX() + Mathf.range(size * 2f), getY() + Mathf.range(size * 2f));
}else{

View File

@@ -24,8 +24,8 @@ public class ItemSource extends Block{
configurable = true;
saveConfig = true;
config(Item.class, (tile, item) -> ((ItemSourceEntity)tile).outputItem = item);
configClear(tile -> ((ItemSourceEntity)tile).outputItem = null);
config(Item.class, (ItemSourceEntity tile, Item item) -> tile.outputItem = item);
configClear((ItemSourceEntity tile) -> tile.outputItem = null);
}
@Override

View File

@@ -25,8 +25,8 @@ public class LiquidSource extends Block{
outputsLiquid = true;
saveConfig = true;
config(Liquid.class, (tile, l) -> ((LiquidSourceEntity)tile).source = l);
configClear(tile -> ((LiquidSourceEntity)tile).source = null);
config(Liquid.class, (LiquidSourceEntity tile, Liquid l) -> tile.source = l);
configClear((LiquidSourceEntity tile) -> tile.source = null);
}
@Override

View File

@@ -69,11 +69,11 @@ public class CoreBlock extends StorageBlock{
public void setStats(){
super.setStats();
bars.add("capacity", e ->
bars.add("capacity", (CoreEntity e) ->
new Bar(
() -> Core.bundle.format("bar.capacity", ui.formatAmount(((CoreEntity)e).storageCapacity)),
() -> Core.bundle.format("bar.capacity", ui.formatAmount(e.storageCapacity)),
() -> Pal.items,
() -> e.items().total() / (float)(((CoreEntity)e).storageCapacity * content.items().count(i -> i.type == ItemType.material))
() -> e.items().total() / ((float)e.storageCapacity * content.items().count(i -> i.type == ItemType.material))
));
bars.add("units", e ->

View File

@@ -29,9 +29,7 @@ public class MessageBlock extends Block{
solid = true;
destructible = true;
config(String.class, (tile, text) -> {
MessageBlockEntity entity = (MessageBlockEntity)tile;
config(String.class, (MessageBlockEntity tile, String text) -> {
if(net.server() && text.length() > maxTextLength){
throw new ValidateException(player, "Player has gone above text limit.");
}
@@ -51,8 +49,8 @@ public class MessageBlock extends Block{
}
}
entity.message = result.toString();
entity.lines = entity.message.split("\n");
tile.message = result.toString();
tile.lines = tile.message.split("\n");
});
}

View File

@@ -27,12 +27,8 @@ public class Unloader extends Block{
saveConfig = true;
itemCapacity = 0;
config(Item.class, (tile, item) -> {
tile.items().clear();
((UnloaderEntity)tile).sortItem = item;
});
configClear(tile -> ((UnloaderEntity)tile).sortItem = null);
config(Item.class, (UnloaderEntity tile, Item item) -> tile.sortItem = item);
configClear((UnloaderEntity tile) -> tile.sortItem = null);
}
@Override

View File

@@ -41,7 +41,7 @@ public class Reconstructor extends UnitBlock{
@Override
public void setBars(){
super.setBars();
bars.add("progress", entity -> new Bar("bar.progress", Pal.ammo, ((ReconstructorEntity)entity)::fraction));
bars.add("progress", (ReconstructorEntity entity) -> new Bar("bar.progress", Pal.ammo, entity::fraction));
}
@Override

View File

@@ -37,20 +37,12 @@ public class UnitFactory extends UnitBlock{
outputsPayload = true;
rotate = true;
config(Integer.class, (tile, i) -> {
((UnitFactoryEntity)tile).currentPlan = i < 0 || i >= plans.length ? -1 : i;
((UnitFactoryEntity)tile).progress = 0;
config(Integer.class, (UnitFactoryEntity tile, Integer i) -> {
tile.currentPlan = i < 0 || i >= plans.length ? -1 : i;
tile.progress = 0;
});
consumes.add(new ConsumeItemDynamic(e -> {
UnitFactoryEntity entity = (UnitFactoryEntity)e;
if(entity.currentPlan != -1){
return plans[entity.currentPlan].requirements;
}
return ItemStack.empty;
}));
consumes.add(new ConsumeItemDynamic((UnitFactoryEntity e) -> e.currentPlan != -1 ? plans[e.currentPlan].requirements : ItemStack.empty));
}
@Override
@@ -69,7 +61,7 @@ public class UnitFactory extends UnitBlock{
@Override
public void setBars(){
super.setBars();
bars.add("progress", entity -> new Bar("bar.progress", Pal.ammo, ((UnitFactoryEntity)entity)::fraction));
bars.add("progress", (UnitFactoryEntity entity) -> new Bar("bar.progress", Pal.ammo, entity::fraction));
}
@Override

View File

@@ -1,6 +1,7 @@
package mindustry.world.consumers;
import arc.func.*;
import arc.math.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
@@ -12,8 +13,8 @@ import mindustry.world.meta.*;
public class ConsumeItemDynamic extends Consume{
public final @NonNull Func<Tilec, ItemStack[]> items;
public ConsumeItemDynamic(Func<Tilec, ItemStack[]> items){
this.items = items;
public <T extends Tilec> ConsumeItemDynamic(Func<T, ItemStack[]> items){
this.items = (Func<Tilec, ItemStack[]>)items;
}
@Override
@@ -43,7 +44,7 @@ public class ConsumeItemDynamic extends Consume{
private void rebuild(Tilec tile, Table table){
for(ItemStack stack : items.get(tile)){
table.add(new ReqImage(new ItemImage(stack.item.icon(Cicon.medium), stack.amount),
() -> tile.items() != null && tile.items().has(stack.item, stack.amount))).size(8 * 4).padRight(5);
() -> tile.items() != null && tile.items().has(stack.item, stack.amount))).size(8 * 4).padRight(6 * Mathf.digits(stack.amount));
}
}

View File

@@ -33,7 +33,7 @@ public class ConsumeItemFilter extends Consume{
@Override
public void build(Tilec tile, Table table){
MultiReqImage image = new MultiReqImage();
content.items().each(i -> filter.get(i) && (!state.isCampaign() || data.isUnlocked(i)), item -> image.add(new ReqImage(new ItemImage(item.icon(Cicon.medium), 1),
content.items().each(i -> filter.get(i) && i.unlockedNow(), item -> image.add(new ReqImage(new ItemImage(item.icon(Cicon.medium), 1),
() -> tile.items() != null && tile.items().has(item))));
table.add(image).size(8 * 4);

View File

@@ -8,8 +8,8 @@ import mindustry.ui.Bar;
public class BlockBars{
private OrderedMap<String, Func<Tilec, Bar>> bars = new OrderedMap<>();
public void add(String name, Func<Tilec, Bar> sup){
bars.put(name, sup);
public <T extends Tilec> void add(String name, Func<T, Bar> sup){
bars.put(name, (Func<Tilec, Bar>)sup);
}
public void remove(String name){