Effects, collisions, method overrides

This commit is contained in:
Anuken
2020-02-08 13:14:23 -05:00
parent 659bfea8cf
commit f46be924b9
20 changed files with 217 additions and 53 deletions

View File

@@ -7,6 +7,7 @@ public class Annotations{
public enum DrawLayer{ public enum DrawLayer{
floor, floor,
floorOver,
groundShadows, groundShadows,
groundUnder, groundUnder,
ground, ground,
@@ -17,6 +18,12 @@ public class Annotations{
names, names,
} }
/** Indicates that a method overrides other methods. */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Replace{
}
/** Indicates that a component field is read-only. */ /** Indicates that a component field is read-only. */
@Target({ElementType.FIELD, ElementType.METHOD}) @Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)

View File

@@ -6,6 +6,7 @@ import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.io.*; import arc.util.io.*;
import arc.util.pooling.*; import arc.util.pooling.*;
import arc.util.pooling.Pool.*;
import com.squareup.javapoet.*; import com.squareup.javapoet.*;
import com.squareup.javapoet.TypeSpec.*; import com.squareup.javapoet.TypeSpec.*;
import com.sun.source.tree.*; import com.sun.source.tree.*;
@@ -216,6 +217,7 @@ public class EntityProcess extends BaseProcessor{
Array<Stype> components = allComponents(type); Array<Stype> components = allComponents(type);
Array<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !g.components.contains(s -> !components.contains(s))) || g.manualInclusions.contains(type)); Array<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !g.components.contains(s -> !components.contains(s))) || g.manualInclusions.contains(type));
ObjectMap<String, Array<Smethod>> methods = new ObjectMap<>(); ObjectMap<String, Array<Smethod>> methods = new ObjectMap<>();
ObjectMap<FieldSpec, Svar> specVariables = new ObjectMap<>();
//add all components //add all components
for(Stype comp : components){ for(Stype comp : components){
@@ -237,6 +239,7 @@ public class EntityProcess extends BaseProcessor{
if(!isFinal) fbuilder.addModifiers(Modifier.PROTECTED); if(!isFinal) fbuilder.addModifiers(Modifier.PROTECTED);
fbuilder.addAnnotations(f.annotations().map(AnnotationSpec::get)); fbuilder.addAnnotations(f.annotations().map(AnnotationSpec::get));
builder.addField(fbuilder.build()); builder.addField(fbuilder.build());
specVariables.put(builder.fieldSpecs.get(builder.fieldSpecs.size() - 1), f);
} }
//get all utility methods from components //get all utility methods from components
@@ -245,8 +248,30 @@ public class EntityProcess extends BaseProcessor{
} }
} }
//override toString method
builder.addMethod(MethodSpec.methodBuilder("toString")
.addAnnotation(Override.class)
.returns(String.class)
.addModifiers(Modifier.PUBLIC)
.addStatement("return $S + $L", name + "#", "id").build());
//add all methods from components //add all methods from components
for(ObjectMap.Entry<String, Array<Smethod>> entry : methods){ for(ObjectMap.Entry<String, Array<Smethod>> entry : methods){
if(entry.value.contains(m -> m.has(Replace.class))){
//check replacements
if(entry.value.count(m -> m.has(Replace.class)) > 1){
err("Type " + type + " has multiple components replacing method " + entry.key + ".");
}
Smethod base = entry.value.find(m -> m.has(Replace.class));
entry.value.clear();
entry.value.add(base);
}
//check multi return
if(entry.value.count(m -> !m.isAny(Modifier.NATIVE, Modifier.ABSTRACT) && !m.isVoid()) > 1){
err("Type " + type + " has multiple components implementing non-void method " + entry.key + ".");
}
entry.value.sort(m -> m.has(MethodPriority.class) ? m.annotation(MethodPriority.class).value() : 0); entry.value.sort(m -> m.has(MethodPriority.class) ? m.annotation(MethodPriority.class).value() : 0);
//representative method //representative method
@@ -275,6 +300,14 @@ public class EntityProcess extends BaseProcessor{
err(entry.value.first().up().getSimpleName() + "#" + entry.value.first() + " is an abstract method and must be implemented in some component", type); err(entry.value.first().up().getSimpleName() + "#" + entry.value.first() + " is an abstract method and must be implemented in some component", type);
} }
//SPECIAL CASE: inject group add/remove code
if(first.name().equals("add") || first.name().equals("remove")){
for(GroupDefinition def : groups){
//remove/add from each group, assume imported
mbuilder.addStatement("Groups.$L.$L(this)", def.name, first.name());
}
}
for(Smethod elem : entry.value){ for(Smethod elem : entry.value){
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE) || !methodBlocks.containsKey(elem)) continue; if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE) || !methodBlocks.containsKey(elem)) continue;
@@ -298,14 +331,6 @@ public class EntityProcess extends BaseProcessor{
//trim block //trim block
str = str.substring(2, str.length() - 1); str = str.substring(2, str.length() - 1);
//SPECIAL CASE: inject group add/remove code
if(elem.name().equals("add") || elem.name().equals("remove")){
for(GroupDefinition def : groups){
//remove/add from each group, assume imported
mbuilder.addStatement("Groups.$L.$L(this)", def.name, elem.name());
}
}
//make sure to remove braces here //make sure to remove braces here
mbuilder.addCode(str); mbuilder.addCode(str);
@@ -313,6 +338,7 @@ public class EntityProcess extends BaseProcessor{
if(writeBlock) mbuilder.addCode("}\n"); if(writeBlock) mbuilder.addCode("}\n");
} }
//add free code to remove methods
if(first.name().equals("remove") && ann.pooled()){ if(first.name().equals("remove") && ann.pooled()){
mbuilder.addStatement("$T.free(this)", Pools.class); mbuilder.addStatement("$T.free(this)", Pools.class);
} }
@@ -320,6 +346,27 @@ public class EntityProcess extends BaseProcessor{
builder.addMethod(mbuilder.build()); builder.addMethod(mbuilder.build());
} }
//add pool reset method and implment Poolable
if(ann.pooled()){
builder.addSuperinterface(Poolable.class);
//implement reset()
MethodSpec.Builder resetBuilder = MethodSpec.methodBuilder("reset").addModifiers(Modifier.PUBLIC);
for(FieldSpec spec : builder.fieldSpecs){
Svar variable = specVariables.get(spec);
if(spec.type.isPrimitive()){
//set to primitive default
resetBuilder.addStatement("$L = $L", spec.name, varInitializers.containsKey(variable) ? varInitializers.get(variable) : getDefault(spec.type.toString()));
}else{
//set to default null
if(!varInitializers.containsKey(variable)){
resetBuilder.addStatement("$L = null", spec.name);
} //else... TODO reset if poolable
}
}
builder.addMethod(resetBuilder.build());
}
//make constructor private //make constructor private
builder.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED).build()); builder.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED).build());

View File

@@ -1,11 +1,12 @@
#Maps entity names to IDs. Autogenerated. #Maps entity names to IDs. Autogenerated.
dagger=7 dagger=7
mindustry.entities.def.AllEntities.GenericBuilderDef=6 mindustry.entities.AllEntities.GenericBuilderDef=6
mindustry.entities.def.AllEntities.BulletDef=0 mindustry.entities.AllEntities.BulletDef=0
mindustry.entities.def.AllEntities.PlayerDef=4 mindustry.entities.AllEntities.PlayerDef=4
mindustry.entities.AllEntities.GroundEffectDef=9
mindustry.entities.AllEntities.EffectDef=2
mindustry.entities.AllEntities.GenericUnitDef=5
mindustry.entities.AllEntities.TileDef=3
dagger2=8 dagger2=8
mindustry.entities.def.AllEntities.GenericUnitDef=5 mindustry.entities.AllEntities.DecalDef=1
mindustry.entities.def.AllEntities.EffectDef=2
mindustry.entities.def.AllEntities.DecalDef=1
mindustry.entities.def.AllEntities.TileDef=3

View File

@@ -1082,7 +1082,7 @@ public class Fx{
//TODO fix false in constructor //TODO fix false in constructor
ripple = new Effect(30, e -> { ripple = new Effect(30, e -> {
color(Tmp.c1.set(e.color).mul(1.2f)); color(Tmp.c1.set(e.color).mul(1.5f));
stroke(e.fout() + 0.4f); stroke(e.fout() + 0.4f);
Lines.circle(e.x, e.y, 2f + e.fin() * 4f); Lines.circle(e.x, e.y, 2f + e.fin() * 4f);
}).ground(), }).ground(),

View File

@@ -1,6 +1,7 @@
package mindustry.content; package mindustry.content;
import arc.*; import arc.*;
import arc.graphics.*;
import arc.math.Mathf; import arc.math.Mathf;
import mindustry.ctype.ContentList; import mindustry.ctype.ContentList;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
@@ -40,6 +41,7 @@ public class StatusEffects implements ContentList{
}}; }};
wet = new StatusEffect("wet"){{ wet = new StatusEffect("wet"){{
color = Color.royal;
speedMultiplier = 0.9f; speedMultiplier = 0.9f;
effect = Fx.wet; effect = Fx.wet;

View File

@@ -188,6 +188,7 @@ public class Renderer implements ApplicationListener{
blocks.floor.drawFloor(); blocks.floor.drawFloor();
Groups.drawFloor(); Groups.drawFloor();
Groups.drawFloorOver();
blocks.processBlocks(); blocks.processBlocks();
blocks.drawShadows(); blocks.drawShadows();

View File

@@ -1,4 +1,4 @@
package mindustry.entities.def; package mindustry.entities;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.gen.*; import mindustry.gen.*;
@@ -11,9 +11,12 @@ class AllEntities{
@EntityDef(value = {Tilec.class}, isFinal = false) @EntityDef(value = {Tilec.class}, isFinal = false)
class TileDef{} class TileDef{}
@EntityDef(value = {Effectc.class, Childc.class}, pooled = true) @EntityDef(value = {StandardEffectc.class, Childc.class}, pooled = true)
class EffectDef{} class EffectDef{}
@EntityDef(value = {GroundEffectc.class, Childc.class}, pooled = true)
class GroundEffectDef{}
@EntityDef({Decalc.class}) @EntityDef({Decalc.class})
class DecalDef{} class DecalDef{}

View File

@@ -39,11 +39,11 @@ public class Effects{
Rect pos = Tmp.r2.setSize(effect.size).setCenter(x, y); Rect pos = Tmp.r2.setSize(effect.size).setCenter(x, y);
if(view.overlaps(pos)){ if(view.overlaps(pos)){
EffectEntity entity = EffectEntity.create(); Effectc entity = effect.ground ? GroundEffectEntity.create() : EffectEntity.create();
entity.effect(effect); entity.effect(effect);
entity.rotation(rotation); entity.rotation(rotation);
entity.data(data); entity.data(data);
entity.id(EntityGroup.nextId()); entity.lifetime(effect.lifetime);
entity.set(x, y); entity.set(x, y);
entity.color().set(color); entity.color().set(color);
if(data instanceof Posc) entity.parent((Posc)data); if(data instanceof Posc) entity.parent((Posc)data);

View File

@@ -1,13 +1,12 @@
package mindustry.entities; package mindustry.entities;
import arc.struct.Array; import arc.math.*;
import arc.math.Mathf;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.world.Tile; import mindustry.world.*;
import static mindustry.Vars.tilesize; import static mindustry.Vars.*;
import static mindustry.Vars.world;
public class EntityCollisions{ public class EntityCollisions{
//range for tile collision scanning //range for tile collision scanning
@@ -26,12 +25,16 @@ public class EntityCollisions{
private Array<Hitboxc> arrOut = new Array<>(); private Array<Hitboxc> arrOut = new Array<>();
public void move(Hitboxc entity, float deltax, float deltay){ public void move(Hitboxc entity, float deltax, float deltay){
move(entity, deltax, deltay, EntityCollisions::solid);
}
public void move(Hitboxc entity, float deltax, float deltay, SolidPred solidCheck){
boolean movedx = false; boolean movedx = false;
while(Math.abs(deltax) > 0 || !movedx){ while(Math.abs(deltax) > 0 || !movedx){
movedx = true; movedx = true;
moveDelta(entity, Math.min(Math.abs(deltax), seg) * Mathf.sign(deltax), 0, true); moveDelta(entity, Math.min(Math.abs(deltax), seg) * Mathf.sign(deltax), 0, true, solidCheck);
if(Math.abs(deltax) >= seg){ if(Math.abs(deltax) >= seg){
deltax -= seg * Mathf.sign(deltax); deltax -= seg * Mathf.sign(deltax);
@@ -44,7 +47,7 @@ public class EntityCollisions{
while(Math.abs(deltay) > 0 || !movedy){ while(Math.abs(deltay) > 0 || !movedy){
movedy = true; movedy = true;
moveDelta(entity, 0, Math.min(Math.abs(deltay), seg) * Mathf.sign(deltay), false); moveDelta(entity, 0, Math.min(Math.abs(deltay), seg) * Mathf.sign(deltay), false, solidCheck);
if(Math.abs(deltay) >= seg){ if(Math.abs(deltay) >= seg){
deltay -= seg * Mathf.sign(deltay); deltay -= seg * Mathf.sign(deltay);
@@ -54,7 +57,7 @@ public class EntityCollisions{
} }
} }
public void moveDelta(Hitboxc entity, float deltax, float deltay, boolean x){ public void moveDelta(Hitboxc entity, float deltax, float deltay, boolean x, SolidPred solidCheck){
Rect rect = r1; Rect rect = r1;
entity.hitboxTile(rect); entity.hitboxTile(rect);
entity.hitboxTile(r2); entity.hitboxTile(r2);
@@ -66,7 +69,7 @@ public class EntityCollisions{
for(int dx = -r; dx <= r; dx++){ for(int dx = -r; dx <= r; dx++){
for(int dy = -r; dy <= r; dy++){ for(int dy = -r; dy <= r; dy++){
int wx = dx + tilex, wy = dy + tiley; int wx = dx + tilex, wy = dy + tiley;
if(solid(wx, wy)){ if(solidCheck.solid(wx, wy)){
tmp.setSize(tilesize).setCenter(wx * tilesize, wy * tilesize); tmp.setSize(tilesize).setCenter(wx * tilesize, wy * tilesize);
if(tmp.overlaps(rect)){ if(tmp.overlaps(rect)){
@@ -118,7 +121,12 @@ public class EntityCollisions{
}); });
} }
private static boolean solid(int x, int y){ public static boolean waterSolid(int x, int y){
Tile tile = world.tile(x, y);
return tile != null && (tile.solid() || !tile.floor().isLiquid);
}
public static boolean solid(int x, int y){
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
return tile != null && tile.solid(); return tile != null && tile.solid();
} }
@@ -221,4 +229,8 @@ public class EntityCollisions{
} }
}); });
} }
public interface SolidPred{
boolean solid(int x, int y);
}
} }

View File

@@ -6,12 +6,12 @@ import mindustry.annotations.Annotations.*;
import mindustry.gen.*; import mindustry.gen.*;
@Component @Component
abstract class DecalComp implements Drawc, Timedc, Rotc, Posc{ abstract class DecalComp implements Drawc, Timedc, Rotc, Posc, DrawLayerFloorc{
Color color = new Color(1, 1, 1, 1); Color color = new Color(1, 1, 1, 1);
TextureRegion region; TextureRegion region;
@Override @Override
public void draw(){ public void drawFloor(){
Draw.color(color); Draw.color(color);
Draw.rect(region, x(), y(), rotation()); Draw.rect(region, x(), y(), rotation());
Draw.color(); Draw.color();

View File

@@ -5,10 +5,5 @@ import mindustry.gen.*;
@Component @Component
abstract class DrawComp implements Posc{ abstract class DrawComp implements Posc{
abstract float clipSize(); abstract float clipSize();
void draw(){
}
} }

View File

@@ -6,13 +6,12 @@ import mindustry.entities.*;
import mindustry.gen.*; import mindustry.gen.*;
@Component @Component
abstract class EffectComp implements Posc, Drawc, Timedc, Rotc{ abstract class EffectComp implements Posc, Drawc, Timedc, Rotc, Childc{
Effect effect;
Color color = new Color(Color.white); Color color = new Color(Color.white);
Effect effect;
Object data; Object data;
@Override void draw(){
public void draw(){
effect.render(id(), color, time(), rotation(), x(), y(), data); effect.render(id(), color, time(), rotation(), x(), y(), data);
} }

View File

@@ -0,0 +1,22 @@
package mindustry.entities.def;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
import static mindustry.Vars.collisions;
@Component
abstract class ElevationMoveComp implements Velc, Posc, Flyingc, Hitboxc{
transient float x, y;
@Replace
@Override
public void move(float cx, float cy){
if(isFlying()){
x += cx;
y += cy;
}else{
collisions.move(this, cx, cy);
}
}
}

View File

@@ -16,6 +16,7 @@ abstract class FlyingComp implements Posc, Velc, Healthc{
float elevation; float elevation;
float drownTime; float drownTime;
float splashTimer;
boolean isGrounded(){ boolean isGrounded(){
return elevation < 0.001f; return elevation < 0.001f;
@@ -25,15 +26,22 @@ abstract class FlyingComp implements Posc, Velc, Healthc{
return elevation >= 0.001f; return elevation >= 0.001f;
} }
boolean canDrown(){
return isGrounded();
}
@Override @Override
public void update(){ public void update(){
Floor floor = floorOn(); Floor floor = floorOn();
if(isGrounded() && floor.isLiquid && vel.len2() > 0.4f*0.4f && Mathf.chance((vel.len2() * floor.speedMultiplier) * 0.03f * Time.delta())){ if(isGrounded() && floor.isLiquid && !vel.isZero(0.01f)){
floor.walkEffect.at(x, y, 0, floor.color); if((splashTimer += vel.len()) >= 7f){
floor.walkEffect.at(x, y, 0, floor.color);
splashTimer = 0f;
}
} }
if(isGrounded() && floor.isLiquid && floor.drownTime > 0){ if(canDrown() && floor.isLiquid && floor.drownTime > 0){
drownTime += Time.delta() * 1f / floor.drownTime; drownTime += Time.delta() * 1f / floor.drownTime;
drownTime = Mathf.clamp(drownTime); drownTime = Mathf.clamp(drownTime);
if(Mathf.chance(Time.delta() * 0.05f)){ if(Mathf.chance(Time.delta() * 0.05f)){

View File

@@ -0,0 +1,13 @@
package mindustry.entities.def;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
@Component
abstract class GroundEffectComp implements Effectc, DrawLayerFloorOverc{
@Override
public void drawFloorOver(){
draw();
}
}

View File

@@ -6,7 +6,7 @@ import mindustry.annotations.Annotations.*;
import mindustry.gen.*; import mindustry.gen.*;
@Component @Component
abstract class LegsComp implements Posc, Flyingc, Hitboxc, DrawLayerGroundUnderc, Unitc, Legsc{ abstract class LegsComp implements Posc, Flyingc, Hitboxc, DrawLayerGroundUnderc, Unitc, Legsc, ElevationMovec{
transient float x, y; transient float x, y;
float baseRotation, walkTime; float baseRotation, walkTime;

View File

@@ -0,0 +1,13 @@
package mindustry.entities.def;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
@Component
abstract class StandardEffectComp implements Effectc, DrawLayerEffectsc{
@Override
public void drawEffects(){
draw();
}
}

View File

@@ -73,13 +73,16 @@ abstract class StatusComp implements Posc, Flyingc{
return Tmp.c1.set(Color.white); return Tmp.c1.set(Color.white);
} }
float r = 0f, g = 0f, b = 0f; float r = 1f, g = 1f, b = 1f, total = 0f;
for(StatusEntry entry : statuses){ for(StatusEntry entry : statuses){
r += entry.effect.color.r; float intensity = entry.time < 10f ? entry.time/10f : 1f;
g += entry.effect.color.g; r += entry.effect.color.r * intensity;
b += entry.effect.color.b; g += entry.effect.color.g * intensity;
b += entry.effect.color.b * intensity;
total += intensity;
} }
return Tmp.c1.set(r / statuses.size, g / statuses.size, b / statuses.size, 1f); float count = statuses.size + total;
return Tmp.c1.set(r / count, g / count, b / count, 1f);
} }
@Override @Override
@@ -118,6 +121,8 @@ abstract class StatusComp implements Posc, Flyingc{
return applied.get(effect.id); return applied.get(effect.id);
} }
//TODO autogen io code
void writeSave(DataOutput stream) throws IOException{ void writeSave(DataOutput stream) throws IOException{
stream.writeByte(statuses.size); stream.writeByte(statuses.size);
for(StatusEntry entry : statuses){ for(StatusEntry entry : statuses){

View File

@@ -14,9 +14,12 @@ abstract class VelComp implements Posc{
@Override @Override
public void update(){ public void update(){
//TODO handle solidity move(vel.x, vel.y);
x += vel.x;
y += vel.y;
vel.scl(1f - drag * Time.delta()); vel.scl(1f - drag * Time.delta());
} }
void move(float cx, float cy){
x += cx;
y += cy;
}
} }

View File

@@ -0,0 +1,33 @@
package mindustry.entities.def;
import mindustry.annotations.Annotations.*;
import mindustry.entities.*;
import mindustry.gen.*;
import static mindustry.Vars.collisions;
//just a proof of concept
@Component
abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc{
transient float x, y;
@Replace
@Override
public void move(float cx, float cy){
if(isGrounded()){
if(!EntityCollisions.waterSolid(tileX(), tileY())){
collisions.move(this, cx, cy, EntityCollisions::waterSolid);
}
}else{
x += cx;
y += cy;
}
}
@Replace
@Override
public boolean canDrown(){
return false;
}
}