WIP logic particle effect instruction

This commit is contained in:
Anuken
2023-08-02 22:15:44 -04:00
parent 660c38db43
commit 6bf5e8ae1e
19 changed files with 596 additions and 23 deletions

View File

@@ -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);

View File

@@ -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));
}
}
}

View File

@@ -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\"";

View 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;
}
}
}