This commit is contained in:
Anuken
2020-02-02 16:44:51 -05:00
parent ad9dd83032
commit ae6d33cad1
3 changed files with 255 additions and 220 deletions

View File

@@ -41,21 +41,27 @@ public class EntityProcess extends BaseProcessor{
//find all components used... //find all components used...
for(Stype type : allDefs){ for(Stype type : allDefs){
imports.addAll(getImports(type.e));
allComponents.addAll(allComponents(type)); allComponents.addAll(allComponents(type));
} }
//add all components w/ dependencies //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 //create component interfaces
for(Stype component : allComponents){ for(Stype component : allComponents){
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)).addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class); 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()){ for(Stype extraInterface : component.interfaces()){
inter.addSuperinterface(extraInterface.mirror()); inter.addSuperinterface(extraInterface.mirror());
} }
//implement super interfaces
Array<Stype> depends = getDependencies(component); Array<Stype> depends = getDependencies(component);
for(Stype type : depends){ for(Stype type : depends){
inter.addSuperinterface(ClassName.get(packageName, interfaceName(type))); inter.addSuperinterface(ClassName.get(packageName, interfaceName(type)));
@@ -100,7 +106,7 @@ public class EntityProcess extends BaseProcessor{
Array<Svar> fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT)); Array<Svar> fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT));
for(Svar f : fields){ for(Svar f : fields){
VariableTree tree = f.tree(); 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 //add initializer if it exists
if(tree.getInitializer() != null){ if(tree.getInitializer() != null){
fbuilder.initializer(tree.getInitializer().toString()); fbuilder.initializer(tree.getInitializer().toString());
@@ -128,13 +134,15 @@ public class EntityProcess extends BaseProcessor{
mbuilder.addParameter(var.tname(), var.name()); 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){ for(Smethod elem : entry.value){
//get all statements in the method, copy them over //get all statements in the method, copy them over
MethodTree methodTree = elem.tree(); MethodTree methodTree = elem.tree();
BlockTree blockTree = methodTree.getBody(); BlockTree blockTree = methodTree.getBody();
String str = blockTree.toString(); String str = blockTree.toString();
//name for code blocks in the methods
String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", ""); String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", "");
//skip empty blocks //skip empty blocks
@@ -143,13 +151,20 @@ public class EntityProcess extends BaseProcessor{
} }
//wrap scope to prevent variable leakage //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 //make sure to remove braces here
mbuilder.addCode(str.substring(2, str.length() - 1).replace("return;", "break " + blockName + ";")); mbuilder.addCode(str);
//end scope //end scope
if(!returns) mbuilder.addCode("}\n"); if(writeBlock) mbuilder.addCode("}\n");
} }
builder.addMethod(mbuilder.build()); builder.addMethod(mbuilder.build());
@@ -194,7 +209,7 @@ public class EntityProcess extends BaseProcessor{
} }
Array<String> getImports(Element elem){ Array<String> 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 */ /** @return interface for a component type */

View File

@@ -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<StatusEntry> 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> T as(Class<T> type){
return (T)this;
}
}
}

View File

@@ -1,220 +1,10 @@
package mindustry.entities.def; 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.annotations.Annotations.*;
import mindustry.content.*; import mindustry.entities.def.EntityComps.*;
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;
class EntityDefs{ class EntityDefs{
@EntityDef({UnitComp.class, ConnectionComp.class}) @EntityDef({BulletComp.class, VelComp.class, TimedComp.class})
class PlayerDef{}
@EntityDef({BulletComp.class, VelComp.class})
class BulletDef{} 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<StatusEntry> 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> T as(Class<T> type){
return (T)this;
}
}
static void testing(){
Entityc abullet = new BulletGen();
Entityc aplayer = new PlayerGen();
}
} }