Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for Conditional Biomes #3

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/main/java/org/terasology/biomesAPI/BaseConditionalBiome.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2019 MovingBlocks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...

*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.biomesAPI;

import org.terasology.math.geom.Vector2f;
import org.terasology.world.generation.facets.base.FieldFacet2D;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public abstract class BaseConditionalBiome implements ConditionalBiome {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide Javadoc what this base class offers.

protected Map<Class<? extends FieldFacet2D>, Vector2f> limitedFacets = new HashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using Vector2f to store the range, maybe consider using the Range class from Guava?. Handily, it would come with range.contains(value) for even simpler checks 😉


@Override
public boolean isValid(Class<? extends FieldFacet2D> facetClass, Float value) {
Vector2f constraints = limitedFacets.get(facetClass);
return constraints == null || (value >= constraints.x && value <= constraints.y);
}

@Override
public Set<Class<? extends FieldFacet2D>> getLimitedFacets() {
return limitedFacets.keySet();
}

@Override
public void setLowerLimit(Class<? extends FieldFacet2D> facetClass, Float minimum) {
limitedFacets.compute(facetClass, (k, v) -> {
if (v == null) v = new Vector2f(minimum, Float.MAX_VALUE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use braces even for these simple one-line if-statements.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I'd do

if (v == null) v = new Vector2f(Float.MIN_VALUE, Float.MAX_VALUE);

for the initialization in both cases, and only set the minimum or maximum once afterwards.

v.x = minimum;
return v;
});
}

@Override
public void setUpperLimit(Class<? extends FieldFacet2D> facetClass, Float maximum) {
limitedFacets.compute(facetClass, (k, v) -> {
if (v == null) v = new Vector2f(Float.MIN_VALUE, maximum);
v.y = maximum;
return v;
});
}
}
21 changes: 21 additions & 0 deletions src/main/java/org/terasology/biomesAPI/BiomeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.terasology.entitySystem.systems.BaseComponentSystem;
import org.terasology.entitySystem.systems.RegisterSystem;
import org.terasology.logic.players.PlayerCharacterComponent;
import org.terasology.math.geom.BaseVector2i;
import org.terasology.math.geom.Vector3f;
import org.terasology.math.geom.Vector3i;
import org.terasology.physics.events.MovedEvent;
Expand All @@ -34,7 +35,9 @@
import org.terasology.world.chunks.CoreChunk;
import org.terasology.world.chunks.blockdata.ExtraDataSystem;
import org.terasology.world.chunks.blockdata.RegisterExtraData;
import org.terasology.world.generation.GeneratingRegion;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -97,6 +100,24 @@ public <T extends Biome> List<T> getRegisteredBiomes(Class<T> biomeClass) {
return biomeMap.values().stream().filter(biomeClass::isInstance).map(biomeClass::cast).collect(Collectors.toList());
}

@Override
public List<Biome> getValidBiomes(GeneratingRegion region, BaseVector2i pos) {
return getValidBiomes(region, pos, false);
}

@Override
public List<Biome> getValidBiomes(GeneratingRegion region, BaseVector2i pos, boolean conditionalOnly) {
List<Biome> matches = new ArrayList<>();
for (Biome biome : biomeMap.values()) {
if (biome instanceof ConditionalBiome) {
if (((ConditionalBiome) biome).isValid(region, pos)) matches.add(biome);
} else if (!conditionalOnly) {
matches.add(biome);
}
}
return matches;
}

/**
* Blocks have id, no matter what kind of blocks they are.
*/
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/org/terasology/biomesAPI/BiomeRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
package org.terasology.biomesAPI;

import org.terasology.entitySystem.systems.ComponentSystem;
import org.terasology.math.geom.BaseVector2i;
import org.terasology.math.geom.Vector3i;
import org.terasology.world.chunks.CoreChunk;
import org.terasology.world.generation.GeneratingRegion;

import java.util.Collection;
import java.util.Optional;
Expand Down Expand Up @@ -96,4 +98,21 @@ public interface BiomeRegistry {
* @return Collection of biomes of given subtype
*/
<T extends Biome> Collection<T> getRegisteredBiomes(Class<T> biomeClass);

/**
* Returns all biomes that do not forbid themselves from the given location.
* @param region The world region
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param region The world region
* @param region The world region to generate, holding the facet data

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I created the implementation inside the biome instead of inside the registry is that it is a much simpler matter to add abstract conditional logic as an overridden method of the biome than it is to insert such logic directly into a map.

Honestly, it leaves me wondering if biomes should not be changed to a form of asset that can be handled by the entity system, like prefabs... but I believe that doing so would likely require an overhaul of the game loading system so that the entity system is initialized before facets are created.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the facet providers already use components to hold their configuration (not that this is working for actual world gen, though). Turning biomes into assets sounds like a good idea, overall...

* @param pos The location in the world
* @return Collection of biomes that are valid for that location
*/
Collection<Biome> getValidBiomes(GeneratingRegion region, BaseVector2i pos);

/**
* Returns all biomes that do not forbid themselves from the given location.
* @param region The world region
* @param pos The location in the world
* @param conditionalOnly If true, ignore biomes that have no restrictions
* @return Collection of biomes that are valid for that location
*/
Collection<Biome> getValidBiomes(GeneratingRegion region, BaseVector2i pos, boolean conditionalOnly);
}
74 changes: 74 additions & 0 deletions src/main/java/org/terasology/biomesAPI/ConditionalBiome.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2019 MovingBlocks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...

*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.biomesAPI;

import org.terasology.math.geom.BaseVector2i;
import org.terasology.world.generation.GeneratingRegion;
import org.terasology.world.generation.facets.base.FieldFacet2D;

import java.util.Set;

/**
* This interface allows biomes to accept various limits on where they may generate.
*/
public interface ConditionalBiome extends Biome {

/**
* Returns true if the biome can generate at the given value of the given facet.
* @param facetClass The facet to check, such as humidity or temperature.
* @param value This facet's value.
* @return True if possible.
*/
default boolean isValid(Class<? extends FieldFacet2D> facetClass, Float value)
{
return true;
}

/**
* Checks whether all facets of the given location meet this biome's restrictions.
* @param region The game region being generated.
* @param pos The particular position we are checking.
* @return True if this biome's conditions are met.
*/
default boolean isValid(GeneratingRegion region, BaseVector2i pos)
{
for (Class<? extends FieldFacet2D> classy : getLimitedFacets())
{
FieldFacet2D facetResult = region.getRegionFacet(classy);
if (!isValid(classy, facetResult.get(pos)))
{
return false;
}
}
return true;
}

/**
* @return A list of all facets that this biome has restrictions towards.
*/
Set<Class<? extends FieldFacet2D>> getLimitedFacets();

void setLowerLimit(Class<? extends FieldFacet2D> facetClass, Float minimum);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing Javadoc on the semantics of minimum. In the BaseConditionalBiome I can see that both values or inclusive - that should be noted here.

Likewise, for setUpperLimit.


void setUpperLimit(Class<? extends FieldFacet2D> facetClass, Float maximum);

default void setLimits(Class<? extends FieldFacet2D> facetClass, Float minimum, Float maximum)
{
setLowerLimit(facetClass, minimum);
setUpperLimit(facetClass, maximum);
}

}