Implemented basic block explosion modeling

This commit is contained in:
Anuken
2018-04-15 15:49:50 -04:00
parent 01fc7108d8
commit 6007e511ba
21 changed files with 199 additions and 70 deletions

View File

@@ -22,7 +22,7 @@ public class TurretBullets {
public static final BulletType
basicIron = new BulletType(3f, 0) {
basicIron = new BulletType(3f, 5) {
@Override
public void draw(Bullet b) {
drawBullet(Palette.bulletYellow, Palette.bulletYellowBack,

View File

@@ -46,6 +46,29 @@ public class ExplosionFx {
Lines.lineAngle(e.x + x, e.y + y, Mathf.atan2(x, y), 1f + e.fout()*3f);
});
Draw.reset();
}),
blockExplosion = new Effect(30, e -> {
e.scaled(7, i -> {
Lines.stroke(3f * i.fout());
Lines.circle(e.x, e.y, 3f + i.fin()*10f);
});
Draw.color(Color.GRAY);
Angles.randLenVectors(e.id, 6, 2f + 19f * e.finpow(), (x, y) ->{
Fill.circle(e.x + x, e.y + y, e.fout()*3f + 0.5f);
Fill.circle(e.x + x/2f, e.y + y/2f, e.fout()*1f);
});
Draw.color(Palette.lighterOrange, Palette.lightOrange, Color.GRAY, e.fin());
Lines.stroke(1.5f * e.fout());
Angles.randLenVectors(e.id + 1, 8, 1f + 23f * e.finpow(), (x, y) -> {
Lines.lineAngle(e.x + x, e.y + y, Mathf.atan2(x, y), 1f + e.fout()*3f);
});
Draw.reset();
});
}

View File

@@ -86,10 +86,10 @@ public class TileEntity extends Entity{
damage(other.getDamage());
}
public void damage(int damage){
public void damage(float damage){
if(dead) return;
int amount = tile.block().handleDamage(tile, damage);
float amount = tile.block().handleDamage(tile, damage);
health -= amount;
if(health <= 0) onDeath();

View File

@@ -66,18 +66,19 @@ public class DamageArea{
}
/**Damages everything in a radius.*/
public static void damage(float x, float y, float radius, int damage){
public static void damage(float x, float y, float radius, float damage){
damage(null, x, y, radius, damage);
}
/**Damages all entities and blocks in a radius that are enemies of the team.*/
public static void damage(Team team, float x, float y, float radius, int damage){
public static void damage(Team team, float x, float y, float radius, float damage){
Consumer<Unit> cons = entity -> {
if(entity.distanceTo(x, y) > radius){
return;
}
int amount = calculateDamage(x, y, entity.x, entity.y, radius, damage);
float amount = calculateDamage(x, y, entity.x, entity.y, radius, damage);
entity.damage(amount);
entity.velocity.add(tr.set(entity.x - x, entity.y - y).setLength(Mathf.clamp(damage/2f, 0, 6)));
};
rect.setSize(radius *2).setCenter(x, y);
@@ -92,7 +93,7 @@ public class DamageArea{
for(int dy= -trad; dy <= trad; dy ++){
Tile tile = world.tile(Mathf.scl2(x, tilesize) + dx, Mathf.scl2(y, tilesize) + dy);
if(tile != null && tile.entity != null && (team == null || state.teams.areEnemies(team, tile.getTeam())) && Vector2.dst(dx, dy, 0, 0) <= trad){
int amount = calculateDamage(x, y, tile.worldx(), tile.worldy(), radius, damage);
float amount = calculateDamage(x, y, tile.worldx(), tile.worldy(), radius, damage);
tile.entity.damage(amount);
}
}
@@ -100,9 +101,9 @@ public class DamageArea{
}
private static int calculateDamage(float x, float y, float tx, float ty, float radius, int damage){
private static float calculateDamage(float x, float y, float tx, float ty, float radius, float damage){
float dist = Vector2.dst(x, y, tx, ty);
float scaled = 1f - dist/radius;
return (int)(damage * scaled);
return damage * scaled;
}
}

View File

@@ -0,0 +1,13 @@
package io.anuke.mindustry.entities.effect;
import io.anuke.ucore.entities.TimedEntity;
import static io.anuke.mindustry.Vars.effectGroup;
public class Fire extends TimedEntity {
@Override
public Fire add(){
return add(effectGroup);
}
}

View File

@@ -0,0 +1,47 @@
package io.anuke.mindustry.entities.effect;
import com.badlogic.gdx.graphics.Color;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.ucore.entities.TimedEntity;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.effectGroup;
public class Fireball extends TimedEntity {
private float rotation;
private float speed = 0.3f;
private Color color;
public Fireball(float x, float y, Color color, float rotation){
set(x, y);
this.rotation = rotation;
this.color = color;
lifetime = 70f;
speed += Mathf.random(1f);
}
@Override
public void update() {
super.update();
float speed = this.speed - fin()*0.1f;
x += Angles.trnsx(rotation, speed);
y += Angles.trnsy(rotation, speed);
}
@Override
public void draw() {
Draw.color(Palette.lightFlame, color, Color.GRAY, fin());
Fill.circle(x, y, 3f * fout() + 0.5f);
Draw.reset();
}
@Override
public Fireball add(){
super.update();
return add(effectGroup);
}
}

View File

@@ -25,6 +25,8 @@ public class Lightning extends TimedEntity {
private Array<Vector2> lines = new Array<>();
private float angle;
public Color color = Palette.lancerLaser;
public Lightning(Team team, Effect effect, int damage, float x, float y, float targetAngle, int length){
this.x = x;
this.y = y;
@@ -83,7 +85,7 @@ public class Lightning extends TimedEntity {
@Override
public void draw() {
float lx = x, ly = y;
Draw.color(Palette.lancerLaser, Color.WHITE, fin());
Draw.color(color, Color.WHITE, fin());
for(int i = 0; i < lines.size; i ++){
Vector2 v = lines.get(i);
Lines.stroke(fout() * 3f + 1f-(float)i/lines.size);

View File

@@ -102,6 +102,7 @@ public abstract class UnitType {
public void onDeath(BaseUnit unit){
//TODO other things, such as enemies?
Effects.effect(ExplosionFx.explosion, unit);
Effects.shake(2f, 2f, unit);
if(Net.server()){
NetEvents.handleUnitDeath(unit);

View File

@@ -15,6 +15,8 @@ public class TeamInfo {
enemies = new ObjectSet<>();
private ObjectSet<TeamData> allyData = new ObjectSet<>(),
enemyData = new ObjectSet<>();
private ObjectSet<TeamData> allTeamData = new ObjectSet<>();
private ObjectSet<Team> allTeams = new ObjectSet<>();
public ObjectSet<TeamData> getTeams(boolean ally) {
return ally ? allyData : enemyData;
@@ -38,6 +40,9 @@ public class TeamInfo {
enemyData.add(data);
}
allTeamData.add(data);
allTeams.add(team);
map.put(team, data);
}
@@ -59,8 +64,8 @@ public class TeamInfo {
boolean ally = allies.contains(team);
boolean enemy = enemies.contains(team);
//this team isn't even in the game, so target nothing!
if(!ally && !enemy) return empty;
//this team isn't even in the game, so target everything!
if(!ally && !enemy) return allTeams;
return ally ? enemies : allies;
}
@@ -72,8 +77,8 @@ public class TeamInfo {
boolean ally = allies.contains(team);
boolean enemy = enemies.contains(team);
//this team isn't even in the game, so target nothing!
if(!ally && !enemy) return emptyData;
//this team isn't even in the game, so target everything!
if(!ally && !enemy) return allTeamData;
return ally ? enemyData : allyData;
}
@@ -83,7 +88,7 @@ public class TeamInfo {
if(team == other) return false; //fast fail to be more efficient
boolean ally = allies.contains(team);
boolean ally2 = enemies.contains(other);
return ally == ally2;
return (ally == ally2) || !allTeams.contains(team); //if it's not in the game, target everything.
}
public class TeamData {

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.resource;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Bundles;
@@ -25,6 +26,8 @@ public class Item implements Comparable<Item>{
public boolean material = true;
/**drill hardness of the item*/
public int hardness = 0;
/**the burning color of this item*/
public Color flameColor = Palette.darkFlame.cpy();
public Item(String name, Color color) {
this.id = items.size;

View File

@@ -22,6 +22,8 @@ public class Liquid {
public float viscosity = 0.5f;
/**how prone to exploding this liquid is, when heated. 0 = nothing, 1 = nuke*/
public float explosiveness;
/**the burning color of this liquid*/
public Color flameColor = Color.valueOf("ffb763");
public Liquid(String name, Color color) {
this.name = name;

View File

@@ -79,33 +79,6 @@ public class BlockInventoryFragment implements Fragment {
table.margin(3f);
table.defaults().size(16 * 2).space(6f);
/*
if(tile.block().hasPower){
table.add(new ItemImage(Draw.region("icon-power"), () -> round(tile.entity.power.amount), Colors.get("power")));
if (row++ % cols == cols - 1) table.row();
}
if(tile.block().hasLiquids){
ItemImage image = new ItemImage(Draw.region("icon-liquid"),
() -> round(tile.entity.liquid.amount),
tile.entity.liquid.liquid == Liquids.none ? Color.GRAY : tile.entity.liquid.liquid.color);
image.addListener(new HandCursorListener());
image.tapped(() -> {
if (tile.entity.liquid.amount > 0) {
/*
int amount = Inputs.keyDown("item_withdraw") ? items[f] : 1;
items[f] -= amount;
move(item.region, image, () -> player.inventory.addItem(item, amount), Color.WHITE);
}
});
table.add(image);
if (row++ % cols == cols - 1) table.row();
}*/
if(tile.block().hasInventory) {
int[] items = tile.entity.inventory.items;

View File

@@ -51,6 +51,8 @@ public class DebugFragment implements Fragment {
row();
new button("noclip", "toggle", () -> noclip = !noclip);
row();
new button("team", "toggle", () -> player.team = (player.team == Team.blue ? Team.red : Team.blue));
row();
new button("blocks", "toggle", () -> showBlockDebug = !showBlockDebug);
row();
new button("effect", () -> Effects.effect(BlockFx.teleport, player));

View File

@@ -1,23 +1,32 @@
package io.anuke.mindustry.world;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.DamageArea;
import io.anuke.mindustry.entities.effect.Fireball;
import io.anuke.mindustry.entities.effect.Lightning;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.DrawLayer;
import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.resource.ItemStack;
import io.anuke.mindustry.resource.Liquid;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Hue;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
@@ -32,7 +41,8 @@ public class Block extends BaseBlock {
private static ObjectMap<String, Block> map = new ObjectMap<>();
protected Array<Tile> tempTiles = new Array<>();
protected Vector2 offset = new Vector2();
protected Vector2 tempVector = new Vector2();
protected Color tempColor = new Color();
/**internal name*/
public final String name;
@@ -40,10 +50,6 @@ public class Block extends BaseBlock {
public final int id;
/**display name*/
public final String formalName;
/**played on destroy*/
public Effect explosionEffect = ExplosionFx.explosion; //TODO better explosion effect
/**played on destroy*/
public String explosionSound = "break";
/**whether this block has a tile entity that updates*/
public boolean update;
/**whether this block has health and can be destroyed*/
@@ -66,6 +72,8 @@ public class Block extends BaseBlock {
public float breaktime = 18;
/**tile entity health*/
public int health = 40;
/**base block explosiveness*/
public float baseExplosiveness = 0f;
/**the shadow drawn under the block*/
public String shadow = "shadow";
/**whether to display a different shadow per variant*/
@@ -89,7 +97,7 @@ public class Block extends BaseBlock {
/**Draw layer. Only used for 'cached' rendering.*/
public DrawLayer drawLayer = DrawLayer.normal;
/**Layer to draw extra stuff on.*/
public io.anuke.mindustry.graphics.Layer layer = null;
public Layer layer = null;
/**Extra layer to draw extra extra stuff on.*/
public Layer layer2 = null;
/**whether this block can be replaced in all cases*/
@@ -189,7 +197,7 @@ public class Block extends BaseBlock {
return (other != this || rotate) && this.group != BlockGroup.none && other.group == this.group;
}
public int handleDamage(Tile tile, int amount){
public float handleDamage(Tile tile, float amount){
return amount;
}
@@ -201,10 +209,69 @@ public class Block extends BaseBlock {
public void onDestroyed(Tile tile){
float x = tile.worldx(), y = tile.worldy();
Effects.shake(4f, 4f, x, y);
Effects.effect(explosionEffect, x, y);
Effects.sound(explosionSound, x, y);
float explosiveness = baseExplosiveness;
float flammability = 0f;
float heat = 0f;
float power = 0f;
int units = 1;
tempColor.set(Color.WHITE);
if(hasInventory){
for(Item item : Item.getAllItems()){
int amount = tile.entity.inventory.getItem(item);
explosiveness += item.explosiveness*amount;
flammability += item.flammability*amount;
if(item.flammability*amount > 0.5){
units ++;
Hue.addu(tempColor, item.flameColor);
}
}
}
if(hasLiquids){
float amount = tile.entity.liquid.amount;
explosiveness += tile.entity.liquid.liquid.explosiveness*amount/2f;
flammability += tile.entity.liquid.liquid.flammability*amount/2f;
heat += Mathf.clamp(tile.entity.liquid.liquid.temperature-0.5f)*amount/2f;
if(tile.entity.liquid.liquid.flammability*amount > 2f){
units ++;
Hue.addu(tempColor, tile.entity.liquid.liquid.flameColor);
}
}
if(hasPower){
power += tile.entity.power.amount;
}
tempColor.mul(1f/units);
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i ++){
int branches = 5 + Mathf.clamp((int)(power/30), 1, 20);
Timers.run(i*2f + Mathf.random(4f), () -> {
Lightning l = new Lightning(Team.none, Fx.none, 3, x, y, Mathf.random(360f), branches + Mathf.range(2));
l.color = Colors.get("power");
l.add();
});
}
for(int i = 0; i < Mathf.clamp(flammability / 20, 0, 20); i ++){
Timers.run(i, () -> {
Fireball f = new Fireball(x, y, Mathf.choose(tempColor, Color.LIGHT_GRAY), Mathf.random(360f));
f.add();
});
}
if(explosiveness > 15f){
Effects.effect(ExplosionFx.shockwave, x, y);
}
float shake = Math.min(explosiveness/4f + 3f, 9f);
Effects.shake(shake, shake, x, y);
Effects.effect(ExplosionFx.blockExplosion, x, y);
DamageArea.damage(x, y, Mathf.clamp(size * tilesize + explosiveness, 0, 60f), 5 + explosiveness);
}
public TextureRegion[] getIcon(){
@@ -269,7 +336,7 @@ public class Block extends BaseBlock {
/**Offset for placing and drawing multiblocks.*/
public Vector2 getPlaceOffset(){
return offset.set(((size + 1) % 2) * tilesize/2, ((size + 1) % 2) * tilesize/2);
return tempVector.set(((size + 1) % 2) * tilesize/2, ((size + 1) % 2) * tilesize/2);
}
public boolean isMultiblock(){

View File

@@ -23,7 +23,7 @@ public class ShieldedWallBlock extends PowerBlock{
}
@Override
public int handleDamage(Tile tile, int amount){
public float handleDamage(Tile tile, float amount){
float drain = amount * powerPerDamage;
ShieldedWallEntity entity = tile.entity();

View File

@@ -46,7 +46,6 @@ public class NuclearReactor extends LiquidBurnerGenerator {
generateItem = Items.thorium;
itemCapacity = 30;
liquidCapacity = 50;
explosionEffect = ExplosionFx.nuclearShockwave;
powerCapacity = 80f;
}
@@ -130,7 +129,7 @@ public class NuclearReactor extends LiquidBurnerGenerator {
if(fuel < 5 && entity.heat < 0.5f) return;
Effects.shake(6f, 16f, tile.worldx(), tile.worldy());
Effects.effect(explosionEffect, tile.worldx(), tile.worldy());
Effects.effect(ExplosionFx.nuclearShockwave, tile.worldx(), tile.worldy());
for(int i = 0; i < 6; i ++){
Timers.run(Mathf.random(40), () -> {
Effects.effect(BlockFx.nuclearcloud, tile.worldx(), tile.worldy());

View File

@@ -1,18 +1,17 @@
package io.anuke.mindustry.world.blocks.types.power;
import com.badlogic.gdx.math.GridPoint2;
import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.world.Edges;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.PowerBlock;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
public class PowerGenerator extends PowerBlock {
public PowerGenerator(String name) {
super(name);
baseExplosiveness = 5f;
}
protected void distributePower(Tile tile){
@@ -56,14 +55,6 @@ public class PowerGenerator extends PowerBlock {
distributePower(tile);
}
@Override
public void onDestroyed(Tile tile){
float x = tile.worldx(), y = tile.worldy();
Effects.effect(BlockFx.blastsmoke, x, y);
//TODO better effect
}
@Override
public TileEntity getEntity() {
return new GeneratorEntity();