diff --git a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java index 9a9b253b00..b24b9c682d 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java +++ b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java @@ -41,21 +41,27 @@ public class EntityProcess extends BaseProcessor{ //find all components used... for(Stype type : allDefs){ - imports.addAll(getImports(type.e)); allComponents.addAll(allComponents(type)); } //add all components w/ dependencies - allComponents.addAll(types(Depends.class)); + allComponents.addAll(types(Depends.class).map(s -> Array.withArrays(getDependencies(s), s)).flatten()); + + //add component imports + for(Stype comp : allComponents){ + imports.addAll(getImports(comp.e)); + } //create component interfaces for(Stype component : allComponents){ TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)).addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class); + //implement extra interfaces these components may have, e.g. position for(Stype extraInterface : component.interfaces()){ inter.addSuperinterface(extraInterface.mirror()); } + //implement super interfaces Array depends = getDependencies(component); for(Stype type : depends){ inter.addSuperinterface(ClassName.get(packageName, interfaceName(type))); @@ -100,7 +106,7 @@ public class EntityProcess extends BaseProcessor{ Array fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT)); for(Svar f : fields){ VariableTree tree = f.tree(); - FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name(), Modifier.PUBLIC); + FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name()); //add initializer if it exists if(tree.getInitializer() != null){ fbuilder.initializer(tree.getInitializer().toString()); @@ -128,13 +134,15 @@ public class EntityProcess extends BaseProcessor{ mbuilder.addParameter(var.tname(), var.name()); } - boolean returns = !first.ret().toString().equals("void"); + //only write the block if it's a void method with several entries + boolean writeBlock = first.ret().toString().equals("void") && entry.value.size > 1; for(Smethod elem : entry.value){ //get all statements in the method, copy them over MethodTree methodTree = elem.tree(); BlockTree blockTree = methodTree.getBody(); String str = blockTree.toString(); + //name for code blocks in the methods String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", ""); //skip empty blocks @@ -143,13 +151,20 @@ public class EntityProcess extends BaseProcessor{ } //wrap scope to prevent variable leakage - if(!returns) mbuilder.addCode(blockName + ": {\n"); + if(writeBlock){ + //replace return; with block break + str = str.replace("return;", "break " + blockName + ";"); + mbuilder.addCode(blockName + ": {\n"); + } + + //trim block + str = str.substring(2, str.length() - 1); //make sure to remove braces here - mbuilder.addCode(str.substring(2, str.length() - 1).replace("return;", "break " + blockName + ";")); + mbuilder.addCode(str); //end scope - if(!returns) mbuilder.addCode("}\n"); + if(writeBlock) mbuilder.addCode("}\n"); } builder.addMethod(mbuilder.build()); @@ -194,7 +209,7 @@ public class EntityProcess extends BaseProcessor{ } Array getImports(Element elem){ - return Array.with(trees.getPath(elem).getCompilationUnit().getImports()).map(t -> t.toString()); + return Array.with(trees.getPath(elem).getCompilationUnit().getImports()).map(Object::toString); } /** @return interface for a component type */ diff --git a/core/src/mindustry/entities/def/EntityComps.java b/core/src/mindustry/entities/def/EntityComps.java new file mode 100644 index 0000000000..cb0cae7553 --- /dev/null +++ b/core/src/mindustry/entities/def/EntityComps.java @@ -0,0 +1,230 @@ +package mindustry.entities.def; + +import arc.graphics.*; +import arc.math.*; +import arc.math.geom.*; +import arc.struct.Bits; +import arc.struct.*; +import arc.util.*; +import arc.util.pooling.*; +import mindustry.annotations.Annotations.*; +import mindustry.content.*; +import mindustry.ctype.*; +import mindustry.entities.bullet.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.type.*; + +import java.io.*; + +import static mindustry.Vars.content; + +public class EntityComps{ + + @Depends({HealthComp.class, VelComp.class, StatusComp.class}) + class UnitComp{ + + } + + class OwnerComp{ + Entityc owner; + } + + class TimedComp extends EntityComp implements Scaled{ + float time, lifetime = 1f; + + void update(){ + time = Math.min(time + Time.delta(), lifetime); + + if(time >= lifetime){ + remove(); + } + } + + @Override + public float fin(){ + return time / lifetime; + } + } + + class HealthComp{ + float health, maxHealth; + boolean dead; + + float healthf(){ + return health / maxHealth; + } + } + + abstract class PosComp implements Position{ + float x, y; + + void set(float x, float y){ + this.x = x; + this.y = y; + } + } + + @Depends(PosComp.class) + class VelComp{ + //transient fields act as imports from any other component clases; these are ignored by the generator + transient float x, y; + + final Vec2 vel = new Vec2(); + + void update(){ + x += vel.x; + y += vel.y; + vel.scl(0.9f); + } + } + + @Depends(PosComp.class) + class HitboxComp{ + transient float x, y; + + float hitSize; + + boolean collides(Hitboxc other){ + return Intersector.overlapsRect(x - hitSize/2f, y - hitSize/2f, hitSize, hitSize, + other.getX() - other.getHitSize()/2f, other.getY() - other.getHitSize()/2f, other.getHitSize(), other.getHitSize()); + } + } + + @Depends(PosComp.class) + class StatusComp{ + private Array statuses = new Array<>(); + private Bits applied = new Bits(content.getBy(ContentType.status).size); + + private float speedMultiplier; + private float damageMultiplier; + private float armorMultiplier; + + void apply(StatusEffect effect, float duration){ + if(effect == StatusEffects.none || effect == null || isImmune(effect)) return; //don't apply empty or immune effects + + if(statuses.size > 0){ + //check for opposite effects + for(StatusEntry entry : statuses){ + //extend effect + if(entry.effect == effect){ + entry.time = Math.max(entry.time, duration); + return; + }else if(entry.effect.reactsWith(effect)){ //find opposite + StatusEntry.tmp.effect = entry.effect; + //TODO unit cannot be null here + entry.effect.getTransition(null, effect, entry.time, duration, StatusEntry.tmp); + entry.time = StatusEntry.tmp.time; + + if(StatusEntry.tmp.effect != entry.effect){ + entry.effect = StatusEntry.tmp.effect; + } + + //stop looking when one is found + return; + } + } + } + + //otherwise, no opposites found, add direct effect + StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); + entry.set(effect, duration); + statuses.add(entry); + } + + boolean isImmune(StatusEffect effect){ + return false; + } + + Color getStatusColor(){ + if(statuses.size == 0){ + return Tmp.c1.set(Color.white); + } + + float r = 0f, g = 0f, b = 0f; + for(StatusEntry entry : statuses){ + r += entry.effect.color.r; + g += entry.effect.color.g; + b += entry.effect.color.b; + } + return Tmp.c1.set(r / statuses.size, g / statuses.size, b / statuses.size, 1f); + } + + void update(){ + applied.clear(); + speedMultiplier = damageMultiplier = armorMultiplier = 1f; + + if(statuses.isEmpty()) return; + + statuses.eachFilter(entry -> { + entry.time = Math.max(entry.time - Time.delta(), 0); + applied.set(entry.effect.id); + + if(entry.time <= 0){ + Pools.free(entry); + return true; + }else{ + speedMultiplier *= entry.effect.speedMultiplier; + armorMultiplier *= entry.effect.armorMultiplier; + damageMultiplier *= entry.effect.damageMultiplier; + //TODO unit can't be null + entry.effect.update(null, entry.time); + } + + return false; + }); + } + + boolean hasEffect(StatusEffect effect){ + return applied.get(effect.id); + } + + void writeSave(DataOutput stream) throws IOException{ + stream.writeByte(statuses.size); + for(StatusEntry entry : statuses){ + stream.writeByte(entry.effect.id); + stream.writeFloat(entry.time); + } + } + + void readSave(DataInput stream, byte version) throws IOException{ + for(StatusEntry effect : statuses){ + Pools.free(effect); + } + + statuses.clear(); + + byte amount = stream.readByte(); + for(int i = 0; i < amount; i++){ + byte id = stream.readByte(); + float time = stream.readFloat(); + StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); + entry.set(content.getByID(ContentType.status, id), time); + statuses.add(entry); + } + } + } + + class BulletComp{ + BulletType bullet; + + void init(){ + bullet.init(); + } + } + + @BaseComponent + class EntityComp{ + int id; + + void init(){} + + void update(){} + + void remove(){} + + T as(Class type){ + return (T)this; + } + } +} diff --git a/core/src/mindustry/entities/def/EntityDefs.java b/core/src/mindustry/entities/def/EntityDefs.java index df262e216b..d538cec4a8 100644 --- a/core/src/mindustry/entities/def/EntityDefs.java +++ b/core/src/mindustry/entities/def/EntityDefs.java @@ -1,220 +1,10 @@ package mindustry.entities.def; -import arc.graphics.*; -import arc.math.geom.*; -import arc.struct.Bits; -import arc.struct.*; -import arc.util.*; -import arc.util.pooling.*; import mindustry.annotations.Annotations.*; -import mindustry.content.*; -import mindustry.ctype.*; -import mindustry.entities.bullet.*; -import mindustry.entities.units.*; -import mindustry.gen.*; -import mindustry.net.*; -import mindustry.type.*; - -import java.io.*; - -import static mindustry.Vars.content; +import mindustry.entities.def.EntityComps.*; class EntityDefs{ - @EntityDef({UnitComp.class, ConnectionComp.class}) - class PlayerDef{} - - @EntityDef({BulletComp.class, VelComp.class}) + @EntityDef({BulletComp.class, VelComp.class, TimedComp.class}) class BulletDef{} - - @Depends({HealthComp.class, VelComp.class, StatusComp.class}) - class UnitComp{ - - } - - class HealthComp{ - float health, maxHealth; - boolean dead; - - float healthf(){ - return health / maxHealth; - } - } - - abstract class PosComp implements Position{ - float x, y; - - void set(float x, float y){ - this.x = x; - this.y = y; - } - } - - @Depends(PosComp.class) - class VelComp{ - //transient fields act as imports from any other component clases; these are ignored by the generator - transient float x, y; - - final Vec2 vel = new Vec2(); - - void update(){ - x += vel.x; - y += vel.y; - vel.scl(0.9f); - } - } - - @Depends(PosComp.class) - class HitboxComp{ - transient float x, y; - - float hitSize; - - boolean collides(Hitboxc other){ - return Intersector.overlapsRect(x - hitSize/2f, y - hitSize/2f, hitSize, hitSize, - other.getX() - other.getHitSize()/2f, other.getY() - other.getHitSize()/2f, other.getHitSize(), other.getHitSize()); - } - } - - @Depends(PosComp.class) - class StatusComp{ - private Array statuses = new Array<>(); - private Bits applied = new Bits(content.getBy(ContentType.status).size); - - private float speedMultiplier; - private float damageMultiplier; - private float armorMultiplier; - - void apply(StatusEffect effect, float duration){ - if(effect == StatusEffects.none || effect == null || isImmune(effect)) return; //don't apply empty or immune effects - - if(statuses.size > 0){ - //check for opposite effects - for(StatusEntry entry : statuses){ - //extend effect - if(entry.effect == effect){ - entry.time = Math.max(entry.time, duration); - return; - }else if(entry.effect.reactsWith(effect)){ //find opposite - StatusEntry.tmp.effect = entry.effect; - //TODO unit cannot be null here - entry.effect.getTransition(null, effect, entry.time, duration, StatusEntry.tmp); - entry.time = StatusEntry.tmp.time; - - if(StatusEntry.tmp.effect != entry.effect){ - entry.effect = StatusEntry.tmp.effect; - } - - //stop looking when one is found - return; - } - } - } - - //otherwise, no opposites found, add direct effect - StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); - entry.set(effect, duration); - statuses.add(entry); - } - - boolean isImmune(StatusEffect effect){ - return false; - } - - Color getStatusColor(){ - if(statuses.size == 0){ - return Tmp.c1.set(Color.white); - } - - float r = 0f, g = 0f, b = 0f; - for(StatusEntry entry : statuses){ - r += entry.effect.color.r; - g += entry.effect.color.g; - b += entry.effect.color.b; - } - return Tmp.c1.set(r / statuses.size, g / statuses.size, b / statuses.size, 1f); - } - - void update(){ - applied.clear(); - speedMultiplier = damageMultiplier = armorMultiplier = 1f; - - if(statuses.isEmpty()) return; - - statuses.eachFilter(entry -> { - entry.time = Math.max(entry.time - Time.delta(), 0); - applied.set(entry.effect.id); - - if(entry.time <= 0){ - Pools.free(entry); - return true; - }else{ - speedMultiplier *= entry.effect.speedMultiplier; - armorMultiplier *= entry.effect.armorMultiplier; - damageMultiplier *= entry.effect.damageMultiplier; - //TODO unit can't be null - entry.effect.update(null, entry.time); - } - - return false; - }); - } - - boolean hasEffect(StatusEffect effect){ - return applied.get(effect.id); - } - - void writeSave(DataOutput stream) throws IOException{ - stream.writeByte(statuses.size); - for(StatusEntry entry : statuses){ - stream.writeByte(entry.effect.id); - stream.writeFloat(entry.time); - } - } - - void readSave(DataInput stream, byte version) throws IOException{ - for(StatusEntry effect : statuses){ - Pools.free(effect); - } - - statuses.clear(); - - byte amount = stream.readByte(); - for(int i = 0; i < amount; i++){ - byte id = stream.readByte(); - float time = stream.readFloat(); - StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); - entry.set(content.getByID(ContentType.status, id), time); - statuses.add(entry); - } - } - } - - class ConnectionComp{ - NetConnection connection; - } - - class BulletComp{ - BulletType bullet; - - void init(){ - bullet.init(); - } - } - - @BaseComponent - class EntityComp{ - int id; - - void init(){} - - T as(Class type){ - return (T)this; - } - } - - static void testing(){ - Entityc abullet = new BulletGen(); - Entityc aplayer = new PlayerGen(); - } }