From 24d1be98b626454297bf26d420319902844b0ae4 Mon Sep 17 00:00:00 2001 From: Petter Arvidsson Date: Mon, 23 May 2016 00:27:50 +0200 Subject: [PATCH] Add support for leaf decay --- .../org/konstructs/forest/ForestConfig.java | 9 +- .../org/konstructs/forest/ForestPlugin.java | 19 +++- .../java/org/konstructs/forest/LeafDecay.java | 91 +++++++++++++++++++ .../org/konstructs/forest/LeafRemover.java | 47 ++++++++++ src/main/resources/reference.conf | 3 + 5 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/konstructs/forest/LeafDecay.java create mode 100644 src/main/java/org/konstructs/forest/LeafRemover.java diff --git a/src/main/java/org/konstructs/forest/ForestConfig.java b/src/main/java/org/konstructs/forest/ForestConfig.java index 840c360..9332320 100644 --- a/src/main/java/org/konstructs/forest/ForestConfig.java +++ b/src/main/java/org/konstructs/forest/ForestConfig.java @@ -22,6 +22,7 @@ public class ForestConfig { private final int maxSeedsPerGeneration; private final int seedEveryGeneration; private final int randomGrowth; + private final int leafDecayDelay; ForestConfig(String wood, String leaves, @@ -41,7 +42,8 @@ public class ForestConfig { int randomGrowthDelay, int maxSeedsPerGeneration, int seedEveryGeneration, - int randomGrowth) { + int randomGrowth, + int leafDecayDelay) { this.wood = BlockTypeId.fromString(wood); this.leaves = BlockTypeId.fromString(leaves); this.thinLeaves = BlockTypeId.fromString(thinLeaves); @@ -61,6 +63,7 @@ public class ForestConfig { this.maxSeedsPerGeneration = maxSeedsPerGeneration; this.seedEveryGeneration = seedEveryGeneration; this.randomGrowth = randomGrowth; + this.leafDecayDelay = leafDecayDelay; } public BlockTypeId getWood() { @@ -120,5 +123,7 @@ public int getSeedEveryGeneration() { public int getRandomGrowth() { return randomGrowth; } - + public int getLeafDecayDelay() { + return leafDecayDelay; + } } diff --git a/src/main/java/org/konstructs/forest/ForestPlugin.java b/src/main/java/org/konstructs/forest/ForestPlugin.java index 9e7efb4..d525802 100644 --- a/src/main/java/org/konstructs/forest/ForestPlugin.java +++ b/src/main/java/org/konstructs/forest/ForestPlugin.java @@ -16,8 +16,11 @@ public class ForestPlugin extends KonstructsActor { private final BlockTypeId growsOn; private final BlockTypeId seedsOn; private final BlockTypeId sapling; + private final BlockTypeId wood; private final int randomGrowth; private final Random random = new Random(); + private final ActorRef leafDecay; + private final Position leafDecayRadi; private float speed = GlobalConfig.DEFAULT_SIMULATION_SPEED; public ForestPlugin(String name, ActorRef universe, ForestConfig config) { @@ -26,7 +29,11 @@ public ForestPlugin(String name, ActorRef universe, ForestConfig config) { this.growsOn = config.getGrowsOn(); this.seedsOn = config.getSeedsOn(); this.sapling = config.getSapling(); + this.wood = config.getWood(); this.randomGrowth = config.getRandomGrowth(); + this.leafDecay = getContext().actorOf(LeafDecay.props(getUniverse(), config)); + int radi = config.getCrownRadi() * 2; + this.leafDecayRadi = new Position(radi, radi, radi); } void tryToSeed(Position pos) { @@ -71,7 +78,10 @@ public void onBoxQueryResult(BoxQueryResult result) { public void onBlockUpdateEvent(BlockUpdateEvent update) { for(Map.Entry p: update.getUpdatedBlocks().entrySet()) { BlockTypeId after = p.getValue().getAfter().getType(); - if(after.equals(sapling)) { + BlockTypeId before = p.getValue().getBefore().getType(); + if(before.equals(wood) && !after.equals(wood)) { + getUniverse().tell(new BoxQuery(Box.createAround(p.getKey(), leafDecayRadi)), leafDecay); + } else if(after.equals(sapling)) { seeded(p.getKey()); } else if(after.equals(growsOn) && random.nextInt(10000) <= randomGrowth) { @@ -86,6 +96,7 @@ public void onBlockUpdateEvent(BlockUpdateEvent update) { @Override public void onGlobalConfig(GlobalConfig config) { speed = config.getSimulationSpeed(); + leafDecay.forward(config, getContext()); } @Override @@ -127,7 +138,8 @@ public void onReceive(Object message) { @Config(key = "random-growth-delay") int randomGrowthDelay, @Config(key = "max-seeds-per-generation") int maxSeedsPerGeneration, @Config(key = "seed-every-generation") int seedEveryGeneration, - @Config(key = "random-growth") int randomGrowth + @Config(key = "random-growth") int randomGrowth, + @Config(key = "leaf-decay-delay") int leafDecayDelay ) { Class currentClass = new Object() { }.getClass().getEnclosingClass(); ForestConfig config = @@ -150,7 +162,8 @@ public void onReceive(Object message) { randomGrowthDelay, maxSeedsPerGeneration, seedEveryGeneration, - randomGrowth); + randomGrowth, + leafDecayDelay); return Props.create(currentClass, pluginName, universe, config); } } diff --git a/src/main/java/org/konstructs/forest/LeafDecay.java b/src/main/java/org/konstructs/forest/LeafDecay.java new file mode 100644 index 0000000..0ce5f3a --- /dev/null +++ b/src/main/java/org/konstructs/forest/LeafDecay.java @@ -0,0 +1,91 @@ +package org.konstructs.forest; + +import java.util.Set; +import java.util.Map; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import scala.concurrent.duration.Duration; +import scala.concurrent.duration.FiniteDuration; + +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.actor.Cancellable; + +import konstructs.plugin.KonstructsActor; +import konstructs.api.*; +import konstructs.api.messages.*; + +public class LeafDecay extends KonstructsActor { + private static class Trigger {} + private final Random random = new Random(); + private final Position radi; + private final ActorRef leafRemover; + private final BlockTypeId leavesId; + private final BlockTypeId thinLeavesId; + private final ForestConfig config; + private Set leaves; + private float speed = GlobalConfig.DEFAULT_SIMULATION_SPEED; + private Cancellable scheduled; + + LeafDecay(ActorRef universe, ForestConfig config) { + super(universe); + this.radi = new Position(config.getCrownRadi(), config.getCrownRadi(), config.getCrownRadi()); + this.leaves = new HashSet<>(); + this.leafRemover = getContext().actorOf(LeafRemover.props(getUniverse(), config)); + this.leavesId = config.getLeaves(); + this.thinLeavesId = config.getThinLeaves(); + this.config = config; + this.scheduled = schedule(); + } + + private Cancellable schedule() { + FiniteDuration duration = + Duration.create((long)((float)config.getLeafDecayDelay() / speed), + TimeUnit.MILLISECONDS); + return getContext().system().scheduler().schedule(duration, duration, getSelf(), new Trigger(), + getContext().system().dispatcher(), getSelf()); + } + + @Override + public void onBoxQueryResult(BoxQueryResult result) { + Map placed = result.getAsMap(); + for(Map.Entry p: placed.entrySet()) { + BlockTypeId type = p.getValue(); + if(type.equals(leavesId) || type.equals(thinLeavesId)) + leaves.add(p.getKey()); + } + } + + @Override + public void onGlobalConfig(GlobalConfig config) { + speed = config.getSimulationSpeed(); + scheduled.cancel(); + scheduled = schedule(); + } + + @Override + public void onReceive(Object message) { + if(message instanceof Trigger && leaves.size() > 0) { + int current = 0; + int find = random.nextInt(leaves.size()); + for(Iterator i = leaves.iterator(); i.hasNext();) { + Position p = i.next(); + if(current == find) { /* Random leaf found, try to remove it */ + getUniverse().tell(new BoxQuery(Box.createAround(p, radi)), leafRemover); + i.remove(); + return; + } + current++; + } + } else { + super.onReceive(message); // Handle konstructs messages + } + } + + public static Props props(ActorRef universe, ForestConfig config) { + return Props.create(LeafDecay.class, universe, config); + } +} diff --git a/src/main/java/org/konstructs/forest/LeafRemover.java b/src/main/java/org/konstructs/forest/LeafRemover.java new file mode 100644 index 0000000..e71a7c9 --- /dev/null +++ b/src/main/java/org/konstructs/forest/LeafRemover.java @@ -0,0 +1,47 @@ +package org.konstructs.forest; + +import java.util.Arrays; + +import akka.actor.ActorRef; +import akka.actor.Props; +import konstructs.plugin.KonstructsActor; +import konstructs.api.*; +import konstructs.api.messages.*; + +public class LeafRemover extends KonstructsActor { + private final BlockFilter leavesFilter; + private final BlockTypeId wood; + + LeafRemover(ActorRef universe, ForestConfig config) { + super(universe); + this.wood = config.getWood(); + this.leavesFilter = BlockFilterFactory + .withBlockTypeId(config.getLeaves()) + .or(BlockFilterFactory.withBlockTypeId(config.getThinLeaves())); + } + + private Position getCenter(Box box) { + Position size = box.getSize(); + return box + .getFrom() + .addX(size.getX() / 2) + .addY(size.getY() / 2) + .addZ(size.getZ() / 2); + } + + private boolean findWood(BoxQueryResult result) { + return Arrays.asList(result.getBlocks()).contains(wood); + } + + @Override + public void onBoxQueryResult(BoxQueryResult result) { + if(!findWood(result)) { + replaceWithVacuum(leavesFilter, getCenter(result.getBox())); + } + } + + public static Props props(ActorRef universe, ForestConfig config) { + return Props.create(LeafRemover.class, universe, config); + } + +} diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index d9cf65f..7313d3b 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -20,6 +20,7 @@ konstructs { max-seeds-per-generation = 1 seed-every-generation = 4 random-growth = 2 + leaf-decay-delay = 5000 } org/konstructs/forest/beech { class = "org.konstructs.forest.ForestPlugin" @@ -42,6 +43,7 @@ konstructs { max-seeds-per-generation = 3 seed-every-generation = 2 random-growth = 1 + leaf-decay-delay = 5000 } org/konstructs/forest/birch { class = "org.konstructs.forest.ForestPlugin" @@ -64,6 +66,7 @@ konstructs { max-seeds-per-generation = 1 seed-every-generation = 3 random-growth = 6 + leaf-decay-delay = 5000 } org/konstructs/block-manager { blocks {