From 6483d924af310ac1e88903a4dbd17d7e487cb188 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 29 Nov 2021 20:06:49 -0500 Subject: [PATCH] Unit cargo transport system --- .../src/main/resources/classids.properties | 1 + .../main/resources/revisions/manifold/0.json | 1 + .../sprites/blocks/units/rally-point.png | Bin 710 -> 0 bytes .../sprites/blocks/units/resupply-point.png | Bin 1161 -> 0 bytes .../blocks/units/unit-cargo-loader.png | Bin 0 -> 2068 bytes .../units/unit-cargo-unload-point-top.png | Bin 0 -> 193 bytes .../blocks/units/unit-cargo-unload-point.png | Bin 0 -> 798 bytes .../sprites/units/manifold-cell.png | Bin 0 -> 434 bytes core/assets-raw/sprites/units/manifold.png | Bin 0 -> 1232 bytes core/assets/icons/icons.properties | 3 + core/assets/logicids.dat | Bin 3567 -> 3621 bytes core/src/mindustry/ai/BlockIndexer.java | 82 ++------ core/src/mindustry/ai/Pathfinder.java | 4 +- core/src/mindustry/ai/types/CargoAI.java | 179 ++++++++++++++++++ core/src/mindustry/content/Blocks.java | 22 ++- core/src/mindustry/content/UnitTypes.java | 31 ++- core/src/mindustry/core/FileTree.java | 43 +++-- .../mindustry/entities/comp/BuildingComp.java | 10 +- .../entities/comp/BuildingTetherComp.java | 3 +- .../entities/units/AIController.java | 3 +- core/src/mindustry/game/SectorInfo.java | 4 +- core/src/mindustry/logic/LExecutor.java | 3 +- core/src/mindustry/mod/Scripts.java | 10 +- .../ui/fragments/BlockInventoryFragment.java | 56 ++++-- .../mindustry/ui/fragments/HintsFragment.java | 2 +- .../blocks/distribution/StackConveyor.java | 15 ++ .../world/blocks/logic/LogicBlock.java | 6 +- .../world/blocks/production/Pump.java | 6 + .../world/blocks/storage/CoreBlock.java | 3 + .../world/blocks/units/UnitCargoLoader.java | 146 ++++++++++++++ .../blocks/units/UnitCargoUnloadPoint.java | 109 +++++++++++ core/src/mindustry/world/meta/BlockFlag.java | 4 +- 32 files changed, 631 insertions(+), 115 deletions(-) create mode 100644 annotations/src/main/resources/revisions/manifold/0.json delete mode 100644 core/assets-raw/sprites/blocks/units/rally-point.png delete mode 100644 core/assets-raw/sprites/blocks/units/resupply-point.png create mode 100644 core/assets-raw/sprites/blocks/units/unit-cargo-loader.png create mode 100644 core/assets-raw/sprites/blocks/units/unit-cargo-unload-point-top.png create mode 100644 core/assets-raw/sprites/blocks/units/unit-cargo-unload-point.png create mode 100644 core/assets-raw/sprites/units/manifold-cell.png create mode 100644 core/assets-raw/sprites/units/manifold.png create mode 100644 core/src/mindustry/ai/types/CargoAI.java create mode 100644 core/src/mindustry/world/blocks/units/UnitCargoLoader.java create mode 100644 core/src/mindustry/world/blocks/units/UnitCargoUnloadPoint.java diff --git a/annotations/src/main/resources/classids.properties b/annotations/src/main/resources/classids.properties index 714849785a..54729b159d 100644 --- a/annotations/src/main/resources/classids.properties +++ b/annotations/src/main/resources/classids.properties @@ -9,6 +9,7 @@ corvus=24 flare=3 gamma=31 mace=4 +manifold=36 mega=5 mindustry.entities.comp.BuildingComp=6 mindustry.entities.comp.BulletComp=7 diff --git a/annotations/src/main/resources/revisions/manifold/0.json b/annotations/src/main/resources/revisions/manifold/0.json new file mode 100644 index 0000000000..13a7219ebf --- /dev/null +++ b/annotations/src/main/resources/revisions/manifold/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/core/assets-raw/sprites/blocks/units/rally-point.png b/core/assets-raw/sprites/blocks/units/rally-point.png deleted file mode 100644 index 493d90df1d3b59c494a0cfb5f6d456144783ce7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 710 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hE~)oo$$R*nr2i z^z@xYF9fZ=akxH{Sx~(ETZ*O)OMz(TpGW^o9-U&`!XkF`|DzjAJfgG9C7g)1t?wM_~_3XHtF2(Jt*m_yAe2UMX<3E%7F26i3bMtd1hyMEfSm%3> z)^LQ`@Foh@OK(*y`M5%2>JNjKtv{rgc4eG)J2t^NKxf<33dc;b@|&jm{F-)pq|a@TlWvD=ihw83}5wx)pljYqw#+{Dv_ zSLiBSso!nQaC-WQ|M#kSR~^w-SYzzRae&vU<(r4jM$wkiUA)XMY^Tbr%C%njv~syW zpF*?^Q=&ve=|K*Lr@3ctNHTs<7jj{|(l|NDhCA`2BTdgC?0#TK%tO**@E{qk?*Jd(Aafb3cXvqH4XE^km zk-dTWz!8RJbsT~PV^kjo)pIq zS{F7Q;s};H-SptA!U?ZrpF$IErljB?nbcE{HCpZk?-Y&d|H69lY2mV+3QPxIbGRO@ zcD?mQY$gYbzYP=P%Mj1}NnH*ON23KM_)R^#>xi2|PuKg@Ig*wxCw+3ZNnLU~x!2Jn zbLw60mgTP`?ys$QIz diff --git a/core/assets-raw/sprites/blocks/units/resupply-point.png b/core/assets-raw/sprites/blocks/units/resupply-point.png deleted file mode 100644 index d6d9d52572356c942d652f248b4601d901f18197..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1161 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hEk44ofy`glX=O&z~b!b z;uumf=WVoY)@(-sSxv9Rq#jv;hy{ljmkVk#?QFQa!!vQR^9~ccrapm7N833HXS?lm z*rmX+B0zD2gFt(Nlhtwq<*?N=Z_d0Md9rDbSH}Nmd;iY9nZKQptLERQuk*aE-|`&U zblv>;EVYO!SL(}(u6SIXe@0=V`xT*si|WWx{0VW z{ao%+XEm>bckk;u@yqtUaiO~V>o25U))1WaQC@ud-|D`Uu06*bIukT28E(3*N){K{ zx#P`w<1bb1+*w&C+BZ!4s37lTE%eOZ=@W;Dqj#N!%*>uMuItNJoLVlpkkMiCcC&3- zpE&ddrDl7`Jybk&NhxD(P3#lhT<_o+A_>ALqH`a(WE`!pn9yH7VM_90#gd9;k2!BY zE3gQARU4{wwhWZPsy$eq$>nIgeW_KhADX54` zD^GOKaPfHhQEXx5KJ(zzB_|ddN`2z!Tu?TpqO;(Zq>WC~^J?QAyK=jErzylW+V{m9 zS&D!5e47;Y?obE!=Nhg8Wk(~X@MjMTj^r6%lQr`_dsPf$jy0@@8VvF-~#O z?V&9d-f>YZ!GV%et}CUx9yr?N_&a&AupI5Z!>S^v($3N#$0?^F+W7cLUUN+26|U7^ zRf-n~Ha1;ZxNcs8?pDX~_xYNzWNSa_MXC(-M4Sl_VJSBw6~Vuwq_*{Bn*}qtg;sd_}pZ z<#jHdU@fa7r(T}eyt^~#kxXi3)3u2c9_JlT)wy1vqp~pG9oCJNiWDpNn$&V0LS>U1o^7ynnXKii^o{U%j3%iZC&pP`aY@KCr&ahT(ucW7oQW Us-3zopr0Q;c+(f|Me diff --git a/core/assets-raw/sprites/blocks/units/unit-cargo-loader.png b/core/assets-raw/sprites/blocks/units/unit-cargo-loader.png new file mode 100644 index 0000000000000000000000000000000000000000..69024f4877c63f0ffb652060a6ac7fd6165ae4ad GIT binary patch literal 2068 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^RW7>k44ofy`glX=O&z<$ot z#WAE}&f8eq?ih2Cy~Yxc)LgrGH6Ok6H8x0P`pq76BO&?DF7686wh3CH2jV=YYI$y* zR~5baO;hX>eu+s%XH1Oe&Q1{5yQFe;i9z|AO87bhf^yZgU?B~yD(J5z1J zk(-}tme+oJQgPVJJaUfTmiGl4qixe}{`&IN=Wky-|GU*WbF<`DIo7?aHmUT;IbGS$ zy)kR+iSq2l+sbxs%et@IdL=CIk>Lq<4{otdTU`Zja2{8gKRf>1=39S~9&{a$V%GVU z#8<@mWWsFcRV*>*JQ(>}YdmL7bMfH%aQD0astM;Cm%KhP^K3-3km4=>yL(FW*%>aJ z6*HJ$zG(Wo>$mUSx3Bxm|MQIh{`mKQ-|h?cUJ-qHZSEu`Gc}fIhDrPk8}6=QJGNZG zGg?{x^q!(mom*QP8T5W~tmBxrB=YI?Nxq7LRZJ}>+&!Y3M2qfOw(S+E5nT6BU-OgT z?Xz4Ak7^&*-22Ma`|^IxH`dG>WuN!Wf1lQUFg-v1zqg(Iv9AZ~i*ip%vJ0f2{w&+a z8O$K}Q@+P*JBx_`w^r-7os%kqMg5s`IDuAO6<^ocufxb-Hz8!x`#j!h zN-K6Nh<~mWpR-q??#JWxPd;CHt~w!ma%grs_l2`!j*~X3Ww=T-*SxwLGuugN&GqPu zznZVI?mSYq`RKmgA7?9?vc~SS=6X^)rDmDP2A|XA3;lmiJ@mFO=F#=ny4kn9wlxH- zeV5jqAuo~5x$@en!SnUMyXE$Oa5t}DEL`>fu+!By zzqgjPxO}K|3aI&M^L5vPh-ET60=4yi{(EouW1=RX#d(i61+1GMaUKv3aaz7-OZzCd~BJ)bYsGK$5jnsN*O1o-$-N>Nxa4MbCQW>Q}M~^OEw-j`El7f z9gm;|Z_`})&df4g*di-tP^{hu`Co^C6YL+G0u1z!v{5Oe)&7| zeHa11r)PJei5pd|(6~WaD7*u1g ztYqqY+ocidctiW4)W^n+E)v}}E*>pvcQ2~CNLc@ncb?3|a6{;t)=vgr?hhLmL^4L% zCrg_q zRnu$Er<441FI+knty*2Rqq=X2S<9z~3RMowj-3gvbuYdP)G)1B>~d#|ko6jugx!ma zlbJ59?Yy(s`cCcKTej|EN=F2^Pq=((27_O{a*$Gt((?F{gIESTAO>ZHdc{+UI#+B)QfY9@uMnMAW6K_5J$$e8+!B-OqTU zT-x;a<>@cRdhB^W?;7?;FT4>Opi^`tm($Sw?u`2tOE#X&eR(rdsDYSz&=Wdrd;7cmK4&QEh<*teLdd7O~=JbPx%k`wF+I!CfP6GD|OGqYkN&OPkgDr6@j$k$uLki_$0 zqOqC>mz+{i6sN`Y^`5y8oRU`WNb6T@+QzzT+cpoo1}BDwT}n^N>do`-D)TG;dVj?! tdG#sjSk44ofy`glX=O&z)jAK^QXk(l4X}R ztbQ7t%9-j|KHWc5bxMm(tX=371KS60KV_D2{yua1zXRi4=8|BC?+kZEF4#Jl^Dn%~ rnE7&fLDVnfDf_<3f}Fy@aIKKx{R9<>s}lC1APYQQ{an^LB{Ts5CYMLx literal 0 HcmV?d00001 diff --git a/core/assets-raw/sprites/blocks/units/unit-cargo-unload-point.png b/core/assets-raw/sprites/blocks/units/unit-cargo-unload-point.png new file mode 100644 index 0000000000000000000000000000000000000000..29d5c9a88334b91bb96b2e991502c15dc94cf875 GIT binary patch literal 798 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hE^q zzqDy!*=L>3L{8RyZnIBlS#ck1;`g5LVJ*v+41IUE3#tZPYgzrLZ*Z?z)w1`{W|bBH ze^)o8dqi*~9B^-wO1W`zf`hn$@lD0a_tvzuHuNu#4i~Z%SNIUfxj4pH=+?YC77tFj z>L4bge&h5%D_XWqpWL8z@V8dh-LySIHanGsZpocvneySlb+rf~8_PvPw`OrOzTML+ zCLb%kckf135i<{msSk8-SsPdPr+co5+0Y<#F-fOl8p}x&U4~4HWUo0BH|%OCx*H~B zCa$plu2WOA!_EWB3+BqKeHO*p!|L6v?;ywc^h~Ds2aXq>5`V+AB$6*)dKIb>;$U08 zji-`ji_jW-UlF;f7n&d&Y~IUTx=&C|cGXudFC zuGM(D;`){KLYyY49~e4QB4#`5G=x4;C| zhe|9tiVL4@R>`PsVR7l+sJi9nmVIuvCm!m(UAvKoQ(1w3kIZ{t#%pUewH({F`BxNO z*%$Cb%9-(&;xZOqJFSDv@s_)o)->~QitTZ4+GD-o!=#5k3{@3wOkDjQ8Lz!K=l%14 z&r`|bqEhkDU1-a$dA81OKZ281v}o)pf4qbpTj9sM zThr^(A6$Bn{a(!`xasRu@trw_=XxKczE`txKBU9q-E?g?r@eCH)jv7&0^bYRC@EUK z%$<6k_x;2P7j>-jIpiizU_F1vp4EQR1m5#2nm5!kr?{sIfBIgdz`(%3;OXk;vd$@? F2>^>XSM&e? literal 0 HcmV?d00001 diff --git a/core/assets-raw/sprites/units/manifold-cell.png b/core/assets-raw/sprites/units/manifold-cell.png new file mode 100644 index 0000000000000000000000000000000000000000..d921e81cb14cce210d486747e2be525b7ae3a766 GIT binary patch literal 434 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^RW7>k44ofy`glX=O&z?kFd z;uumf=k3kFq9zA{mWTIZRCl)A+y6kQO6=35l}6G58V6^Z{r{dNYJIhL%^D#F?4aV% zm%~qYozJ?-eCHK&*4np5_XDrrb)M-s?cKTb!&a|$RsTF8=McsB^6s@clj9uH1Vp_& z{z}EQB~Ds${&MEG&x^k`6v(=JUb_Bs&NSy+hXm_hc;EOfd3UFxZftdD_fx&Wv+L`fV@q3t^ppMzvZ`#>OgJs* zwDoaYbq?2rYpkzD_N9b1l-MV1|Dbx9Y01w8vWKVpa8IaXe#ftGiny zO>DFdYSx_Gz>v9zc=CuZnY0Z4Xf4~=D8Esuf2ERjxk8W)78&q Iol`;+07G%LssI20 literal 0 HcmV?d00001 diff --git a/core/assets-raw/sprites/units/manifold.png b/core/assets-raw/sprites/units/manifold.png new file mode 100644 index 0000000000000000000000000000000000000000..d9276a4c9f13ed7ea68558608ab3bf80fdabd13b GIT binary patch literal 1232 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^RW7>k44ofy`glX=O&z|!UE z;uumf=k1*Hej<(n$GGnwa1q{fnDwcb(;;DxMS<(@`CJe#Qj+_gVWFt5`tez4kJ=9w zHI*1az1`&=Djx+uBxL1Qt^RfXOUYNgy8SIJbLXy|^>5$Ic`x7HQe|R50}WYcw%Rqs z+)Udh|`#E;-TZD@*Lrcx(Gz z->Uw9zuQ^sc34I$wHNTzMe!aeKdh)y?#`e>7gUY*_Hi*CJKw;Cr)o_IsUOT$Zy>f1Im% zXxE!HXV@PLo-{9;KkKjk#EZ!pCs=%Io_%>>6kU^+o*LKlT*zqQwjVBS%!X%Kt~12z zC@uPGZhy4mKEHju^|$ud=a2ko&zJYfDfYdg#>_h&-csV?4lnxlG2K+yqTE|}mc#?| z8|DYa*K~9KOaC*Y?p&DK4Cw`?Q&?X+`nn(uR|&4oM+3WE!p`i zM!4umdXn0dbIwZ>%wA5}9B}A}&x7YbZY*A=Vt4uYmvYW!n-}imjM*s=rF6yT1AE{s zPQ{Ovxi{j!Y!{lp;-f>)M{UtNZ&zN;iJBU#@y*0Qluf{w>(Rbxt8S`n(b^sG=9;WJr}%v-2G(c*^3`il-7wwueRLus?BFz!`Zb5r>f1x0AL0 z9ePx>T%WCM;V#BJ=OAB>z^FOWr(en~Zsks)v4{20~ge`SY_?eg>MJWn)8N6(8>+V`zP zVs?2N)0eY5&VNa53Z1*+Wz2bP0l+XkKa!yI> literal 0 HcmV?d00001 diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index 49c3f940ec..13172138a3 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -462,3 +462,6 @@ 63244=reinforced-vault|block-reinforced-vault-ui 63243=nitrogen|liquid-nitrogen-ui 63242=atmospheric-concentrator|block-atmospheric-concentrator-ui +63241=unit-cargo-loader|block-unit-cargo-loader-ui +63240=unit-cargo-unload-point|block-unit-cargo-unload-point-ui +63239=manifold|unit-manifold-ui diff --git a/core/assets/logicids.dat b/core/assets/logicids.dat index 01ea5f0764315faef96438369b918e6a5a040c3f..de04a9e6fdf566cfd30cf56835a36d5d0f8b4259 100644 GIT binary patch delta 77 zcmaDay;O#Y;n+r|T5esz(!9(P-Q>ig^nBf%{KS;hA_j3Jq0&5%pl(5aW?l({*5(Fo VGe%C1+{C=hwEUcu&0Be_7y%RI8fpLl delta 23 fcmZ1~^In>X;mAg&T5d+o&1<;L7&pJ+v19}QUp)tX diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index c3d6670ffb..19c3d0f206 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -15,8 +15,6 @@ import mindustry.type.*; import mindustry.world.*; import mindustry.world.meta.*; -import java.util.*; - import static mindustry.Vars.*; /** Class used for indexing special target blocks for AI. */ @@ -37,7 +35,7 @@ public class BlockIndexer{ /** Stores teams that are present here as tiles. */ private Seq activeTeams = new Seq<>(Team.class); /** Maps teams to a map of flagged tiles by flag. */ - private TileArray[][] flagMap = new TileArray[Team.all.length][BlockFlag.all.length]; + private Seq[][] flagMap = new Seq[Team.all.length][BlockFlag.all.length]; /** Counts whether a certain floor is present in the world upon load. */ private boolean[] blocksPresent; @@ -61,7 +59,7 @@ public class BlockIndexer{ Events.on(WorldLoadEvent.class, event -> { damagedTiles = new Seq[Team.all.length]; - flagMap = new TileArray[Team.all.length][BlockFlag.all.length]; + flagMap = new Seq[Team.all.length][BlockFlag.all.length]; activeTeams = new Seq<>(Team.class); clearFlags(); @@ -105,9 +103,9 @@ public class BlockIndexer{ var flags = tile.block().flags; var data = team.data(); - if(flags.size() > 0){ - for(BlockFlag flag : flags){ - getFlagged(team)[flag.ordinal()].remove(tile); + if(flags.size > 0){ + for(BlockFlag flag : flags.array){ + getFlagged(team)[flag.ordinal()].remove(build); } } @@ -166,12 +164,12 @@ public class BlockIndexer{ private void clearFlags(){ for(int i = 0; i < flagMap.length; i++){ for(int j = 0; j < BlockFlag.all.length; j++){ - flagMap[i][j] = new TileArray(); + flagMap[i][j] = new Seq(); } } } - private TileArray[] getFlagged(Team team){ + private Seq[] getFlagged(Team team){ return flagMap[team.id]; } @@ -190,13 +188,13 @@ public class BlockIndexer{ } /** Get all allied blocks with a flag. */ - public TileArray getAllied(Team team, BlockFlag type){ + public Seq getFlagged(Team team, BlockFlag type){ return flagMap[team.id][type.ordinal()]; } @Nullable - public Tile findClosestFlag(float x, float y, Team team, BlockFlag flag){ - return Geometry.findClosest(x, y, getAllied(team, flag)); + public Building findClosestFlag(float x, float y, Team team, BlockFlag flag){ + return Geometry.findClosest(x, y, getFlagged(team, flag)); } public boolean eachBlock(Teamc team, float range, Boolf pred, Cons cons){ @@ -239,34 +237,30 @@ public class BlockIndexer{ } /** Get all enemy blocks with a flag. */ - public Seq getEnemy(Team team, BlockFlag type){ - returnArray.clear(); + public Seq getEnemy(Team team, BlockFlag type){ + breturnArray.clear(); Seq data = state.teams.present; //when team data is not initialized, scan through every team. this is terrible if(data.isEmpty()){ for(Team enemy : Team.all){ if(enemy == team) continue; - TileArray set = getFlagged(enemy)[type.ordinal()]; + var set = getFlagged(enemy)[type.ordinal()]; if(set != null){ - for(Tile tile : set){ - returnArray.add(tile); - } + breturnArray.addAll(set); } } }else{ for(int i = 0; i < data.size; i++){ Team enemy = data.items[i].team; if(enemy == team) continue; - TileArray set = getFlagged(enemy)[type.ordinal()]; + var set = getFlagged(enemy)[type.ordinal()]; if(set != null){ - for(Tile tile : set){ - returnArray.add(tile); - } + breturnArray.addAll(set); } } } - return returnArray; + return breturnArray; } public void notifyBuildHealed(Building build){ @@ -400,16 +394,12 @@ public class BlockIndexer{ //only process entity changes with centered tiles if(tile.isCenter() && tile.build != null){ var data = team.data(); - if(tile.block().flags.size() > 0 && tile.isCenter()){ - TileArray[] map = getFlagged(team); - for(BlockFlag flag : tile.block().flags){ + if(tile.block().flags.size > 0 && tile.isCenter()){ + var map = getFlagged(team); - TileArray arr = map[flag.ordinal()]; - - arr.add(tile); - - map[flag.ordinal()] = arr; + for(BlockFlag flag : tile.block().flags.array){ + map[flag.ordinal()].add(tile.build); } } @@ -436,34 +426,4 @@ public class BlockIndexer{ //bounds checks only needed in very specific scenarios if(tile.blockID() < blocksPresent.length) blocksPresent[tile.blockID()] = true; } - - public static class TileArray implements Iterable{ - Seq tiles = new Seq<>(false, 16); - IntSet contained = new IntSet(); - - public void add(Tile tile){ - if(contained.add(tile.pos())){ - tiles.add(tile); - } - } - - public void remove(Tile tile){ - if(contained.remove(tile.pos())){ - tiles.remove(tile); - } - } - - public int size(){ - return tiles.size; - } - - public Tile first(){ - return tiles.first(); - } - - @Override - public Iterator iterator(){ - return tiles.iterator(); - } - } } diff --git a/core/src/mindustry/ai/Pathfinder.java b/core/src/mindustry/ai/Pathfinder.java index 9652c73d07..18fb9a6aff 100644 --- a/core/src/mindustry/ai/Pathfinder.java +++ b/core/src/mindustry/ai/Pathfinder.java @@ -394,7 +394,7 @@ public class Pathfinder implements Runnable{ public static class EnemyCoreField extends Flowfield{ @Override protected void getPositions(IntSeq out){ - for(Tile other : indexer.getEnemy(team, BlockFlag.core)){ + for(Building other : indexer.getEnemy(team, BlockFlag.core)){ out.add(other.pos()); } @@ -410,7 +410,7 @@ public class Pathfinder implements Runnable{ public static class RallyField extends Flowfield{ @Override protected void getPositions(IntSeq out){ - for(Tile other : indexer.getAllied(team, BlockFlag.rally)){ + for(Building other : indexer.getFlagged(team, BlockFlag.rally)){ out.add(other.pos()); } } diff --git a/core/src/mindustry/ai/types/CargoAI.java b/core/src/mindustry/ai/types/CargoAI.java new file mode 100644 index 0000000000..9c60c0d495 --- /dev/null +++ b/core/src/mindustry/ai/types/CargoAI.java @@ -0,0 +1,179 @@ +package mindustry.ai.types; + +import arc.struct.*; +import arc.util.*; +import mindustry.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.type.*; +import mindustry.world.blocks.units.UnitCargoUnloadPoint.*; +import mindustry.world.meta.*; + +import static mindustry.Vars.*; + +public class CargoAI extends AIController{ + static Seq orderedItems = new Seq<>(); + static Seq targets = new Seq<>(); + + public static float emptyWaitTime = 60f * 2f, dropSpacing = 60f * 1.5f; + public static float transferRange = 20f, moveRange = 6f, moveSmoothing = 20f; + + public @Nullable UnitCargoUnloadPointBuild unloadTarget; + public @Nullable Item itemTarget; + public float noDestTimer = 0f; + public int targetIndex = 0; + + @Override + public void updateMovement(){ + if(!(unit instanceof BuildingTetherc tether) || tether.building() == null) return; + + var build = tether.building(); + + //empty, approach the loader, even if there's nothing to pick up (units hanging around doing nothing looks bad) + if(!unit.hasItem()){ + moveTo(build, moveRange, moveSmoothing); + + //check if ready to pick up + if(build.items.any() && unit.within(build, transferRange)){ + if(retarget()){ + findAnyTarget(build); + + //target has been found, grab items and go + if(unloadTarget != null){ + Call.takeItems(build, itemTarget, Math.min(unit.type.itemCapacity, build.items.get(itemTarget)), unit); + } + } + } + }else{ //the unit has an item, deposit it somewhere. + + //there may be no current target, try to find one + if(unloadTarget == null){ + if(retarget()){ + findDropTarget(unit.item(), 0, null); + + //if there is not even a single place to unload, dump items. + if(unloadTarget == null){ + unit.clearItem(); + } + } + }else{ + moveTo(unloadTarget, moveRange, moveSmoothing); + + //deposit in bursts, unloading can take a while + if(unit.within(unloadTarget, transferRange) && timer.get(timerTarget2, dropSpacing)){ + int max = unloadTarget.acceptStack(unit.item(), unit.stack.amount, unit); + + //deposit items when it's possible + if(max > 0){ + noDestTimer = 0f; + Call.transferItemTo(unit, unit.item(), max, unit.x, unit.y, unloadTarget); + + //try the next target later + if(!unit.hasItem()){ + targetIndex ++; + } + }else if((noDestTimer += dropSpacing) >= emptyWaitTime){ + //oh no, it's out of space - wait for a while, and if nothing changes, try the next destination + + //next targeting attempt will try the next destination point + targetIndex = findDropTarget(unit.item(), targetIndex, unloadTarget) + 1; + + //nothing found at all, clear item + if(unloadTarget == null){ + unit.clearItem(); + } + } + } + } + } + + } + + /** find target for the unit's current item */ + public int findDropTarget(Item item, int offset, UnitCargoUnloadPointBuild ignore){ + unloadTarget = null; + itemTarget = item; + + //autocast for convenience... I know all of these must be cargo unload points anyway + targets.selectFrom((Seq)(Seq)Vars.indexer.getFlagged(unit.team, BlockFlag.unitCargoUnloadPoint), u -> u.item == item); + + if(targets.isEmpty()) return 0; + + UnitCargoUnloadPointBuild lastStale = null; + + offset %= targets.size; + + int i = 0; + + for(var target : targets){ + if(i >= offset && target != ignore){ + if(target.stale){ + lastStale = target; + }else{ + unloadTarget = target; + return i; + } + } + i ++; + } + + //it's still possible that the ignored target may become available at some point, try that, so it doesn't waste items + if(ignore != null){ + unloadTarget = ignore; + }else if(lastStale != null){ //a stale target is better than nothing + unloadTarget = lastStale; + } + + return -1; + } + + public void findAnyTarget(Building build){ + unloadTarget = null; + itemTarget = null; + + //autocast for convenience... I know all of these must be cargo unload points anyway + var baseTargets = (Seq)(Seq)Vars.indexer.getFlagged(unit.team, BlockFlag.unitCargoUnloadPoint); + + if(baseTargets.isEmpty()) return; + + orderedItems.size = 0; + for(Item item : content.items()){ + if(build.items.get(item) > 0){ + orderedItems.add(item); + } + } + + //sort by most items in descending order, and try each one. + orderedItems.sort(i -> -build.items.get(i)); + + UnitCargoUnloadPointBuild lastStale = null; + + outer: + for(Item item : orderedItems){ + targets.selectFrom(baseTargets, u -> u.item == item); + + if(targets.size > 0) itemTarget = item; + + for(int i = 0; i < targets.size; i ++){ + var target = targets.get((i + targetIndex) % targets.size); + + lastStale = target; + + if(!target.stale){ + unloadTarget = target; + break outer; + } + } + } + + //if the only thing that was found was a "stale" target, at least try that... + if(unloadTarget == null && lastStale != null){ + unloadTarget = lastStale; + } + } + + void sortTargets(Seq targets){ + //find sort by "most desirable" first + targets.sort(Structs.comps(Structs.comparingInt(b -> b.items.total()), Structs.comparingFloat(b -> b.dst2(unit)))); + } +} diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 5da3da8026..3583b8cc4c 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -83,6 +83,8 @@ public class Blocks{ duct, ductRouter, overflowDuct, ductBridge, ductUnloader, surgeConveyor, surgeRouter, + unitCargoLoader, unitCargoUnloadPoint, + //liquid mechanicalPump, rotaryPump, impulsePump, conduit, pulseConduit, platedConduit, liquidRouter, liquidContainer, liquidTank, liquidJunction, bridgeConduit, phaseConduit, @@ -1554,7 +1556,7 @@ public class Blocks{ consumes.power(1.75f); }}; - //special transport blocks + //erekir transport blocks duct = new Duct("duct"){{ requirements(Category.distribution, with(Items.graphite, 2)); @@ -1608,6 +1610,24 @@ public class Blocks{ consumes.power(3f / 60f); }}; + unitCargoLoader = new UnitCargoLoader("unit-cargo-loader"){{ + requirements(Category.distribution, with(Items.silicon, 80, Items.phaseFabric, 60, Items.carbide, 50, Items.oxide, 40)); + + size = 3; + + consumes.power(4f / 60f); + + itemCapacity = 200; + }}; + + unitCargoUnloadPoint = new UnitCargoUnloadPoint("unit-cargo-unload-point"){{ + requirements(Category.distribution, with(Items.silicon, 60, Items.thorium, 80)); + + size = 2; + + itemCapacity = 100; + }}; + //endregion //region liquid diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 32329a1460..6c90ea8f72 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -67,6 +67,9 @@ public class UnitTypes{ //special block unit type public static @EntityDef({Unitc.class, BlockUnitc.class}) UnitType block; + //transport + public static @EntityDef({Unitc.class, BuildingTetherc.class}) UnitType manifold; + //endregion //region neoplasm @@ -2601,7 +2604,7 @@ public class UnitTypes{ //TODO emanate (+ better names) //endregion - //region internal + //region internal + special block = new UnitType("block"){{ speed = 0f; @@ -2613,6 +2616,32 @@ public class UnitTypes{ hidden = true; }}; + manifold = new UnitType("manifold"){{ + defaultController = CargoAI::new; + isCounted = false; + allowedInPayloads = false; + logicControllable = false; + envDisabled = 0; + + outlineColor = Pal.darkOutline; + lowAltitude = false; + flying = true; + drag = 0.06f; + speed = 2f; + rotateSpeed = 9f; + accel = 0.1f; + itemCapacity = 60; + health = 200f; + hitSize = 11f; + commandLimit = 0; + engineSize = 2.3f; + engineOffset = 6.5f; + + setEnginesMirror( + new UnitEngine(24 / 4f, -24 / 4f, 2.3f, 315f) + ); + }}; + //endregion //region neoplasm diff --git a/core/src/mindustry/core/FileTree.java b/core/src/mindustry/core/FileTree.java index 6ef93d7f5b..e5e676d951 100644 --- a/core/src/mindustry/core/FileTree.java +++ b/core/src/mindustry/core/FileTree.java @@ -9,10 +9,13 @@ import arc.audio.*; import arc.files.*; import arc.struct.*; import mindustry.*; +import mindustry.gen.*; /** Handles files in a modded context. */ public class FileTree implements FileHandleResolver{ private ObjectMap files = new ObjectMap<>(); + private ObjectMap loadedSounds = new ObjectMap<>(); + private ObjectMap loadedMusic = new ObjectMap<>(); public void addFile(String path, Fi f){ files.put(path, f); @@ -46,29 +49,41 @@ public class FileTree implements FileHandleResolver{ return get(fileName); } + /** + * Loads a sound by name from the sounds/ folder. OGG and MP3 are supported; the extension is automatically added to the end of the file name. + * Results are cached; consecutive calls to this method with the same name will return the same sound instance. + * */ public Sound loadSound(String soundName){ - if(Vars.headless) return new Sound(); + if(Vars.headless) return Sounds.none; - String name = "sounds/" + soundName; - String path = Vars.tree.get(name + ".ogg").exists() ? name + ".ogg" : name + ".mp3"; + return loadedSounds.get(soundName, () -> { + String name = "sounds/" + soundName; + String path = Vars.tree.get(name + ".ogg").exists() ? name + ".ogg" : name + ".mp3"; - var sound = new Sound(); - AssetDescriptor desc = Core.assets.load(path, Sound.class, new SoundParameter(sound)); - desc.errored = Throwable::printStackTrace; + var sound = new Sound(); + AssetDescriptor desc = Core.assets.load(path, Sound.class, new SoundParameter(sound)); + desc.errored = Throwable::printStackTrace; - return sound; + return sound; + }); } - public Music loadMusic(String soundName){ + /** + * Loads a music file by name from the music/ folder. OGG and MP3 are supported; the extension is automatically added to the end of the file name. + * Results are cached; consecutive calls to this method with the same name will return the same music instance. + * */ + public Music loadMusic(String musicName){ if(Vars.headless) return new Music(); - String name = "music/" + soundName; - String path = Vars.tree.get(name + ".ogg").exists() ? name + ".ogg" : name + ".mp3"; + return loadedMusic.get(musicName, () -> { + String name = "music/" + musicName; + String path = Vars.tree.get(name + ".ogg").exists() ? name + ".ogg" : name + ".mp3"; - var music = new Music(); - AssetDescriptor desc = Core.assets.load(path, Music.class, new MusicParameter(music)); - desc.errored = Throwable::printStackTrace; + var music = new Music(); + AssetDescriptor desc = Core.assets.load(path, Music.class, new MusicParameter(music)); + desc.errored = Throwable::printStackTrace; - return music; + return music; + }); } } diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 75aa611dbb..ce23b0e4e9 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -785,17 +785,19 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, } /** Dumps any item with an accumulator. May dump multiple times per frame. Use with care. */ - public void dumpAccumulate(){ - dumpAccumulate(null); + public boolean dumpAccumulate(){ + return dumpAccumulate(null); } /** Dumps any item with an accumulator. May dump multiple times per frame. Use with care. */ - public void dumpAccumulate(Item item){ + public boolean dumpAccumulate(Item item){ + boolean res = false; dumpAccum += delta(); while(dumpAccum >= 1f){ - dump(item); + res |= dump(item); dumpAccum -=1f; } + return res; } /** Try dumping any item near the building. */ diff --git a/core/src/mindustry/entities/comp/BuildingTetherComp.java b/core/src/mindustry/entities/comp/BuildingTetherComp.java index 410c7fa85d..d7139c6c56 100644 --- a/core/src/mindustry/entities/comp/BuildingTetherComp.java +++ b/core/src/mindustry/entities/comp/BuildingTetherComp.java @@ -1,5 +1,6 @@ package mindustry.entities.comp; +import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.game.*; import mindustry.gen.*; @@ -11,7 +12,7 @@ abstract class BuildingTetherComp implements Unitc{ @Import UnitType type; @Import Team team; - public Building building; + public @Nullable Building building; @Override public void update(){ diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index d51ef390c8..88ab08e18e 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -173,8 +173,7 @@ public class AIController implements UnitController{ public Teamc targetFlag(float x, float y, BlockFlag flag, boolean enemy){ if(unit.team == Team.derelict) return null; - Tile target = Geometry.findClosest(x, y, enemy ? indexer.getEnemy(unit.team, flag) : indexer.getAllied(unit.team, flag)); - return target == null ? null : target.build; + return Geometry.findClosest(x, y, enemy ? indexer.getEnemy(unit.team, flag) : indexer.getFlagged(unit.team, flag)); } public Teamc target(float x, float y, float range, boolean air, boolean ground){ diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index e428cd95d5..0900558353 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -193,10 +193,10 @@ public class SectorInfo{ stat.mean = Math.min(stat.mean, rawProduction.get(item, ExportStat::new).mean); }); - var pads = indexer.getAllied(state.rules.defaultTeam, BlockFlag.launchPad); + var pads = indexer.getFlagged(state.rules.defaultTeam, BlockFlag.launchPad); //disable export when launch pads are disabled, or there aren't any active ones - if(pads.size() == 0 || !Seq.with(pads).contains(t -> t.build.consValid())){ + if(pads.size == 0 || !pads.contains(t -> t.consValid())){ export.clear(); } diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index ee045f2c29..025ea5fc1c 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -273,7 +273,8 @@ public class LExecutor{ } } case building -> { - res = Geometry.findClosest(unit.x, unit.y, exec.bool(enemy) ? indexer.getEnemy(unit.team, flag) : indexer.getAllied(unit.team, flag)); + Building b = Geometry.findClosest(unit.x, unit.y, exec.bool(enemy) ? indexer.getEnemy(unit.team, flag) : indexer.getFlagged(unit.team, flag)); + res = b == null ? null : b.tile; build = true; } case spawn -> { diff --git a/core/src/mindustry/mod/Scripts.java b/core/src/mindustry/mod/Scripts.java index 50ee4cfc39..a07b5011f4 100644 --- a/core/src/mindustry/mod/Scripts.java +++ b/core/src/mindustry/mod/Scripts.java @@ -82,14 +82,16 @@ public class Scripts implements Disposable{ return Vars.tree.get(path, true).readBytes(); } - //kept for backwards compatibility + /** @deprecated use Vars.tree.loadSound(soundName) instead */ + @Deprecated public Sound loadSound(String soundName){ return Vars.tree.loadSound(soundName); } - //kept for backwards compatibility - public Music loadMusic(String soundName){ - return Vars.tree.loadMusic(soundName); + /** @deprecated use Vars.tree.loadMusic(musicName) instead */ + @Deprecated + public Music loadMusic(String musicName){ + return Vars.tree.loadMusic(musicName); } /** Ask the user to select a file to read for a certain purpose like "Please upload a sprite" */ diff --git a/core/src/mindustry/ui/fragments/BlockInventoryFragment.java b/core/src/mindustry/ui/fragments/BlockInventoryFragment.java index ecad1514fa..23ee7842d9 100644 --- a/core/src/mindustry/ui/fragments/BlockInventoryFragment.java +++ b/core/src/mindustry/ui/fragments/BlockInventoryFragment.java @@ -30,7 +30,7 @@ public class BlockInventoryFragment extends Fragment{ Table table = new Table(); Building tile; float holdTime = 0f, emptyTime; - boolean holding; + boolean holding, held; float[] shrinkHoldTimes = new float[content.items().size]; Item lastItem; @@ -69,6 +69,20 @@ public class BlockInventoryFragment extends Fragment{ tile = null; } + private void takeItem(int requested){ + //take everything + int amount = Math.min(requested, player.unit().maxAccepted(lastItem)); + + if(amount > 0){ + Call.requestItem(player, tile, lastItem, amount); + holding = false; + holdTime = 0f; + held = true; + + if(net.client()) Events.fire(new WithdrawEvent(tile, player, lastItem, amount)); + } + } + private void rebuild(boolean actions){ IntSet container = new IntSet(); @@ -90,17 +104,11 @@ public class BlockInventoryFragment extends Fragment{ emptyTime = 0f; } - if(holding && lastItem != null){ - holdTime += Time.delta; + if(holding && lastItem != null && (holdTime += Time.delta) >= holdWithdraw){ + holdTime = 0f; - if(holdTime >= holdWithdraw){ - int amount = Math.min(tile.items.get(lastItem), player.unit().maxAccepted(lastItem)); - Call.requestItem(player, tile, lastItem, amount); - holding = false; - holdTime = 0f; - - if(net.client()) Events.fire(new WithdrawEvent(tile, player, lastItem, amount)); - } + //take one when held + takeItem(1); } updateTablePosition(); @@ -155,23 +163,33 @@ public class BlockInventoryFragment extends Fragment{ }); image.addListener(l); - image.addListener(new InputListener(){ + Boolp validClick = () -> !(!canPick.get() || tile == null || !tile.isValid() || tile.items == null || !tile.items.has(item)); + + image.addListener(new ClickListener(){ + @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){ - if(!canPick.get() || tile == null || !tile.isValid() || tile.items == null || !tile.items.has(item)) return false; - int amount = Math.min(1, player.unit().maxAccepted(item)); - if(amount > 0){ - Call.requestItem(player, tile, item, amount); + held = false; + if(validClick.get()){ lastItem = item; holding = true; - holdTime = 0f; - if(net.client()) Events.fire(new WithdrawEvent(tile, player, item, amount)); } - return true; + + return super.touchDown(event, x, y, pointer, button); + } + + @Override + public void clicked(InputEvent event, float x, float y){ + if(!validClick.get() || held) return; + + //take all + takeItem(tile.items.get(lastItem = item)); } @Override public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){ + super.touchUp(event, x, y, pointer, button); + holding = false; lastItem = null; } diff --git a/core/src/mindustry/ui/fragments/HintsFragment.java b/core/src/mindustry/ui/fragments/HintsFragment.java index 26992df73d..d4f33fafbf 100644 --- a/core/src/mindustry/ui/fragments/HintsFragment.java +++ b/core/src/mindustry/ui/fragments/HintsFragment.java @@ -168,7 +168,7 @@ public class HintsFragment extends Fragment{ command(() -> state.rules.defaultTeam.data().units.size > 3 && !net.active(), () -> player.unit().isCommanding()), payloadPickup(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()), payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()), - waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getAllied(state.rules.defaultTeam, BlockFlag.extinguisher).size() > 0), + waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getFlagged(state.rules.defaultTeam, BlockFlag.extinguisher).size > 0), generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)), guardian(() -> state.boss() != null && state.boss().armor >= 4, () -> state.boss() == null), coreUpgrade(() -> state.isCampaign() && Blocks.coreFoundation.unlocked() diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index 6f376c7329..8cb6cb0570 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -3,6 +3,7 @@ package mindustry.world.blocks.distribution; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; +import arc.math.geom.*; import arc.struct.*; import arc.util.*; import arc.util.io.*; @@ -127,6 +128,20 @@ public class StackConveyor extends Block implements Autotiler{ } } + //draw inputs + if(state == stateLoad){ + for(int i = 0; i < 4; i++){ + if((blendprox & (1 << i)) != 0 && i != 0){ + int dir = rotation - i; + Draw.rect(sliced(regions[0], SliceMode.bottom), x + Geometry.d4x(dir) * tilesize*0.75f, y + Geometry.d4y(dir) * tilesize*0.75f, (float)(dir*90)); + } + } + }else if(state == stateUnload){ //front unload + if((blendprox & (1)) != 0){ + Draw.rect(sliced(regions[0], SliceMode.top), x + Geometry.d4x(rotation) * tilesize*0.75f, y + Geometry.d4y(rotation) * tilesize*0.75f, rotation * 90f); + } + } + Draw.z(Layer.block - 0.1f); Tile from = world.tile(link); diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index 36dc1673e1..ada9f8bc93 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -525,12 +525,16 @@ public class LogicBlock extends Block{ write.b(compressed); //write only the non-constant variables - int count = Structs.count(executor.vars, v -> !v.constant || v == executor.vars[LExecutor.varUnit]); + int count = Structs.count(executor.vars, v -> (!v.constant || v == executor.vars[LExecutor.varUnit]) && !(v.isobj && v.objval == null)); write.i(count); for(int i = 0; i < executor.vars.length; i++){ Var v = executor.vars[i]; + //null is the default variable value, so waste no time serializing that + if(v.isobj && v.objval == null) continue; + + //skip constants if(v.constant && i != LExecutor.varUnit) continue; //write the name and the object value diff --git a/core/src/mindustry/world/blocks/production/Pump.java b/core/src/mindustry/world/blocks/production/Pump.java index d8f29c33e8..6477714679 100644 --- a/core/src/mindustry/world/blocks/production/Pump.java +++ b/core/src/mindustry/world/blocks/production/Pump.java @@ -113,6 +113,12 @@ public class Pump extends LiquidBlock{ draw.drawBase(this); } + @Override + public void drawLight(){ + super.drawLight(); + draw.drawLights(this); + } + @Override public void pickedUp(){ amount = 0f; diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index 583327d953..f7a6a4775a 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -396,6 +396,9 @@ public class CoreBlock extends StorageBlock{ @Override public void drawSelect(){ + //do not draw a pointless single outline when there's no storage + if(team.cores().size <= 1 && !proximity.contains(storage -> storage.items == items)) return; + Lines.stroke(1f, Pal.accent); Cons outline = b -> { for(int i = 0; i < 4; i++){ diff --git a/core/src/mindustry/world/blocks/units/UnitCargoLoader.java b/core/src/mindustry/world/blocks/units/UnitCargoLoader.java new file mode 100644 index 0000000000..74eaa3da58 --- /dev/null +++ b/core/src/mindustry/world/blocks/units/UnitCargoLoader.java @@ -0,0 +1,146 @@ +package mindustry.world.blocks.units; + +import arc.*; +import arc.graphics.*; +import arc.graphics.g2d.*; +import arc.math.*; +import arc.util.*; +import arc.util.io.*; +import mindustry.content.*; +import mindustry.entities.*; +import mindustry.gen.*; +import mindustry.graphics.*; +import mindustry.type.*; +import mindustry.ui.*; +import mindustry.world.*; + +public class UnitCargoLoader extends Block{ + public UnitType unitType = UnitTypes.manifold; + public float buildTime = 60f * 8f; + + public float polyStroke = 1.8f, polyRadius = 8f; + public int polySides = 6; + public float polyRotateSpeed = 1f; + public Color polyColor = Pal.accent; + + public UnitCargoLoader(String name){ + super(name); + + solid = true; + update = true; + hasItems = true; + itemCapacity = 200; + } + + @Override + public void setBars(){ + super.setBars(); + + bars.add("units", (UnitTransportSourceBuild e) -> + new Bar( + () -> + Core.bundle.format("bar.unitcap", + Fonts.getUnicodeStr(unitType.name), + e.team.data().countType(unitType), + Units.getStringCap(e.team) + ), + () -> Pal.power, + () -> (float)e.team.data().countType(unitType) / Units.getCap(e.team) + )); + } + + public class UnitTransportSourceBuild extends Building{ + //needs to be "unboxed" after reading, since units are read after buildings. + public int readUnitId = -1; + public float buildProgress, totalProgress; + public float warmup, readyness; + public @Nullable Unit unit; + + @Override + public void updateTile(){ + //unit was lost/destroyed + if(unit != null && (unit.dead || !unit.isAdded())){ + unit = null; + } + + if(readUnitId != -1){ + unit = Groups.unit.getByID(readUnitId); + readUnitId = -1; + } + + warmup = Mathf.approachDelta(warmup, efficiency(), 1f / 60f); + readyness = Mathf.approachDelta(readyness, unit != null ? 1f : 0f, 1f / 60f); + + if(unit == null && Units.canCreate(team, unitType)){ + buildProgress += edelta() / buildTime; + totalProgress += edelta(); + + if(buildProgress >= 1f){ + unit = unitType.create(team); + if(unit instanceof BuildingTetherc bt){ + bt.building(this); + } + unit.set(x, y); + unit.rotation = 90f; + unit.add(); + + Fx.spawn.at(unit); + + buildProgress = 0f; + } + } + } + + @Override + public boolean acceptItem(Building source, Item item){ + return items.total() < itemCapacity; + } + + @Override + public boolean shouldConsume(){ + return unit == null; + } + + @Override + public void draw(){ + Draw.rect(block.region, x, y); + if(unit == null){ + Draw.draw(Layer.blockOver, () -> { + //TODO make sure it looks proper + Drawf.construct(this, unitType.fullIcon, 0f, buildProgress, warmup, totalProgress); + }); + }else{ + Draw.z(Layer.bullet - 0.01f); + Draw.color(polyColor); + Lines.stroke(polyStroke * readyness); + Lines.poly(x, y, polySides, polyRadius, Time.time * polyRotateSpeed); + Draw.reset(); + Draw.z(Layer.block); + } + } + + @Override + public float totalProgress(){ + return totalProgress; + } + + @Override + public float progress(){ + return buildProgress; + } + + @Override + public void write(Writes write){ + super.write(write); + + write.i(unit == null ? -1 : unit.id); + } + + @Override + public void read(Reads read, byte revision){ + super.read(read, revision); + + readUnitId = read.i(); + } + } +} diff --git a/core/src/mindustry/world/blocks/units/UnitCargoUnloadPoint.java b/core/src/mindustry/world/blocks/units/UnitCargoUnloadPoint.java new file mode 100644 index 0000000000..7e9e4ba917 --- /dev/null +++ b/core/src/mindustry/world/blocks/units/UnitCargoUnloadPoint.java @@ -0,0 +1,109 @@ +package mindustry.world.blocks.units; + +import arc.graphics.g2d.*; +import arc.scene.ui.layout.*; +import arc.struct.*; +import arc.util.*; +import arc.util.io.*; +import mindustry.*; +import mindustry.annotations.Annotations.*; +import mindustry.gen.*; +import mindustry.type.*; +import mindustry.world.*; +import mindustry.world.blocks.*; +import mindustry.world.meta.*; + +import static mindustry.Vars.*; + +public class UnitCargoUnloadPoint extends Block{ + /** If a block is full for this amount of time, it will not be flown to anymore. */ + public float staleTimeDuration = 60f * 6f; + + public @Load("@-top") TextureRegion topRegion; + + public UnitCargoUnloadPoint(String name){ + super(name); + update = solid = true; + hasItems = true; + configurable = true; + saveConfig = true; + flags = EnumSet.of(BlockFlag.unitCargoUnloadPoint); + + config(Item.class, (UnitCargoUnloadPointBuild build, Item item) -> build.item = item); + configClear((UnitCargoUnloadPointBuild build) -> build.item = null); + } + + public class UnitCargoUnloadPointBuild extends Building{ + public Item item; + public float staleTimer; + public boolean stale; + + @Override + public void draw(){ + super.draw(); + + if(item != null){ + Draw.color(item.color); + Draw.rect(topRegion, x, y); + Draw.color(); + } + } + + @Override + public void updateTile(){ + super.updateTile(); + + if(items.total() < itemCapacity){ + staleTimer = 0f; + stale = false; + } + + if(dumpAccumulate()){ + staleTimer = 0f; + stale = false; + }else if(items.total() >= itemCapacity && (staleTimer += Time.delta) >= staleTimeDuration){ + stale = true; + } + } + + @Override + public int acceptStack(Item item, int amount, Teamc source){ + return Math.min(itemCapacity - items.total(), amount); + } + + @Override + public void buildConfiguration(Table table){ + ItemSelection.buildTable(UnitCargoUnloadPoint.this, table, content.items(), () -> item, this::configure); + } + + @Override + public boolean onConfigureTileTapped(Building other){ + if(this == other){ + deselect(); + configure(null); + return false; + } + + return true; + } + + @Override + public Object config(){ + return item; + } + + @Override + public void write(Writes write){ + super.write(write); + write.s(item == null ? -1 : item.id); + write.bool(stale); + } + + @Override + public void read(Reads read, byte revision){ + super.read(read, revision); + item = Vars.content.item(read.s()); + stale = read.bool(); + } + } +} diff --git a/core/src/mindustry/world/meta/BlockFlag.java b/core/src/mindustry/world/meta/BlockFlag.java index 985b66c181..3e69cff29c 100644 --- a/core/src/mindustry/world/meta/BlockFlag.java +++ b/core/src/mindustry/world/meta/BlockFlag.java @@ -23,7 +23,9 @@ public enum BlockFlag{ /** Blocks that extinguishes fires. */ extinguisher, /** Just a launch pad. */ - launchPad; + launchPad, + /** Destination for unit cargo. */ + unitCargoUnloadPoint; public final static BlockFlag[] all = values();