/*
* The MIT License (MIT)
*
* FXGL - JavaFX Game Library
*
* Copyright (c) 2015-2017 AlmasB (almaslvl@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.almasb.fxglgames.geowars;
import com.almasb.fxgl.annotation.SetEntityFactory;
import com.almasb.fxgl.annotation.Spawns;
import com.almasb.fxgl.app.FXGL;
import com.almasb.fxgl.core.math.FXGLMath;
import com.almasb.fxgl.entity.Entities;
import com.almasb.fxgl.entity.EntityFactory;
import com.almasb.fxgl.entity.GameEntity;
import com.almasb.fxgl.entity.SpawnData;
import com.almasb.fxgl.entity.component.CollidableComponent;
import com.almasb.fxgl.entity.control.ExpireCleanControl;
import com.almasb.fxgl.entity.control.OffscreenCleanControl;
import com.almasb.fxgl.entity.control.ProjectileControl;
import com.almasb.fxglgames.geowars.component.HPComponent;
import com.almasb.fxglgames.geowars.component.OldPositionComponent;
import com.almasb.fxglgames.geowars.control.*;
import javafx.geometry.Point2D;
import javafx.util.Duration;
/**
* @author Almas Baimagambetov (almaslvl@gmail.com)
*/
@SetEntityFactory
public class GeoWarsFactory implements EntityFactory {
private GeoWarsConfig config;
public GeoWarsFactory() {
try {
config = FXGL.getAssetLoader().loadKV("config.kv").to(GeoWarsConfig.class);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to parse KV file: " + e);
}
}
private static final int SPAWN_DISTANCE = 50;
/**
* These correspond to top-left, top-right, bottom-right, bottom-left.
*/
private Point2D[] spawnPoints = new Point2D[] {
new Point2D(SPAWN_DISTANCE, SPAWN_DISTANCE),
new Point2D(FXGL.getApp().getWidth() - SPAWN_DISTANCE, SPAWN_DISTANCE),
new Point2D(FXGL.getApp().getWidth() - SPAWN_DISTANCE, FXGL.getApp().getHeight() - SPAWN_DISTANCE),
new Point2D(SPAWN_DISTANCE, FXGL.getApp().getHeight() - SPAWN_DISTANCE)
};
private Point2D getRandomSpawnPoint() {
return spawnPoints[FXGLMath.random(3)];
}
@Spawns("Player")
public GameEntity spawnPlayer(SpawnData data) {
// TODO: move this to proper PlayerControl
OldPositionComponent oldPosition = new OldPositionComponent();
oldPosition.valueProperty().addListener((obs, old, newPos) -> {
Entities.getRotation(oldPosition.getEntity()).rotateToVector(newPos.subtract(old));
});
return Entities.builder()
.type(GeoWarsType.PLAYER)
.at(FXGL.getApp().getWidth() / 2, FXGL.getApp().getHeight() / 2)
.viewFromTextureWithBBox("Player.png")
.with(new CollidableComponent(true), oldPosition)
.with(new PlayerControl(), new KeepOnScreenControl(true, true))
.build();
}
@Spawns("Bullet")
public GameEntity spawnBullet(SpawnData data) {
FXGL.getAudioPlayer().playSound("shoot" + (int) (Math.random() * 8 + 1) + ".wav");
return Entities.builder()
.type(GeoWarsType.BULLET)
.from(data)
.viewFromTextureWithBBox("Bullet.png")
.with(new CollidableComponent(true))
.with(new ProjectileControl(data.get("direction"), 600),
new BulletControl(FXGL.<GeoWarsApp>getAppCast().getGrid()),
new OffscreenCleanControl())
.build();
}
@Spawns("Wanderer")
public GameEntity spawnWanderer(SpawnData data) {
boolean red = FXGLMath.randomBoolean((float)config.getRedEnemyChance());
int moveSpeed = red ? config.getRedEnemyMoveSpeed()
: FXGLMath.random(100, config.getWandererMaxMoveSpeed());
return Entities.builder()
.type(GeoWarsType.WANDERER)
.at(getRandomSpawnPoint())
.viewFromTextureWithBBox(red ? "RedWanderer.png" : "Wanderer.png")
.with(new HPComponent(red ? config.getRedEnemyHealth() : config.getEnemyHealth()),
new CollidableComponent(true))
.with(new WandererControl(moveSpeed))
.build();
}
@Spawns("Seeker")
public GameEntity spawnSeeker(SpawnData data) {
boolean red = FXGLMath.randomBoolean((float)config.getRedEnemyChance());
int moveSpeed = red ? config.getRedEnemyMoveSpeed()
: FXGLMath.random(150, config.getSeekerMaxMoveSpeed());
return Entities.builder()
.type(GeoWarsType.SEEKER)
.at(getRandomSpawnPoint())
.viewFromTextureWithBBox(red ? "RedSeeker.png" : "Seeker.png")
.with(new HPComponent(red ? config.getRedEnemyHealth() : config.getEnemyHealth()),
new CollidableComponent(true))
.with(new SeekerControl(FXGL.<GeoWarsApp>getAppCast().getPlayer(), moveSpeed))
.build();
}
@Spawns("Explosion")
public GameEntity spawnExplosion(SpawnData data) {
FXGL.getAudioPlayer().playSound("explosion-0" + (int) (Math.random() * 8 + 1) + ".wav");
return Entities.builder()
.at(data.getX() - 40, data.getY() - 40)
.viewFromNode(FXGL.getAssetLoader().loadTexture("explosion.png", 80 * 48, 80).toAnimatedTexture(48, Duration.seconds(2)))
.with(new ExpireCleanControl(Duration.seconds(1.8)))
.build();
}
@Spawns("Crystal")
public GameEntity spawnCrystal(SpawnData data) {
return Entities.builder()
.type(GeoWarsType.CRYSTAL)
.from(data)
.viewFromNodeWithBBox(FXGL.getAssetLoader().loadTexture("YellowCrystal.png").toAnimatedTexture(8, Duration.seconds(1)))
.with(new CollidableComponent(true))
.with(new ExpireCleanControl(Duration.seconds(10)))
.build();
}
}