Skip to content

Commit

Permalink
Add support for dynamic creature spawning
Browse files Browse the repository at this point in the history
Position of spawn in zone is random instead of a static point

#713
  • Loading branch information
AntumDeluge committed May 14, 2024
1 parent 18c9003 commit 618e820
Show file tree
Hide file tree
Showing 6 changed files with 488 additions and 1 deletion.
24 changes: 24 additions & 0 deletions data/conf/zones.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
maxOccurs="unbounded"/>
<element name="associated" type="Q1:associatedZones" minOccurs="0"
maxOccurs="1"/>
<element name="spawns" type="Q1:spawnsGroup" minOccurs="0" maxOccurs="1"/>
</choice>
</sequence>
<attribute name="name" type="string"/>
Expand Down Expand Up @@ -178,4 +179,27 @@
<attribute name="zones" type="string" use="required"></attribute>
</complexType>

<complexType name="spawnsGroup">
<choice minOccurs="1" maxOccurs="unbounded">
<element name="creatures" type="Q1:creaturesSpawnsGroup" minOccurs="0" maxOccurs="1"/>
</choice>
</complexType>

<!--
adding a dynamic creature spawner to zone:
<spawns>
<creatures>
<parameter name="bat">15</parameter>
</creatures>
</spans>
"name" attribute is the creature to be spawned, value is the maximum number that can be active
at same time
-->
<complexType name="creaturesSpawnsGroup">
<sequence>
<element name="parameter" type="Q1:parameterType" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
</complexType>

</schema>
1 change: 1 addition & 0 deletions doc/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Changelog
- new achievements:
- 30 Minutes or Less: Deliver 25 hot pizzas
- Balduin allows requesting different item for Ultimate Collector quest
- support added for dynamic creature spawning

*web client*
- fixed text extending past edge of notification bubbles
Expand Down
12 changes: 11 additions & 1 deletion src/games/stendhal/server/core/config/ZonesXMLLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* $Id$
*/
/***************************************************************************
* (C) Copyright 2003-2023 - Stendhal *
* (C) Copyright 2003-2024 - Stendhal *
***************************************************************************
***************************************************************************
* *
Expand Down Expand Up @@ -38,6 +38,7 @@
import games.stendhal.common.tiled.StendhalMapStructure;
import games.stendhal.server.core.config.zone.AttributesXMLReader;
import games.stendhal.server.core.config.zone.ConfiguratorXMLReader;
import games.stendhal.server.core.config.zone.CreatureSpawnsXMLReader;
import games.stendhal.server.core.config.zone.EntitySetupXMLReader;
import games.stendhal.server.core.config.zone.PortalSetupXMLReader;
import games.stendhal.server.core.config.zone.RegionNameSubstitutionHelper;
Expand All @@ -60,6 +61,8 @@ public final class ZonesXMLLoader {

/** Zone attributes reader. */
private static final SetupXMLReader attributesReader = new AttributesXMLReader();
/** Dynamically spawned creatures. */
private static final SetupXMLReader creatureSpawnsReader = new CreatureSpawnsXMLReader();
/**
* The ConfiguratorDescriptor XML reader.
*/
Expand Down Expand Up @@ -417,6 +420,13 @@ public ZoneDesc readZone(final Element element) {
continue;
} else if (tag.equals("associated")) {
desc.setAssociatedZones(child.getAttribute("zones"));
} else if (tag.equals("spawns")) {
for (final Element spawn: XMLUtil.getElements(child)) {
final String spawnType = spawn.getTagName();
if ("creatures".equals(spawnType)) {
setupDesc = creatureSpawnsReader.read(spawn);
}
}
} else {
logger.warn("Zone [" + name + "] has unknown element: " + tag);
continue;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/***************************************************************************
* Copyright © 2024 - Faiumoni e. V. *
***************************************************************************
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
package games.stendhal.server.core.config.zone;

import java.util.Map;

import org.apache.log4j.Logger;
import org.w3c.dom.Element;

import games.stendhal.common.MathHelper;
import games.stendhal.server.core.engine.StendhalRPZone;
import games.stendhal.server.entity.mapstuff.spawner.DynamicCreatureSpawner;


/**
* Parses info for dynamically spawned creatures.
*/
public class CreatureSpawnsXMLReader extends SetupXMLReader {

private static Logger logger = Logger.getLogger(CreatureSpawnsXMLReader.class);


@Override
public SetupDescriptor read(final Element element) {
final CreatureSpawnsDescriptor desc = new CreatureSpawnsDescriptor();
readParameters(desc, element);
return desc;
}

private static class CreatureSpawnsDescriptor extends SetupDescriptor {
@Override
public void setup(final StendhalRPZone zone) {
final DynamicCreatureSpawner spawner = new DynamicCreatureSpawner(zone);
final Map<String, String> params = getParameters();
for (final String name: params.keySet()) {
final int max = MathHelper.parseIntDefault(params.get(name), 0);
if (max < 0) {
logger.warn("Max must be a positive integer, not registering dynamic spawn for creature \"" + name + "\"");
continue;
}
spawner.register(name, max);
}
// NOTE: does zone need to know about dynamic spawner?
zone.setDynamicSpawner(spawner);
}
}
}
52 changes: 52 additions & 0 deletions src/games/stendhal/server/core/engine/StendhalRPZone.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import games.stendhal.common.Direction;
import games.stendhal.common.Line;
import games.stendhal.common.MathHelper;
import games.stendhal.common.Rand;
import games.stendhal.common.filter.FilterCriteria;
import games.stendhal.common.grammar.Grammar;
import games.stendhal.common.tiled.LayerDefinition;
Expand All @@ -64,6 +65,7 @@
import games.stendhal.server.entity.mapstuff.portal.OneWayPortalDestination;
import games.stendhal.server.entity.mapstuff.portal.Portal;
import games.stendhal.server.entity.mapstuff.spawner.CreatureRespawnPoint;
import games.stendhal.server.entity.mapstuff.spawner.DynamicCreatureSpawner;
import games.stendhal.server.entity.mapstuff.spawner.PassiveEntityRespawnPoint;
import games.stendhal.server.entity.mapstuff.spawner.PassiveEntityRespawnPointFactory;
import games.stendhal.server.entity.mapstuff.spawner.SheepFood;
Expand All @@ -73,6 +75,7 @@
import games.stendhal.server.entity.npc.TrainingDummyFactory;
import games.stendhal.server.entity.player.Player;
import games.stendhal.server.util.StringUtils;
import marauroa.common.Pair;
import marauroa.common.game.IRPZone;
import marauroa.common.game.RPObject;
import marauroa.common.game.RPSlot;
Expand Down Expand Up @@ -113,8 +116,12 @@ public class StendhalRPZone extends MarauroaRPZone {
*/
private final List<SheepFood> sheepFoods;

/** Statically spawned creatures. */
private final List<CreatureRespawnPoint> respawnPoints;

/** Dynamically spawned creatures. */
private DynamicCreatureSpawner dynamicSpawner;

private final List<PassiveEntityRespawnPoint> plantGrowers;

private final List<RPEntity> playersAndFriends;
Expand Down Expand Up @@ -311,6 +318,30 @@ public void remove(final CreatureRespawnPoint point) {
respawnPoints.remove(point);
}

/**
* Retrieves the dynamic creature spawner associated with this zone.
*
* @return
* Creature spawner instance.
*/
public DynamicCreatureSpawner getDynamicSpawner() {
return dynamicSpawner;
}

/**
* Sets the dynamic creature spawner associated with this zone.
*
* @param spawner
* Creature spawner instance.
*/
public void setDynamicSpawner(final DynamicCreatureSpawner spawner) {
if (dynamicSpawner != null) {
dynamicSpawner.remove();
}
dynamicSpawner = spawner;
dynamicSpawner.init();
}

/**
* Retrieves growers in this zone.
*/
Expand Down Expand Up @@ -1517,6 +1548,7 @@ public void nextTurn() {
os.append("playersAndFriends: " + playersAndFriends.size() + "\n");
os.append("portals: " + portals.size() + "\n");
os.append("respawnPoints: " + respawnPoints.size() + "\n");
os.append("dynamicRespawns: " + dynamicSpawner.maxTotal() + "\n");
os.append("sheepFoods: " + sheepFoods.size() + "\n");
os.append("objects: " + objects.size() + "\n");
logger.info(os);
Expand Down Expand Up @@ -1971,4 +2003,24 @@ public boolean passes(final Entity e) {
}
return (WeatherEntity) entities.get(0);
}

/**
* Attempts to find a position where an entity can be spawned.
*
* @param entity
* Entity attempting to spawn.
* @return
* Appropriate position or `null` if none found.
*/
public Pair<Integer, Integer> getRandomSpawnPosition(final Entity entity) {
final short retries = 50;
for (short t = 0; t < retries; t++) {
final int x = Rand.rand(getWidth());
final int y = Rand.rand(getHeight());
if (!collides(entity, x, y)) {
return new Pair<>(x, y);
}
}
return null;
}
}
Loading

0 comments on commit 618e820

Please sign in to comment.