From d54b7dc396d6bcf757d4570ff63a5f19c1cda81d Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 24 Jun 2021 10:01:01 -0400 Subject: [PATCH] WIP payload deconstructor --- .../payload/payload-deconstructor-top.png | Bin 0 -> 1105 bytes .../blocks/payload/payload-deconstructor.png | Bin 0 -> 1263 bytes core/assets/icons/icons.properties | 1 + core/src/mindustry/content/Blocks.java | 60 +++--- core/src/mindustry/type/ItemSeq.java | 21 ++ core/src/mindustry/type/UnitType.java | 73 +++++-- .../world/blocks/payloads/BuildPayload.java | 11 ++ .../world/blocks/payloads/Payload.java | 7 + .../blocks/payloads/PayloadDeconstructor.java | 179 ++++++++++++++++++ .../world/blocks/payloads/UnitPayload.java | 11 ++ 10 files changed, 321 insertions(+), 42 deletions(-) create mode 100644 core/assets-raw/sprites/blocks/payload/payload-deconstructor-top.png create mode 100644 core/assets-raw/sprites/blocks/payload/payload-deconstructor.png create mode 100644 core/src/mindustry/world/blocks/payloads/PayloadDeconstructor.java diff --git a/core/assets-raw/sprites/blocks/payload/payload-deconstructor-top.png b/core/assets-raw/sprites/blocks/payload/payload-deconstructor-top.png new file mode 100644 index 0000000000000000000000000000000000000000..f334d2303233ab2dc9043f8fc0c7342c522d9b47 GIT binary patch literal 1105 zcmeAS@N?(olHy`uVBq!ia0y~yU|0ac9Lx+140{Bp%QG-AFct^7J29*~C-ahlfq^C6 z(btiIVPik{pF~y$1_qu0pAc6D28Mr|8}@{Nv{&h7vVhc;1o;L3|No!CL2Km~1_lPs z0*}aI1_r*vAk26?e?`T_V{^7?Pgujo9yyz-^HsqV5xyl!lJj@E zGXGr5EMLXS{BtGue5<2OKbH#2|88OWxlnrkZWpGXu?+kbYgj-1OS8#~=6Z5aGOs>y z#=~uysuP~4?f84d;1H+v1a*;^f(Op5;w5Pc{2DtR99qt$w<4)=j`hcn zg3nh4C>+nLsXMSyOXx%<1AmW2C@YgC&%;i}cCC7abVr*@kA0rZRxDDk8%8Y^;<9Hc1Jtw>_;{qZhqHFx$PClcKc$cLT%8A zZ8tZ+X?^=ZXTq-b)xWNB`7>n-=ZG)x-N37L!DUv0bjX5ElY?d}9E3BQaswLGF0tHR zz~sAxbDJaUEDzzF=6ETOnF+gDa+}sJXA4rhq31L^;dMsi6wYm@FR0y!@={1`dh4`> zbK5bM2`_`T*czI?ahTRE`2E186$e<4*lsY394-wXAaNzjE{CKJ_e zu2T~M0ZO?`LI}oDi%FGwqc)&%`QQ0qa;(-E1TXvb%PK^hhiux?Vk1{#8UeVaG zz=(Ofn9$+}P3QRTM0oTxajUB}x+S*XmhE#mE4cmq8K%drGJ7r>Kl-*ZGhxewosNP1 zx1T?2+~{USS)zmdVAT@16L1Oo?_|0eT}L5k4^)- zjZ))7C%bY^RyOXRH`K)>w4MYnPe@oWfq(C!#&=G3#oX-QR=g;fvPCmM^NB)s_=ANL yEOVnD1e}<$UH^lU=#0?DNHoIU=ELXD@|VQSabnn0I`)!^jd^|M< z?3uxJUptxJud|w`T6Kia>tO4YChq^t$=v%J7Pj6$aN>}9fT-O11;#s~ZX7As6%G)T zV^>gVQj6Q_;}BRO(=fq9pvcarf{FEEA%l~Wzb$z%{ewHA19++b8(2yL+6IiCOmVMgW39dP@9?5}Q znazZLXPI2Q&NVyn*yct4U$!21T$R6cLT);{p75u;G6!4_`5Aw<+98>7ugjeI?BfS% z3I+l1r85QO+3%Q#G24ml$j@K?+e=~oj&s+uuP}e=+}7tRI@{p)RQ6AuVe_LS{+wd` z>9YI0=9;?P#+pUPp9`7ozvd9_d((V*6z814iL(4`NoE^lywpYhF!vp6XR1wBl=f~o zD&M@3DdNAW2kX=DlvcW-x zv0(z^qS+ta5^66!i91sK+`jQ`=JI0YnYRDAPt4u1yKUyr&+^ynb}%h!H@Q}qc7I~L zt5$x-`j_c(hx+v{?2t{i%jWZR2z~MK<|YreET`$`C%H)ph(*;@NWDDp^t*ZJ{h$XA ze%>isP1D9R(>pW*qi^>`Pr1lJD$P(FFyWkv0HHdxzMhLxcTlc7=P9B zJzUSy7JuZU`J^9*eOel={;bpcchKq2;k7^5*L>vv`FOrb?SahnKf>QHIy883KA+aL z?4`q&i?P%1^UTfL*>tI=d#{=J>O;pXEsX3Z;L^n&oyknp5w8&k&had;M@~#W&mj z=aHL?XY5ngd~VL+y+C(%Ypi{zaAG3kmNetSsWU?gSPew$d5#Gs%ql2-D4X5zO>n7n z{eg==EFMhJUQ;=x{g1KMx(|QnT)$RTe%8jRoxkGa`c>Xf#CFvjSUuS_RcZT$m&|c? zXS0NNw&uxy`1MZGWp+#GVs>HskD{N{bZ>b7@QGS+UZNm!q8-aYHP&BK*yanrNLI0~ z{cQJq)2Zh_75skqa~=J!5&Y+TROlg1_Txb+HW@*q81CmPjj6FD%=A~9=u+?zg&CCf)@wgzZbfnJE3_I dv+!T$g++Jn-TRw-kb!}L!PC{xWt~$(69D@~MR@=K literal 0 HcmV?d00001 diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index f7ff25a374..64eeb8a54a 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -392,3 +392,4 @@ 63341=beryllic-boulder|block-beryllic-boulder-ui 63340=carbon-boulder|block-carbon-boulder-ui 63339=carbon-stone|block-carbon-stone-ui +63338=payload-deconstructor|block-payload-deconstructor-ui diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 64d085655c..e531d3d901 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -96,7 +96,7 @@ public class Blocks implements ContentList{ repairPoint, repairTurret, resupplyPoint, //payloads - payloadConveyor, payloadRouter, payloadPropulsionTower, + payloadConveyor, payloadRouter, payloadPropulsionTower, payloadDeconstructor, blockForge, blockLoader, blockUnloader, //logic message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, memoryCell, memoryBank, @@ -105,10 +105,7 @@ public class Blocks implements ContentList{ launchPad, payloadLaunchPad, interplanetaryAccelerator, //nuclear? - nuclearWarhead, warheadAssembler, ballisticSilo, //TODO - - //misc experimental - blockForge, blockLoader, blockUnloader + nuclearWarhead, warheadAssembler, ballisticSilo //TODO ; @Override @@ -2206,6 +2203,35 @@ public class Blocks implements ContentList{ consumes.power(6f); }}; + payloadDeconstructor = new PayloadDeconstructor("payload-deconstructor"){{ + requirements(Category.units, with(Items.graphite, 30, Items.silicon, 30, Items.copper, 30)); + itemCapacity = 200; + consumes.power(1f); + size = 5; + deconstructSpeed = 2f; + }}; + + blockForge = new BlockForge("block-forge"){{ + requirements(Category.units, BuildVisibility.debugOnly, with(Items.thorium, 100)); + hasPower = true; + consumes.power(2f); + size = 3; + }}; + + blockLoader = new BlockLoader("block-loader"){{ + requirements(Category.units, BuildVisibility.debugOnly, with(Items.thorium, 100)); + hasPower = true; + consumes.power(2f); + size = 3; + }}; + + blockUnloader = new BlockUnloader("block-unloader"){{ + requirements(Category.units, BuildVisibility.debugOnly, with(Items.thorium, 100)); + hasPower = true; + consumes.power(2f); + size = 3; + }}; + //endregion //region sandbox @@ -2390,30 +2416,6 @@ public class Blocks implements ContentList{ size = 6; }}; - //endregion - //region experimental - - blockForge = new BlockForge("block-forge"){{ - requirements(Category.units, BuildVisibility.debugOnly, with(Items.thorium, 100)); - hasPower = true; - consumes.power(2f); - size = 3; - }}; - - blockLoader = new BlockLoader("block-loader"){{ - requirements(Category.units, BuildVisibility.debugOnly, with(Items.thorium, 100)); - hasPower = true; - consumes.power(2f); - size = 3; - }}; - - blockUnloader = new BlockUnloader("block-unloader"){{ - requirements(Category.units, BuildVisibility.debugOnly, with(Items.thorium, 100)); - hasPower = true; - consumes.power(2f); - size = 3; - }}; - //endregion } } diff --git a/core/src/mindustry/type/ItemSeq.java b/core/src/mindustry/type/ItemSeq.java index a02f29bdb6..61bcc37e3a 100644 --- a/core/src/mindustry/type/ItemSeq.java +++ b/core/src/mindustry/type/ItemSeq.java @@ -55,6 +55,21 @@ public class ItemSeq implements Iterable, JsonSerializable{ return out; } + public ItemStack[] toArray(){ + int count = 0; + for(int value : values){ + if(value != 0) count++; + } + ItemStack[] result = new ItemStack[count]; + int index = 0; + for(int i = 0; i < values.length; i++){ + if(values[i] != 0){ + result[index ++] = new ItemStack(Vars.content.item(i), values[i]); + } + } + return result; + } + public void min(int number){ for(Item item : Vars.content.items()){ set(item, Math.min(get(item), number)); @@ -90,6 +105,12 @@ public class ItemSeq implements Iterable, JsonSerializable{ itemModule.each(this::add); } + public void add(ItemStack[] stacks){ + for(var s : stacks){ + add(s); + } + } + public void add(ItemSeq seq){ seq.each(this::add); } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index f1f0585ff1..e045f247cb 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -127,7 +127,9 @@ public class UnitType extends UnlockableContent{ softShadowRegion, jointRegion, footRegion, legBaseRegion, baseJointRegion, outlineRegion; public TextureRegion[] wreckRegions; + protected float maxBuildTime = -1f; protected @Nullable ItemStack[] cachedRequirements; + protected @Nullable ItemStack[] totalRequirements; public UnitType(String name){ super(name); @@ -460,25 +462,70 @@ public class UnitType extends UnlockableContent{ } } + /** @return the time required to build this unit, as a maximum value that takes into account reconstructors */ + public float getBuildTime(){ + getTotalRequirements(); + return maxBuildTime; + } + + /** @return all items needed to build this unit, including reconstructor steps. */ + public ItemStack[] getTotalRequirements(){ + if(totalRequirements == null){ + UnitType[] ret = {null}; + float[] timeret = {0f}; + ItemStack[] result = getRequirements(ret, timeret); + + //prevents stack overflow if requirements are circular and result != null + totalRequirements = ItemStack.empty; + + if(result != null){ + maxBuildTime = timeret[0]; + ItemSeq total = new ItemSeq(); + + total.add(result); + if(ret[0] != null){ + total.add(ret[0].getTotalRequirements()); + maxBuildTime = Math.max(ret[0].maxBuildTime, maxBuildTime); + } + totalRequirements = total.toArray(); + } + } + return totalRequirements; + } + + /** @return item requirements based on reconstructors or factories found; returns previous unit in array if provided */ + public @Nullable ItemStack[] getRequirements(@Nullable UnitType[] prevReturn, @Nullable float[] timeReturn){ + var rec = (Reconstructor)content.blocks().find(b -> b instanceof Reconstructor re && re.upgrades.contains(u -> u[1] == this)); + + if(rec != null && rec.consumes.has(ConsumeType.item) && rec.consumes.get(ConsumeType.item) instanceof ConsumeItems ci){ + if(prevReturn != null){ + prevReturn[0] = rec.upgrades.find(u -> u[1] == this)[0]; + } + if(timeReturn != null){ + timeReturn[0] = rec.constructTime; + } + return ci.items; + }else{ + var factory = (UnitFactory)content.blocks().find(u -> u instanceof UnitFactory uf && uf.plans.contains(p -> p.unit == this)); + if(factory != null){ + + var plan = factory.plans.find(p -> p.unit == this); + if(timeReturn != null){ + timeReturn[0] = plan.time; + } + return plan.requirements; + } + } + return null; + } + @Override public ItemStack[] researchRequirements(){ if(cachedRequirements != null){ return cachedRequirements; } - ItemStack[] stacks = null; - - //calculate costs based on reconstructors or factories found - Block rec = content.blocks().find(b -> b instanceof Reconstructor re && re.upgrades.contains(u -> u[1] == this)); - - if(rec != null && rec.consumes.has(ConsumeType.item) && rec.consumes.get(ConsumeType.item) instanceof ConsumeItems ci){ - stacks = ci.items; - }else{ - UnitFactory factory = (UnitFactory)content.blocks().find(u -> u instanceof UnitFactory uf && uf.plans.contains(p -> p.unit == this)); - if(factory != null){ - stacks = factory.plans.find(p -> p.unit == this).requirements; - } - } + ItemStack[] stacks = getRequirements(null, null); if(stacks != null){ ItemStack[] out = new ItemStack[stacks.length]; diff --git a/core/src/mindustry/world/blocks/payloads/BuildPayload.java b/core/src/mindustry/world/blocks/payloads/BuildPayload.java index d9e6c08fa0..78cd768711 100644 --- a/core/src/mindustry/world/blocks/payloads/BuildPayload.java +++ b/core/src/mindustry/world/blocks/payloads/BuildPayload.java @@ -5,6 +5,7 @@ import arc.util.io.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.type.*; import mindustry.world.*; import static mindustry.Vars.*; @@ -33,6 +34,16 @@ public class BuildPayload implements Payload{ build.dropped(); } + @Override + public ItemStack[] requirements(){ + return build.block.requirements; + } + + @Override + public float buildTime(){ + return build.block.buildCost; + } + @Override public float x(){ return build.x; diff --git a/core/src/mindustry/world/blocks/payloads/Payload.java b/core/src/mindustry/world/blocks/payloads/Payload.java index d182f08a37..f68253b866 100644 --- a/core/src/mindustry/world/blocks/payloads/Payload.java +++ b/core/src/mindustry/world/blocks/payloads/Payload.java @@ -6,6 +6,7 @@ import arc.util.*; import arc.util.io.*; import mindustry.game.*; import mindustry.gen.*; +import mindustry.type.*; import mindustry.world.*; import static mindustry.Vars.*; @@ -25,6 +26,12 @@ public interface Payload extends Position{ float x(); float y(); + /** @return the items needed to make this payload; may be empty. */ + ItemStack[] requirements(); + + /** @return the time taken to build this payload. */ + float buildTime(); + /** @return whether this payload was dumped. */ default boolean dump(){ return false; diff --git a/core/src/mindustry/world/blocks/payloads/PayloadDeconstructor.java b/core/src/mindustry/world/blocks/payloads/PayloadDeconstructor.java new file mode 100644 index 0000000000..5619506f37 --- /dev/null +++ b/core/src/mindustry/world/blocks/payloads/PayloadDeconstructor.java @@ -0,0 +1,179 @@ +package mindustry.world.blocks.payloads; + +import arc.graphics.g2d.*; +import arc.math.*; +import arc.util.*; +import arc.util.io.*; +import mindustry.*; +import mindustry.gen.*; +import mindustry.graphics.*; + +public class PayloadDeconstructor extends PayloadBlock{ + public float deconstructSpeed = 2f; + + public PayloadDeconstructor(String name){ + super(name); + + outputsPayload = false; + acceptsPayload = true; + update = true; + rotate = false; + size = 5; + payloadSpeed = 1f; + //make sure to display large units. + clipSize = 120; + hasItems = true; + hasPower = true; + itemCapacity = 100; + } + + @Override + public TextureRegion[] icons(){ + return new TextureRegion[]{region, topRegion}; + } + + public class PayloadDeconstructorBuild extends PayloadBlockBuild{ + public @Nullable Payload deconstructing; + public @Nullable float[] accum; + public float progress; + public float time, speedScl; + + @Override + public void draw(){ + Draw.rect(region, x, y); + + //draw input + for(int i = 0; i < 4; i++){ + if(blends(i)){ + Draw.rect(inRegion, x, y, (i * 90) - 180); + } + } + + Draw.z(Layer.blockOver); + drawPayload(); + if(deconstructing != null){ + deconstructing.set(x + payVector.x, y + payVector.y, payRotation); + + Draw.z(Layer.blockOver); + //deconstructing.draw(); + + //TODO shadow + Draw.draw(Layer.blockOver, () -> { + Drawf.construct(x, y, deconstructing.icon(), Pal.remove, 0f, 1f - progress, speedScl, time); + Draw.color(Pal.remove); + Draw.alpha(speedScl); + + Lines.lineAngleCenter(x + Mathf.sin(time, 20f, Vars.tilesize / 2f * block.size - 2f), y, 90, block.size * Vars.tilesize - 4f); + + Draw.reset(); + }); + } + + Draw.rect(topRegion, x, y); + } + + @Override + public void handlePayload(Building source, Payload payload){ + super.handlePayload(source, payload); + accum = null; + } + + @Override + public boolean acceptPayload(Building source, Payload payload){ + return deconstructing == null && super.acceptPayload(source, payload) && payload.requirements().length > 0; + } + + @Override + public void updateTile(){ + //always dump items + dumpAccumulate(); + + if(deconstructing != null){ + var reqs = deconstructing.requirements(); + if(accum == null || reqs.length != accum.length){ + accum = new float[reqs.length]; + } + + //check if there is enough space to get the items for deconstruction + boolean canProgress = items.total() <= itemCapacity; + if(canProgress){ + for(var ac : accum){ + if(ac >= 1f){ + canProgress = false; + break; + } + } + } + + //move progress forward if possible + if(canProgress){ + float shift = edelta() * deconstructSpeed / deconstructing.buildTime(); + float realShift = Math.min(shift, 1f - progress); + + progress += shift; + time += edelta(); + + for(int i = 0; i < reqs.length; i++){ + accum[i] += reqs[i].amount * realShift; + } + } + + speedScl = Mathf.lerpDelta(speedScl, canProgress ? 1f : 0f, 0.1f); + + //transfer items from accumulation buffer into block inventory when they reach integers + for(int i = 0; i < reqs.length; i++){ + int taken = Math.min((int)accum[i], itemCapacity - items.total()); + if(taken > 0){ + items.add(reqs[i].item, taken); + accum[i] -= taken; + } + } + + //finish deconstruction, prepare for next payload. + if(progress >= 1f){ + //TODO + deconstructing = null; + accum = null; + } + }else if(moveInPayload(false) && payload != null && cons.valid()){ + accum = new float[payload.requirements().length]; + deconstructing = payload; + payload = null; + progress = 0f; + } + } + + @Override + public void write(Writes write){ + super.write(write); + + write.f(progress); + if(accum != null){ + write.s(accum.length); + for(float v : accum){ + write.f(v); + } + }else{ + write.s(0); + } + Payload.write(deconstructing, write); + } + + @Override + public void read(Reads read, byte revision){ + super.read(read, revision); + + progress = read.f(); + short accums = read.s(); + if(accums > 0){ + accum = new float[accums]; + for(int i = 0; i < accums; i++){ + accum[i] = read.f(); + } + } + deconstructing = Payload.read(read); + } + + + } +} diff --git a/core/src/mindustry/world/blocks/payloads/UnitPayload.java b/core/src/mindustry/world/blocks/payloads/UnitPayload.java index 22d11f4b65..f951cb3651 100644 --- a/core/src/mindustry/world/blocks/payloads/UnitPayload.java +++ b/core/src/mindustry/world/blocks/payloads/UnitPayload.java @@ -12,6 +12,7 @@ import mindustry.entities.EntityCollisions.*; import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.gen.*; +import mindustry.type.*; public class UnitPayload implements Payload{ public static final float deactiveDuration = 40f; @@ -23,6 +24,16 @@ public class UnitPayload implements Payload{ this.unit = unit; } + @Override + public ItemStack[] requirements(){ + return unit.type.getTotalRequirements(); + } + + @Override + public float buildTime(){ + return unit.type.getBuildTime(); + } + @Override public void write(Writes write){ write.b(payloadUnit);