Improve entity spawner functionality and fix some spawner bugs
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package com.bartlomiejpluta.base.api.camera;
|
||||
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.move.Movable;
|
||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||
import com.bartlomiejpluta.base.internal.object.Placeable;
|
||||
import com.bartlomiejpluta.base.internal.render.ShaderManager;
|
||||
@@ -10,5 +12,7 @@ public interface Camera extends Placeable {
|
||||
|
||||
boolean insideFrustum(float x, float y, float radius);
|
||||
|
||||
boolean insideFrustum(Context context, float x, float y);
|
||||
|
||||
void render(Screen screen, ShaderManager shaderManager);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.bartlomiejpluta.base.util.world;
|
||||
|
||||
import com.bartlomiejpluta.base.api.camera.Camera;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||
import com.bartlomiejpluta.base.api.event.Event;
|
||||
import com.bartlomiejpluta.base.api.event.EventType;
|
||||
@@ -26,28 +28,30 @@ public class EntitySpawner implements Updatable {
|
||||
private final List<Entity> spawnedEntities = new LinkedList<>();
|
||||
private final List<Supplier<Entity>> spawners = new ArrayList<>();
|
||||
private final Vector2ic origin;
|
||||
private final Context context;
|
||||
private final Camera camera;
|
||||
private final GameMap map;
|
||||
private final ObjectLayer layer;
|
||||
private float maxTime = 10000f;
|
||||
private DiceRoller interval = DiceRoller.of("90d2");
|
||||
private int range = 4;
|
||||
private int spawnChance = 50;
|
||||
private float spawnChance = 50;
|
||||
private DiceRoller countRoller = DiceRoller.of("1d4");
|
||||
private Entity awayEntity;
|
||||
private EventType<? extends Event> entityRemoveEvent;
|
||||
|
||||
private float accumulator = 10000f;
|
||||
private boolean spawnOutsideViewport = false;
|
||||
private float threshold;
|
||||
|
||||
public EntitySpawner(int x, int y, @NonNull GameMap map, @NonNull ObjectLayer layer) {
|
||||
public EntitySpawner(int x, int y, @NonNull Context context, @NonNull GameMap map, @NonNull ObjectLayer layer) {
|
||||
this.origin = new Vector2i(x, y);
|
||||
this.context = context;
|
||||
this.camera = context.getCamera();
|
||||
this.map = map;
|
||||
this.layer = layer;
|
||||
drawThreshold();
|
||||
}
|
||||
|
||||
public EntitySpawner maxTime(float maxTime) {
|
||||
this.maxTime = maxTime;
|
||||
this.accumulator = maxTime;
|
||||
public EntitySpawner interval(@NonNull String interval) {
|
||||
this.interval = DiceRoller.of(interval);
|
||||
drawThreshold();
|
||||
return this;
|
||||
}
|
||||
@@ -57,8 +61,8 @@ public class EntitySpawner implements Updatable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntitySpawner spawnChance(int percent) {
|
||||
this.spawnChance = percent;
|
||||
public EntitySpawner spawnChance(float change) {
|
||||
this.spawnChance = change;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -67,13 +71,18 @@ public class EntitySpawner implements Updatable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntitySpawner entityAway(@NonNull Entity awayEntity) {
|
||||
this.awayEntity = awayEntity;
|
||||
public EntitySpawner trackEntities(@NonNull EventType<? extends Event> entityRemoveEvent) {
|
||||
this.entityRemoveEvent = entityRemoveEvent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntitySpawner trackEntities(@NonNull EventType<? extends Event> entityRemoveEvent) {
|
||||
this.entityRemoveEvent = entityRemoveEvent;
|
||||
public EntitySpawner spawnOutsideViewport() {
|
||||
this.spawnOutsideViewport = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntitySpawner spawnOnCreate() {
|
||||
this.threshold = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -83,15 +92,11 @@ public class EntitySpawner implements Updatable {
|
||||
}
|
||||
|
||||
private void drawThreshold() {
|
||||
threshold = random.nextFloat(maxTime);
|
||||
threshold = interval.roll() * 1000f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
if (awayEntity != null && awayEntity.manhattanDistance(origin) <= range) {
|
||||
return;
|
||||
}
|
||||
|
||||
accumulator += dt * 1000;
|
||||
if (accumulator >= threshold) {
|
||||
spawn();
|
||||
@@ -101,36 +106,61 @@ public class EntitySpawner implements Updatable {
|
||||
}
|
||||
|
||||
private void spawn() {
|
||||
if (random.nextInt(100) < spawnChance) {
|
||||
if (random.nextFloat() > spawnChance) {
|
||||
return;
|
||||
}
|
||||
|
||||
var count = Math.max(0, countRoller.roll() - spawnedEntities.size());
|
||||
// Spawn multiple entities at the time
|
||||
var roll = countRoller.roll();
|
||||
var count = Math.max(0, roll - spawnedEntities.size());
|
||||
for (int i = 0; i < count; ++i) {
|
||||
var attempts = 0;
|
||||
var coordinates = new Vector2i();
|
||||
|
||||
do {
|
||||
while (true) {
|
||||
// Give up if too many fails during drawing the proper coordinates
|
||||
if (attempts > MAX_REPOSITION_ATTEMPTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw the coordinates and make sure they are inside the current map boundaries
|
||||
coordinates.x = MathUtil.clamp(origin.x() + random.nextInt(2 * range) - range, 0, map.getColumns() - 1);
|
||||
coordinates.y = MathUtil.clamp(origin.y() + random.nextInt(2 * range) - range, 0, map.getRows() - 1);
|
||||
|
||||
// If tile is not reachable, draw the coordinates again
|
||||
if (!layer.isTileReachable(coordinates)) {
|
||||
++attempts;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attempts > MAX_REPOSITION_ATTEMPTS) {
|
||||
return;
|
||||
// If we want to keep spawning entities outside the camera view
|
||||
// check if the drawn coordinates are inside frustum.
|
||||
// If so, draw it again.
|
||||
if (spawnOutsideViewport && camera.insideFrustum(context, coordinates.x, coordinates.y)) {
|
||||
++attempts;
|
||||
continue;
|
||||
}
|
||||
|
||||
} while (Distance.manhattan(origin, coordinates) <= range);
|
||||
// We need also to drop the coordinates that are too far from the spawner origin
|
||||
if (Distance.manhattan(origin, coordinates) > range) {
|
||||
++attempts;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We found the proper coordinates
|
||||
break;
|
||||
}
|
||||
|
||||
// Draw the entity spawner
|
||||
var spawner = spawners.get(random.nextInt(spawners.size()));
|
||||
var entity = spawner.get();
|
||||
|
||||
// Create the entity and push it onto the map layer
|
||||
var entity = spawner.get();
|
||||
entity.setCoordinates(coordinates);
|
||||
layer.addEntity(entity);
|
||||
|
||||
// If we want to keep the number of spawned entities per spawner almost constant
|
||||
// we need to know when the entity should be removed (i.e. it has been killed by player).
|
||||
if (entityRemoveEvent != null) {
|
||||
spawnedEntities.add(entity);
|
||||
entity.addEventListener(entityRemoveEvent, e -> spawnedEntities.remove(entity));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bartlomiejpluta.base.engine.world.camera;
|
||||
|
||||
import com.bartlomiejpluta.base.api.camera.Camera;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
|
||||
import com.bartlomiejpluta.base.engine.world.object.Model;
|
||||
@@ -25,6 +26,13 @@ public class DefaultCamera extends Model implements Camera {
|
||||
return frustum.testSphere(x, y, 0.0f, radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insideFrustum(Context context, float x, float y) {
|
||||
var map = context.getMap();
|
||||
var stepSize = map.getStepSize();
|
||||
return insideFrustum(stepSize.x() * x, stepSize.y() * y, stepSize.get(stepSize.maxComponent()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Screen screen, ShaderManager shaderManager) {
|
||||
// Update matrices
|
||||
|
||||
Reference in New Issue
Block a user