package net.mostlyoriginal.game.system;
import com.artemis.Aspect;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.annotations.Wire;
import com.artemis.systems.EntityProcessingSystem;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
import net.mostlyoriginal.api.component.graphics.ColorAnimation;
import net.mostlyoriginal.api.component.script.Schedule;
import net.mostlyoriginal.game.component.Bobbing;
import net.mostlyoriginal.game.component.CastleBlock;
import net.mostlyoriginal.game.component.ExpansionPoint;
import net.mostlyoriginal.game.manager.AssetSystem;
import net.mostlyoriginal.game.manager.EntityFactorySystem;
import net.mostlyoriginal.game.manager.ServantManager;
/**
* Keeps track of castle model, responsible for assembling and updating the castle.
*
* @author Daan van Yperen
*/
@Wire
public class CastleSystem extends EntityProcessingSystem {
public static final int H = 6;
public static final int W = 12;
private static final int BIRD_SPAWN_LEVEL = 3;
public static final int CASTLE_X_OFFSET = 85;
public CastleBlock.Type castle[][] = new CastleBlock.Type[H][W];
public CastleBlock.Type bestCastle[][] = new CastleBlock.Type[H][W];
public int blockCount = 0;
public int bestBlockCount = 0;
public int knightLevel = 0;
public int mageLevel = 0;
public int spelunkerLevel = 1; // we always have a spelunker.
public int queenLevel = 1; // we always have a queen.
public boolean castleDirty = true;
private ServantManager servantManager;
EntityFactorySystem entityFactorySystem;
ComponentMapper<CastleBlock> cm;
private Entity[][] actor;
private AssetSystem assetSystem;
public CastleSystem() {
super(Aspect.getAspectForAll(CastleBlock.class));
for ( int x=0;x<W;x++) {
for (int y = 0; y < H; y++) {
castle[y][x] = CastleBlock.Type.EMPTY;
}
}
castle[0][6] = CastleBlock.Type.WALL;
}
@Override
protected void end() {
if ( castleDirty ) {
castleDirty=false;
assembleCastle();
}
}
@Override
protected void process(Entity e) {
if ( castleDirty) {
if ( cm.get(e).fadeoutOnReplace )
{
e.edit().add(new Schedule().wait(1f).deleteFromWorld())
.add(new ColorAnimation(new Color(1, 1, 1, 1), new Color(1, 1, 1, 0), Interpolation.linear, 1f, 1f))
;
} else {
e.deleteFromWorld();
}
}
}
protected void assembleCastle()
{
spawnCoreBlocks();
spawnTrimming();
spawnExpansionPoints();
}
private void spawnTrimming() {
// spawn trimming
for ( int x=-1;x<=W;x++)
{
int px = CASTLE_X_OFFSET + x * 13;
for (int y=-1;y<=H;y++) {
int py = 30 + y * 17;
// only spawn trimming on empty blocks.
if ( isEmptyAt(x,y) || isTowerAt(x,y))
{
// top wall trimming.
if ( isWallAt(x, y-1) ) entityFactorySystem.createEntity("building-trimming-on-wall", px, py);
else if ( isWallAt(x+1, y-1) && !isWallAt(x+1, y) ) entityFactorySystem.createEntity("building-trimming-on-wall-left", px, py);
else if ( isWallAt(x-1, y-1) && !isWallAt(x-1, y) ) entityFactorySystem.createEntity("building-trimming-on-wall-right", px, py);
// side wall trimming left side
if ( isWallAt(x+1, y) && isWallAt(x+1, y+1) ) entityFactorySystem.createEntity("building-trimming-bottom-wall-left", px, py);
else if ( isWallAt(x+1, y) ) entityFactorySystem.createEntity("building-trimming-top-wall-left", px, py);
// side wall trimming right side.
if ( isWallAt(x-1, y) && isWallAt(x-1, y+1) ) entityFactorySystem.createEntity("building-trimming-bottom-wall-right", px, py);
else if ( isWallAt(x-1, y) ) entityFactorySystem.createEntity("building-trimming-top-wall-right", px, py);
// tower spire.
if ( isTowerAt(x, y-1) && !isTowerAt(x,y) ) {
entityFactorySystem.createEntity("building-trimming-on-tower", px, py);
if ( y >= BIRD_SPAWN_LEVEL ) spawnBirdsOfDoom(px, py);
}
if ( (isWallAt(x, y-1) && !isWallAt(x, y)) || (isTowerAt(x,y-2) && !isTowerAt(x,y-1)))
{
entityFactorySystem.createEntity("building-flag", px, py);
}
}
}
}
}
private void spawnBirdsOfDoom(int px, int py) {
// spires need birds! provided they're high up enough
for ( int i=0,s= MathUtils.random(1, 3);i<s;i++)
{
Bobbing bobbing = new Bobbing(10, 2, px + 6, py + MathUtils.random(15, 20), MathUtils.random(0.1f, 0.15f));
bobbing.age = MathUtils.random(0f,1f);
entityFactorySystem
.createEntity("bird")
.edit().add(bobbing)
;
}
}
private void spawnExpansionPoints() {
// spawn trimming
for ( int x=0;x<W;x++)
{
int px = CASTLE_X_OFFSET + x * 13;
for (int y=0;y<H;y++) {
int py = 30 + y * 17;
// only spawn trimming on empty blocks.
if ( castle[y][x] == CastleBlock.Type.EMPTY || castle[y][x] == CastleBlock.Type.TOWER)
{
if ( castle[y][x] == CastleBlock.Type.EMPTY )
{
boolean supportForWall = y == 0 || isWallAt(x, y-1);
boolean supportForTower = y == 0 || isWallAt(x, y-1) || isTowerAt(x, y-1);
if ( supportForTower || supportForWall )
{
Entity e = entityFactorySystem.createEntity("building-hammer", px, py);
ExpansionPoint point = e.getComponent(ExpansionPoint.class);
point.x = x;
point.y = y;
point.allowWalls = supportForWall;
point.allowTowers = supportForTower;
}
}
}
}
}
}
private boolean isWallAt(int x, int y) {
if ( x<0 || y<0 || x>=W || y>=H ) return false;
return (castle[y][x].subType == CastleBlock.SubType.WALL);
}
private boolean isTowerAt(int x, int y) {
if ( x<0 || y<0 || x>=W || y>=H ) return false;
return (castle[y][x].subType == CastleBlock.SubType.TOWER);
}
private boolean isEmptyAt(int x, int y) {
if ( x<0 || y<0 || x>=W || y>=H ) return true;
return (castle[y][x].subType == CastleBlock.SubType.NONE);
}
private void spawnCoreBlocks() {
knightLevel = 0;
mageLevel = 0;
spelunkerLevel = 0;
queenLevel = 1;
// spawn core blocks.
for ( int x=0;x<W;x++)
{
int px = CASTLE_X_OFFSET + x * 13;
for (int y=0;y<H;y++) {
int py = 30 + y * 17;
switch (castle[y][x]) {
case EMPTY:
break;
case WALL:
entityFactorySystem.createEntity("building-wall", px, py);
break;
case BARRACKS:
entityFactorySystem.createEntity("building-barracks", px, py);
knightLevel++;
break;
case SPELUNKER:
entityFactorySystem.createEntity("building-spelunker", px, py);
spelunkerLevel++;
break;
case TOWER:
entityFactorySystem.createEntity("building-tower", px, py);
mageLevel++;
break;
}
}
}
}
/** demolish the whole ground floor! */
public void demolishGroundFloor()
{
}
/** demolish any random top block, if available */
public void demolishRandomTopBlock() {
Array<Integer> validX = new Array<Integer>();
// step down the tower, until we hit a row with blocks.
for (int y = H - 1; y >= 0; y--) {
// pick a random block from that row.
if (rowHasBlocks(y)) {
int x = rowGetRandomBlockX(y);
if ( x != -1 )
{
demolishBlock(x,y);
return;
}
}
}
}
private void demolishBlock(int x, int y) {
servantManager.servantLostHome(x, y);
castle[y][x] = CastleBlock.Type.EMPTY;
castleDirty=true;
// kill corresponding actor!
assetSystem.playSfx("sfx_click");
// fling bricks everywhere!
for ( int i=0,s= MathUtils.random(4,6); i<s;i++) {
int px = CASTLE_X_OFFSET + x * 13 + MathUtils.random(0, 13);
int py = 30 + y * 17 + MathUtils.random(0,17);
entityFactorySystem.createEntity("particle-debris", px, py);
}
}
private int rowGetRandomBlockX(int y) {
final Array<Integer> usedX = new Array<Integer>();
for ( int x=0;x<W;x++)
{
if ( castle[y][x] != CastleBlock.Type.EMPTY )
{
usedX.add(x);
}
}
Integer result = usedX.random();
return result != null ? result : -1;
}
private boolean rowHasBlocks(int y) {
for (int x = 0; x < W; x++) {
if ( castle[y][x] != CastleBlock.Type.EMPTY) return true;
}
return false;
}
public void setBlock(int x, int y, CastleBlock.Type type) {
if ( x<0 || y<0 || x>=W || y>=H ) return;
castleDirty=true;
castle[y][x] = type;
servantManager.createServant(x, y, type);
assetSystem.playSfx("sfx_dududi");
}
/**
* Not very efficient, but also not called often. ;)
*
* @return random X coordinate that touches the castle at floor level.
*/
public int getRandomUsedX()
{
Array<Integer> usedX = new Array<Integer>();
for ( int x=0;x<W;x++)
{
if ( castle[0][x] != CastleBlock.Type.EMPTY )
{
usedX.add(CASTLE_X_OFFSET + x * 13 + MathUtils.random(0, 13));
}
}
Integer result = usedX.random();
return result != null ? result : 50;
}
}