mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-07 17:34:13 +07:00
WIP logic particle effect instruction
This commit is contained in:
parent
660c38db43
commit
6bf5e8ae1e
BIN
core/assets-raw/sprites/statuses/status-fast.png
Normal file
BIN
core/assets-raw/sprites/statuses/status-fast.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
@ -2258,6 +2258,8 @@ lst.cutscene = Manipulate the player camera.
|
||||
lst.setflag = Set a global flag that can be read by all processors.
|
||||
lst.getflag = Check if a global flag is set.
|
||||
lst.setprop = Sets a property of a unit or building.
|
||||
lst.effect = Create a particle effect.
|
||||
lst.sync = Sync a variable across the network.\nOnly invoked 10 times a second at most.
|
||||
|
||||
logic.nounitbuild = [red]Unit building logic is not allowed here.
|
||||
|
||||
|
@ -204,6 +204,8 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
|
||||
if(limitFps){
|
||||
nextFrame += (1000 * 1000000) / targetfps;
|
||||
}else{
|
||||
nextFrame = Time.nanos();
|
||||
}
|
||||
|
||||
if(!finished){
|
||||
|
@ -2937,6 +2937,7 @@ public class Blocks{
|
||||
armor = 5f;
|
||||
alwaysUnlocked = true;
|
||||
incinerateNonBuildable = true;
|
||||
requiresCoreZone = true;
|
||||
|
||||
//TODO should this be higher?
|
||||
buildCostMultiplier = 0.7f;
|
||||
@ -2956,6 +2957,7 @@ public class Blocks{
|
||||
armor = 10f;
|
||||
incinerateNonBuildable = true;
|
||||
buildCostMultiplier = 0.7f;
|
||||
requiresCoreZone = true;
|
||||
|
||||
unitCapModifier = 15;
|
||||
researchCostMultipliers.put(Items.silicon, 0.5f);
|
||||
@ -2973,6 +2975,7 @@ public class Blocks{
|
||||
armor = 15f;
|
||||
incinerateNonBuildable = true;
|
||||
buildCostMultiplier = 0.7f;
|
||||
requiresCoreZone = true;
|
||||
|
||||
unitCapModifier = 15;
|
||||
researchCostMultipliers.put(Items.silicon, 0.4f);
|
||||
@ -4571,7 +4574,7 @@ public class Blocks{
|
||||
deathExplosionEffect = Fx.massiveExplosion;
|
||||
shootOnDeath = true;
|
||||
shake = 10f;
|
||||
bullet = new ExplosionBulletType(640f, 65f){{
|
||||
bullet = new ExplosionBulletType(700f, 65f){{
|
||||
hitColor = Pal.redLight;
|
||||
shootEffect = new MultiEffect(Fx.massiveExplosion, Fx.scatheExplosion, Fx.scatheLight, new WaveEffect(){{
|
||||
lifetime = 10f;
|
||||
|
@ -29,11 +29,11 @@ public class Fx{
|
||||
|
||||
none = new Effect(0, 0f, e -> {}),
|
||||
|
||||
blockCrash = new Effect(100f, e -> {
|
||||
blockCrash = new Effect(90f, e -> {
|
||||
if(!(e.data instanceof Block block)) return;
|
||||
|
||||
alpha(e.fin() + 0.5f);
|
||||
float offset = Mathf.lerp(0f, 200f, e.fout());
|
||||
float offset = Mathf.lerp(0f, 180f, e.fout());
|
||||
color(0f, 0f, 0f, 0.44f);
|
||||
rect(block.fullIcon, e.x - offset * 4f, e.y, (float)block.size * 8f, (float)block.size * 8f);
|
||||
color(Color.white);
|
||||
@ -417,6 +417,20 @@ public class Fx{
|
||||
Lines.spikes(e.x, e.y, 1f + e.fin() * 6f, e.fout() * 4f, 6);
|
||||
}),
|
||||
|
||||
sparkExplosion = new Effect(30f, 160f, e -> {
|
||||
color(e.color);
|
||||
stroke(e.fout() * 3f);
|
||||
float circleRad = 6f + e.finpow() * e.rotation;
|
||||
Lines.circle(e.x, e.y, circleRad);
|
||||
|
||||
rand.setSeed(e.id);
|
||||
for(int i = 0; i < 16; i++){
|
||||
float angle = rand.random(360f);
|
||||
float lenRand = rand.random(0.5f, 1f);
|
||||
Lines.lineAngle(e.x, e.y, angle, e.foutpow() * e.rotation * 0.8f * rand.random(1f, 0.6f) + 2f, e.finpow() * e.rotation * 1.2f * lenRand + 6f);
|
||||
}
|
||||
}),
|
||||
|
||||
titanExplosion = new Effect(30f, 160f, e -> {
|
||||
color(e.color);
|
||||
stroke(e.fout() * 3f);
|
||||
@ -609,6 +623,12 @@ public class Fx{
|
||||
Lines.circle(e.x, e.y, 2f + e.finpow() * 7f);
|
||||
}),
|
||||
|
||||
dynamicWave = new Effect(22, e -> {
|
||||
color(e.color, 0.7f);
|
||||
stroke(e.fout() * 2f);
|
||||
Lines.circle(e.x, e.y, 4f + e.finpow() * e.rotation);
|
||||
}),
|
||||
|
||||
shieldWave = new Effect(22, e -> {
|
||||
color(e.color, 0.7f);
|
||||
stroke(e.fout() * 2f);
|
||||
@ -1517,6 +1537,15 @@ public class Fx{
|
||||
});
|
||||
}),
|
||||
|
||||
smokePuff = new Effect(30, e -> {
|
||||
color(e.color);
|
||||
|
||||
randLenVectors(e.id, 6, 4f + 30f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 3f);
|
||||
Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout());
|
||||
});
|
||||
}),
|
||||
|
||||
shootSmall = new Effect(8, e -> {
|
||||
color(Pal.lighterOrange, Pal.lightOrange, e.fin());
|
||||
float w = 1f + 5 * e.fout();
|
||||
|
@ -458,7 +458,6 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
//TODO objectives clientside???
|
||||
if(!state.isEditor()){
|
||||
state.rules.objectives.update();
|
||||
if(state.rules.objectives.checkChanged() && net.server()){
|
||||
|
@ -71,6 +71,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public SchematicsDialog schematics;
|
||||
public ModsDialog mods;
|
||||
public ColorPicker picker;
|
||||
public EffectsDialog effects;
|
||||
public LogicDialog logic;
|
||||
public FullTextDialog fullText;
|
||||
public CampaignCompleteDialog campaignComplete;
|
||||
@ -183,6 +184,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
consolefrag = new ConsoleFragment();
|
||||
|
||||
picker = new ColorPicker();
|
||||
effects = new EffectsDialog();
|
||||
editor = new MapEditorDialog();
|
||||
controls = new KeybindDialog();
|
||||
restart = new GameOverDialog();
|
||||
|
@ -2,6 +2,7 @@ package mindustry.logic;
|
||||
|
||||
import arc.*;
|
||||
import arc.files.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
@ -81,6 +82,13 @@ public class GlobalVars{
|
||||
}
|
||||
}
|
||||
|
||||
for(var entry : Colors.getColors().entries()){
|
||||
//ignore uppercase variants, they are duplicates
|
||||
if(Character.isUpperCase(entry.key.charAt(0))) continue;
|
||||
|
||||
put("@color" + Strings.capitalize(entry.key), entry.value.toDoubleBits());
|
||||
}
|
||||
|
||||
//used as a special value for any environmental solid block
|
||||
put("@solid", Blocks.stoneWall);
|
||||
|
||||
|
@ -16,6 +16,7 @@ import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.LogicFx.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
@ -122,6 +123,11 @@ public class LExecutor{
|
||||
return index < 0 ? logicVars.get(-index) : vars[index];
|
||||
}
|
||||
|
||||
/** @return a Var from this processor, never a global constant. May be null if out of bounds. */
|
||||
public @Nullable Var optionalVar(int index){
|
||||
return index < 0 || index >= vars.length ? null : vars[index];
|
||||
}
|
||||
|
||||
public @Nullable Building building(int index){
|
||||
Object o = var(index).objval;
|
||||
return var(index).isobj && o instanceof Building building ? building : null;
|
||||
@ -203,6 +209,9 @@ public class LExecutor{
|
||||
public Object objval;
|
||||
public double numval;
|
||||
|
||||
//ms timestamp for when this was last synced; used in the sync instruction
|
||||
public long syncTime;
|
||||
|
||||
public Var(String name){
|
||||
this.name = name;
|
||||
}
|
||||
@ -1557,6 +1566,35 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
public static class EffectI implements LInstruction{
|
||||
public EffectEntry type;
|
||||
public int x, y, rotation, color, data;
|
||||
|
||||
public EffectI(EffectEntry type, int x, int y, int rotation, int color, int data){
|
||||
this.type = type;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotation = rotation;
|
||||
this.color = color;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public EffectI(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
if(type != null){
|
||||
double col = exec.num(color);
|
||||
//limit size so people don't create lag with ridiculous numbers (some explosions scale with size)
|
||||
float rot = type.rotate ? exec.numf(rotation) :
|
||||
Math.min(exec.numf(rotation), 1000f);
|
||||
|
||||
type.effect.at(World.unconv(exec.numf(x)), World.unconv(exec.numf(y)), rot, Tmp.c1.fromDouble(col), exec.obj(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExplosionI implements LInstruction{
|
||||
public int team, x, y, radius, damage, air, ground, pierce;
|
||||
|
||||
@ -1617,6 +1655,47 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(unreliable = true)
|
||||
public static void syncVariable(Building building, int variable, Object value){
|
||||
if(building instanceof LogicBuild build){
|
||||
Var v = build.executor.optionalVar(variable);
|
||||
if(v != null && !v.constant){
|
||||
if(value instanceof Double d){
|
||||
v.isobj = false;
|
||||
v.numval = d;
|
||||
}else{
|
||||
v.isobj = true;
|
||||
v.objval =value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SyncI implements LInstruction{
|
||||
//10 syncs per second
|
||||
static final long syncInterval = 1000 / 10;
|
||||
|
||||
public int variable;
|
||||
|
||||
public SyncI(int variable){
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
public SyncI(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
if(exec.build != null && exec.build.block.privileged){
|
||||
Var v = exec.var(variable);
|
||||
if(!v.constant && Time.timeSinceMillis(v.syncTime) > syncInterval){
|
||||
v.syncTime = Time.millis();
|
||||
Call.syncVariable(exec.build, variable, v.isobj ? v.objval : v.numval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetFlagI implements LInstruction{
|
||||
public int result, flag;
|
||||
|
||||
@ -1638,6 +1717,15 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void setFlag(String flag, boolean add){
|
||||
if(add){
|
||||
state.rules.objectiveFlags.add(flag);
|
||||
}else{
|
||||
state.rules.objectiveFlags.remove(flag);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetFlagI implements LInstruction{
|
||||
public int flag, value;
|
||||
|
||||
@ -1651,12 +1739,9 @@ public class LExecutor{
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
if(exec.obj(flag) instanceof String str){
|
||||
if(!exec.bool(value)){
|
||||
state.rules.objectiveFlags.remove(str);
|
||||
}else{
|
||||
state.rules.objectiveFlags.add(str);
|
||||
}
|
||||
//don't invoke unless the flag state actually changes
|
||||
if(exec.obj(flag) instanceof String str && state.rules.objectiveFlags.contains(str) != exec.bool(value)){
|
||||
Call.setFlag(str, exec.bool(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,10 @@ import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.LCanvas.*;
|
||||
import mindustry.logic.LExecutor.*;
|
||||
import mindustry.logic.LogicFx.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
@ -1506,6 +1508,80 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("effect")
|
||||
public static class EffectStatement extends LStatement{
|
||||
public String type = "warn", x = "0", y = "0", sizerot = "2", color = "%ffaaff", data = "";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
table.clearChildren();
|
||||
|
||||
table.button(b -> {
|
||||
b.label(() -> type).growX().wrap().labelAlign(Align.center);
|
||||
b.clicked(() -> ui.effects.show(entry -> {
|
||||
type = entry.name;
|
||||
build(table);
|
||||
}));
|
||||
}, Styles.logict, () -> {}).size(150f, 40f).margin(5f).pad(4f).color(table.color).colspan(2);
|
||||
|
||||
EffectEntry entry = LogicFx.get(type);
|
||||
|
||||
row(table);
|
||||
|
||||
fields(table, "x", x, str -> x = str);
|
||||
fields(table, "y", y, str -> y = str);
|
||||
row(table);
|
||||
|
||||
if(entry != null){
|
||||
if(entry.color){
|
||||
fields(table, "color", color, str -> color = str).width(120f);
|
||||
|
||||
table.button(b -> {
|
||||
b.image(Icon.pencilSmall);
|
||||
b.clicked(() -> {
|
||||
Color current = Pal.accent.cpy();
|
||||
if(color.startsWith("%")){
|
||||
try{
|
||||
current = Color.valueOf(color.substring(1));
|
||||
}catch(Exception ignored){}
|
||||
}
|
||||
|
||||
ui.picker.show(current, result -> {
|
||||
color = "%" + result.toString().substring(0, result.a >= 1f ? 6 : 8);
|
||||
build(table);
|
||||
});
|
||||
});
|
||||
}, Styles.logict, () -> {}).size(40f).padLeft(-11).color(table.color);
|
||||
}
|
||||
|
||||
row(table);
|
||||
|
||||
if(entry.size || entry.rotate){
|
||||
fields(table, entry.size ? "size" : "rotation", sizerot, str -> sizerot = str);
|
||||
}
|
||||
|
||||
if(entry.data != null){
|
||||
fields(table, "data", data, str -> data = str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean privileged(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler b){
|
||||
return new EffectI(LogicFx.get(type), b.var(x), b.var(y), b.var(sizerot), b.var(color), b.var(data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.world;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("explosion")
|
||||
public static class ExplosionStatement extends LStatement{
|
||||
public String team = "@crux", x = "0", y = "0", radius = "5", damage = "50", air = "true", ground = "true", pierce = "false";
|
||||
@ -1626,6 +1702,32 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: test this first
|
||||
//@RegisterStatement("sync")
|
||||
public static class SyncStatement extends LStatement{
|
||||
public String variable = "var";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
fields(table, variable, str -> variable = str).width(190f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean privileged(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new SyncI(builder.var(variable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.world;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("getflag")
|
||||
public static class GetFlagStatement extends LStatement{
|
||||
public String result = "result", flag = "\"flag\"";
|
||||
|
106
core/src/mindustry/logic/LogicFx.java
Normal file
106
core/src/mindustry/logic/LogicFx.java
Normal file
@ -0,0 +1,106 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class LogicFx{
|
||||
private static OrderedMap<String, EffectEntry> map = new OrderedMap<>();
|
||||
|
||||
static{
|
||||
map.putAll(
|
||||
"warn", new EffectEntry(Fx.unitCapKill),
|
||||
"cross", new EffectEntry(Fx.unitEnvKill),
|
||||
"blockFall", new EffectEntry(Fx.blockCrash).data(Block.class).bounds(100f),
|
||||
"placeBlock", new EffectEntry(Fx.placeBlock).size(),
|
||||
"placeBlockSpark", new EffectEntry(Fx.coreLaunchConstruct).size(),
|
||||
"breakBlock", new EffectEntry(Fx.breakBlock).size(),
|
||||
"spawn", new EffectEntry(Fx.spawn),
|
||||
"trail", new EffectEntry(Fx.colorTrail).size().color(),
|
||||
"breakProp", new EffectEntry(Fx.breakProp).size().color(),
|
||||
"smokeCloud", new EffectEntry(Fx.missileTrailSmoke).color(),
|
||||
"vapor", new EffectEntry(Fx.vapor).color(),
|
||||
"hit", new EffectEntry(Fx.hitBulletColor).color(),
|
||||
"hitSquare", new EffectEntry(Fx.hitSquaresColor).color(),
|
||||
"shootSmall", new EffectEntry(Fx.shootSmall).color().rotate(),
|
||||
"shootBig", new EffectEntry(Fx.shootTitan).color().rotate(),
|
||||
"smokeSmall", new EffectEntry(Fx.shootSmallSmoke).rotate(),
|
||||
"smokeBig", new EffectEntry(Fx.shootBigSmoke).rotate(),
|
||||
"smokeColor", new EffectEntry(Fx.shootSmokeTitan).rotate().color(),
|
||||
"smokeSquare", new EffectEntry(Fx.shootSmokeSquare).rotate().color(),
|
||||
"smokeSquareBig", new EffectEntry(Fx.shootSmokeSquareBig).rotate().color(),
|
||||
"spark", new EffectEntry(Fx.hitLaserBlast).color(),
|
||||
"sparkBig", new EffectEntry(Fx.circleColorSpark).color(),
|
||||
"sparkShoot", new EffectEntry(Fx.colorSpark).rotate().color(),
|
||||
"sparkShootBig", new EffectEntry(Fx.randLifeSpark).rotate().color(),
|
||||
"drill", new EffectEntry(Fx.mine).color(),
|
||||
"drillBig", new EffectEntry(Fx.mineHuge).color(),
|
||||
"lightBlock", new EffectEntry(Fx.lightBlock).size().color(),
|
||||
"explosion", new EffectEntry(Fx.dynamicExplosion).size(),
|
||||
"smokePuff", new EffectEntry(Fx.smokePuff).color(),
|
||||
"sparkExplosion", new EffectEntry(Fx.titanExplosion).color(),
|
||||
"crossExplosion", new EffectEntry(Fx.dynamicSpikes).size().color(),
|
||||
"wave", new EffectEntry(Fx.dynamicWave).size(),
|
||||
"bubble", new EffectEntry(Fx.airBubble)
|
||||
);
|
||||
|
||||
map.each((n, e) -> e.name = n);
|
||||
}
|
||||
|
||||
public static Iterable<EffectEntry> entries(){
|
||||
return map.orderedKeys().map(s -> map.get(s));
|
||||
}
|
||||
|
||||
public static @Nullable EffectEntry get(String name){
|
||||
return map.get(name);
|
||||
}
|
||||
|
||||
public static String[] all(){
|
||||
return map.orderedKeys().toArray(String.class);
|
||||
}
|
||||
|
||||
public static class EffectEntry{
|
||||
public String name = "";
|
||||
public Effect effect;
|
||||
public boolean size, rotate, color;
|
||||
public @Nullable Class<?> data;
|
||||
/** cached bounds for this effect, negative if unset */
|
||||
public float bounds = -1f;
|
||||
|
||||
public EffectEntry(Effect effect){
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
public EffectEntry bounds(float bounds){
|
||||
this.bounds = bounds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EffectEntry name(String name){
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EffectEntry size(){
|
||||
size = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EffectEntry rotate(){
|
||||
rotate = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EffectEntry color(){
|
||||
color = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EffectEntry data(Class<?> data){
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -936,6 +936,8 @@ public class UnitType extends UnlockableContent{
|
||||
public void createIcons(MultiPacker packer){
|
||||
super.createIcons(packer);
|
||||
|
||||
if(constructor == null) throw new IllegalArgumentException("No constructor set up for unit '" + name + "'. Make sure you assign a valid value to `constructor`, e.g. `constructor = UnitEntity::new`");
|
||||
|
||||
sample = constructor.get();
|
||||
|
||||
var toOutline = new Seq<TextureRegion>();
|
||||
|
@ -38,7 +38,7 @@ public class Weapon implements Cloneable{
|
||||
public boolean useAmmo = true;
|
||||
/** whether to create a flipped copy of this weapon upon initialization. default: true */
|
||||
public boolean mirror = true;
|
||||
/** whether to flip the weapon's sprite when rendering */
|
||||
/** whether to flip the weapon's sprite when rendering. internal use only - do not set! */
|
||||
public boolean flipSprite = false;
|
||||
/** whether to shoot the weapons in different arms one after another, rather than all at once; only valid when mirror = true */
|
||||
public boolean alternate = true;
|
||||
|
@ -2,6 +2,7 @@ package mindustry.ui;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.graphics.*;
|
||||
@ -28,6 +29,10 @@ public class BorderImage extends Image{
|
||||
thickness = thick;
|
||||
}
|
||||
|
||||
public BorderImage(Drawable region){
|
||||
super(region);
|
||||
}
|
||||
|
||||
public BorderImage border(Color color){
|
||||
this.borderColor = color;
|
||||
return this;
|
||||
|
@ -185,13 +185,6 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
check("@rules.hidebannedblocks", b -> rules.hideBannedBlocks = b, () -> rules.hideBannedBlocks);
|
||||
check("@bannedblocks.whitelist", b -> rules.blockWhitelist = b, () -> rules.blockWhitelist);
|
||||
|
||||
//TODO objectives would be nice
|
||||
if(experimental && false){
|
||||
main.button("@objectives", () -> {
|
||||
|
||||
}).left().width(300f).row();
|
||||
}
|
||||
|
||||
title("@rules.title.unit");
|
||||
check("@rules.unitcapvariable", b -> rules.unitCapVariable = b, () -> rules.unitCapVariable);
|
||||
numberi("@rules.unitcap", f -> rules.unitCap = f, () -> rules.unitCap, -999, 999);
|
||||
|
235
core/src/mindustry/ui/dialogs/EffectsDialog.java
Normal file
235
core/src/mindustry/ui/dialogs/EffectsDialog.java
Normal file
@ -0,0 +1,235 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.logic.LogicFx.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class EffectsDialog extends BaseDialog{
|
||||
static BoundsBatch bounds = new BoundsBatch();
|
||||
|
||||
Iterable<EffectEntry> entries;
|
||||
@Nullable Cons<EffectEntry> listener;
|
||||
|
||||
public EffectsDialog(Iterable<EffectEntry> entries){
|
||||
super("Effects");
|
||||
|
||||
this.entries = entries;
|
||||
|
||||
addCloseButton();
|
||||
makeButtonOverlay();
|
||||
onResize(this::setup);
|
||||
shown(this::setup);
|
||||
|
||||
setup();
|
||||
}
|
||||
|
||||
public EffectsDialog(){
|
||||
this(LogicFx.entries());
|
||||
}
|
||||
|
||||
public static EffectsDialog withAllEffects(){
|
||||
return new EffectsDialog(Seq.select(Fx.class.getFields(), f -> f.getType() == Effect.class).map(f -> new EffectEntry(Reflect.get(f)).name(f.getName())));
|
||||
}
|
||||
|
||||
public Dialog show(Cons<EffectEntry> listener){
|
||||
this.listener = listener;
|
||||
return super.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog show(){
|
||||
this.listener = null;
|
||||
return super.show();
|
||||
}
|
||||
|
||||
void setup(){
|
||||
float size = 280f;
|
||||
int cols = (int)Math.max(1, Core.graphics.getWidth() / Scl.scl(size + 12f));
|
||||
|
||||
cont.clearChildren();
|
||||
cont.pane(t -> {
|
||||
int i = 0;
|
||||
for(var entry : entries){
|
||||
float bounds = calculateSize(entry);
|
||||
|
||||
if(bounds <= 0) continue;
|
||||
|
||||
ClickListener cl = new ClickListener();
|
||||
|
||||
t.stack(
|
||||
new EffectCell(entry, cl),
|
||||
new Table(af -> af.add(entry.name).grow().labelAlign(Align.bottomLeft).style(Styles.outlineLabel).bottom().left())
|
||||
).size(size).with(a -> {
|
||||
a.clicked(() -> {
|
||||
if(listener != null){
|
||||
listener.get(entry);
|
||||
hide();
|
||||
}
|
||||
});
|
||||
a.addListener(cl);
|
||||
a.addListener(new HandCursorListener(() -> listener != null, true));
|
||||
});
|
||||
|
||||
if(++i % cols == 0) t.row();
|
||||
}
|
||||
}).grow().scrollX(false);
|
||||
}
|
||||
|
||||
static Object getData(Class<?> type){
|
||||
if(type == Block.class) return Blocks.router;
|
||||
return null;
|
||||
}
|
||||
|
||||
static float calculateSize(EffectEntry entry){
|
||||
if(entry.bounds >= 0) return entry.bounds;
|
||||
|
||||
|
||||
var effect = entry.effect;
|
||||
try{
|
||||
effect.init();
|
||||
Batch prev = Core.batch;
|
||||
bounds.reset();
|
||||
Core.batch = bounds;
|
||||
Object data = getData(entry.data);
|
||||
|
||||
float lifetime = effect.lifetime;
|
||||
float rot = 1f;
|
||||
int steps = 60;
|
||||
int seeds = 4;
|
||||
for(int s = 0; s < seeds; s++){
|
||||
for(int i = 0; i <= steps; i++){
|
||||
effect.render(1, Color.white, i / (float)steps * lifetime, lifetime, rot, 0f, 0f, data);
|
||||
}
|
||||
}
|
||||
|
||||
Core.batch = prev;
|
||||
|
||||
return entry.bounds = bounds.max * 2f;
|
||||
}catch(Exception e){
|
||||
//might crash with invalid data
|
||||
return -1f;
|
||||
}
|
||||
}
|
||||
|
||||
static class BoundsBatch extends Batch{
|
||||
float max;
|
||||
|
||||
void reset(){
|
||||
max = 0f;
|
||||
}
|
||||
|
||||
void max(float... xs){
|
||||
for(float f : xs){
|
||||
if(Float.isNaN(f)) continue;
|
||||
max = Math.max(max, Math.abs(f));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draw(Texture texture, float[] spriteVertices, int offset, int count){
|
||||
for(int i = offset; i < count; i += SpriteBatch.VERTEX_SIZE){
|
||||
max(spriteVertices[i], spriteVertices[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float rotation){
|
||||
float worldOriginX = x + originX;
|
||||
float worldOriginY = y + originY;
|
||||
float fx = -originX;
|
||||
float fy = -originY;
|
||||
float fx2 = width - originX;
|
||||
float fy2 = height - originY;
|
||||
float cos = Mathf.cosDeg(rotation);
|
||||
float sin = Mathf.sinDeg(rotation);
|
||||
float x1 = cos * fx - sin * fy + worldOriginX;
|
||||
float y1 = sin * fx + cos * fy + worldOriginY;
|
||||
float x2 = cos * fx - sin * fy2 + worldOriginX;
|
||||
float y2 = sin * fx + cos * fy2 + worldOriginY;
|
||||
float x3 = cos * fx2 - sin * fy2 + worldOriginX;
|
||||
float y3 = sin * fx2 + cos * fy2 + worldOriginY;
|
||||
|
||||
max(x1, y1, x2, y2, x3, y3, x1 + (x3 - x2), y3 - (y2 - y1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void flush(){}
|
||||
}
|
||||
|
||||
class EffectCell extends Element{
|
||||
EffectEntry effect;
|
||||
float size = -1f;
|
||||
|
||||
int id = 1;
|
||||
float time = 0f;
|
||||
float lifetime;
|
||||
float rotation = 1f;
|
||||
Object data;
|
||||
ClickListener cl;
|
||||
|
||||
public EffectCell(EffectEntry effect, ClickListener cl){
|
||||
this.effect = effect;
|
||||
this.lifetime = effect.effect.lifetime;
|
||||
this.cl = cl;
|
||||
|
||||
data = getData(effect.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(size < 0){
|
||||
size = calculateSize(effect) + 1f;
|
||||
}
|
||||
|
||||
color.fromHsv((Time.globalTime * 2f) % 360f, 1f, 1f);
|
||||
|
||||
if(clipBegin(x, y, width, height)){
|
||||
Draw.colorl(cl.isOver() && listener != null ? 0.4f : 0.5f);
|
||||
Draw.alpha(parentAlpha);
|
||||
Tex.alphaBg.draw(x, y, width, height);
|
||||
Draw.reset();
|
||||
Draw.flush();
|
||||
|
||||
float scale = width / size;
|
||||
Tmp.m1.set(Draw.trans());
|
||||
Draw.trans().translate(x + width/2f, y + height/2f).scale(scale, scale);
|
||||
Draw.flush();
|
||||
this.lifetime = effect.effect.render(id, color, time, lifetime, rotation, 0f, 0f, data);
|
||||
|
||||
Draw.flush();
|
||||
Draw.trans().set(Tmp.m1);
|
||||
clipEnd();
|
||||
}
|
||||
|
||||
Lines.stroke(Scl.scl(3f), Color.black);
|
||||
Lines.rect(x, y, width, height);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
|
||||
time += Time.delta;
|
||||
if(time >= lifetime){
|
||||
id ++;
|
||||
}
|
||||
time %= lifetime;
|
||||
}
|
||||
}
|
||||
}
|
@ -1370,7 +1370,7 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
return switch(sensor){
|
||||
case color -> mapColor.toDoubleBits();
|
||||
case health, maxHealth -> health;
|
||||
case size -> size * tilesize;
|
||||
case size -> size;
|
||||
case itemCapacity -> itemCapacity;
|
||||
case liquidCapacity -> liquidCapacity;
|
||||
case powerCapacity -> consPower != null && consPower.buffered ? consPower.capacity : 0f;
|
||||
|
@ -37,6 +37,8 @@ public class CoreBlock extends StorageBlock{
|
||||
public @Load(value = "@-thruster2", fallback = "clear-effect") TextureRegion thruster2; //bot left
|
||||
public float thrusterLength = 14f/4f;
|
||||
public boolean isFirstTier;
|
||||
/** If true, this core type requires a core zone to upgrade. */
|
||||
public boolean requiresCoreZone;
|
||||
public boolean incinerateNonBuildable = false;
|
||||
|
||||
public UnitType unitType = UnitTypes.alpha;
|
||||
@ -60,8 +62,6 @@ public class CoreBlock extends StorageBlock{
|
||||
|
||||
//support everything
|
||||
replaceable = false;
|
||||
//TODO should AI ever rebuild this?
|
||||
//rebuildable = false;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
@ -160,7 +160,7 @@ public class CoreBlock extends StorageBlock{
|
||||
//must have all requirements
|
||||
if(core == null || (!state.rules.infiniteResources && !core.items.has(requirements, state.rules.buildCostMultiplier))) return false;
|
||||
|
||||
return tile.block() instanceof CoreBlock && size > tile.block().size;
|
||||
return tile.block() instanceof CoreBlock && size > tile.block().size && (!requiresCoreZone || tempTiles.allMatch(o -> o.floor().allowCorePlacement));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -239,7 +239,7 @@ public class Reconstructor extends UnitBlock{
|
||||
|
||||
@Override
|
||||
public int getMaximumAccepted(Item item){
|
||||
return capacities[item.id];
|
||||
return Mathf.round(capacities[item.id] * state.rules.unitCost(team));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user