Skip to content

Commit

Permalink
feat: Add custom mob registration and extension spawning (#26)
Browse files Browse the repository at this point in the history
* feat: Add custom mob registration and extension spawning

* Add setupTags method to MobTags

* refactor: Address review

* feat: Add equipment wrapping

---------

Co-authored-by: Lilly <[email protected]>
  • Loading branch information
Goldmensch and rainbowdashlabs authored Oct 25, 2023
1 parent db11bb2 commit 9dd0198
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 40 deletions.
61 changes: 55 additions & 6 deletions core/src/main/java/de/eldoria/bloodnight/mob/CustomMob.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,67 @@
package de.eldoria.bloodnight.mob;

import de.eldoria.bloodnight.mob.meta.Attributes;
import de.eldoria.bloodnight.mob.meta.MobDrops;
import de.eldoria.bloodnight.mob.meta.Equipment;
import de.eldoria.bloodnight.mob.meta.Extension;
import de.eldoria.bloodnight.configuration.Configuration;
import de.eldoria.bloodnight.items.ItemRegistry;
import de.eldoria.bloodnight.mob.meta.*;
import de.eldoria.bloodnight.nodes.container.ContainerMeta;
import de.eldoria.bloodnight.nodes.container.NodeContainer;
import de.eldoria.bloodnight.util.Checks;
import de.eldoria.bloodnight.util.MobTags;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;

public record CustomMob(String id, Equipment equipment, Attributes attributes, NodeContainer nodes, MobDrops mobDrops,
Extension extension) {
public CustomMob copy(Entity entity, Entity extension) {

public CustomMob {
Checks.notNull(extension, "Extension can not be null");
Checks.notNull(id, "Mob id can not be null");
Checks.lowerCase(id, "Mob id must be lower case.");
}

public CustomMob createLiving(LivingEntity entity, Configuration configuration) {
NodeContainer copy = nodes.copy();
copy.inject(new ContainerMeta(entity, extension));
Entity extensionEntity = extension.type() != ExtensionType.NONE
? createExtension(entity, configuration)
: null;
MobTags.setupTags(entity, extensionEntity, this);

applyEquipment(entity, this.equipment, configuration);

copy.inject(new ContainerMeta(entity, extensionEntity));
return new CustomMob(id, equipment, attributes, copy, mobDrops, this.extension);
}

private Entity createExtension(Entity baseEntity, Configuration configuration) {
var world = baseEntity.getWorld();
var extensionEntity = world.spawnEntity(baseEntity.getLocation(), extension.entityType());

if (!(extensionEntity instanceof LivingEntity livingEntity)) throw new RuntimeException("Entity is no living entity");

applyEquipment(livingEntity, equipment, configuration);

boolean added = switch (extension.type()) {
case RIDER -> extensionEntity.addPassenger(baseEntity);
case CARRIER -> baseEntity.addPassenger(extensionEntity);
case NONE -> throw new IllegalStateException("No NONE type expected here");
};
if (!added) throw new RuntimeException("Extension couldn't be added."); // TODO: 21.08.23 good logging
return extensionEntity;
}

private ItemStack getItem(String item, Configuration configuration) {
return configuration.items().registrations().get(item);
}

private void applyEquipment(LivingEntity entity, Equipment equipment, Configuration configuration) {
var entityEquipment = entity.getEquipment();
if (entityEquipment == null) return;
entityEquipment.setBoots(getItem(equipment.feet(), configuration));
entityEquipment.setLeggings(getItem(equipment.legs(), configuration));
entityEquipment.setChestplate(getItem(equipment.chest(), configuration));
entityEquipment.setHelmet(getItem(equipment.head(), configuration));
entityEquipment.setItemInMainHand(getItem(equipment.mainHand(), configuration));
entityEquipment.setItemInOffHand(getItem(equipment.offHand(), configuration));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

import org.bukkit.entity.EntityType;

public record Extension(ExtensionType extensionType, EntityType extensionEntity) {
public record Extension(ExtensionType type, EntityType entityType, Equipment equipment) {
}
15 changes: 11 additions & 4 deletions core/src/main/java/de/eldoria/bloodnight/mobs/MobCoordinator.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package de.eldoria.bloodnight.mobs;

import de.eldoria.bloodnight.configuration.Configuration;
import de.eldoria.bloodnight.mob.CustomMob;
import de.eldoria.bloodnight.nodes.dispatching.TriggerData;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;

import java.util.HashMap;
import java.util.Map;
Expand All @@ -17,6 +19,12 @@ public class MobCoordinator {
*/
private final Map<Integer, CustomMob> mobs = new HashMap<>();

private final Configuration configuration;

public MobCoordinator(Configuration configuration) {
this.configuration = configuration;
}

/**
* Dispatch a trigger onto an entity if it is a custom mob.
*
Expand All @@ -31,14 +39,13 @@ public void trigger(Entity entity, TriggerData<?> trigger) {

/**
* Registers the custom mob on the provided entity.
* Will create a copy of the custom mob using {@link CustomMob#copy(Entity, Entity)}
* Will create a living entity of the custom mob using {@link CustomMob#createLiving(LivingEntity, Configuration)}
* If this mob is already registered, it will be changed to the new custom mob.
*
* @param entity the entity to assign the mob to
* @param customMob the custom mob assigned to the entity.
*/
public void register(Entity entity, CustomMob customMob) {
// TODO spawn and register extension
mobs.put(entity.getEntityId(), customMob.copy(entity, null));
public void register(LivingEntity entity, CustomMob customMob) {
mobs.put(entity.getEntityId(), customMob.createLiving(entity, configuration));
}
}
12 changes: 3 additions & 9 deletions core/src/main/java/de/eldoria/bloodnight/mobs/MobRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ public MobRegistry() {
* @throws MobIdAlreadyTakenException when the id is already taken.
*/
public void add(CustomMob mob) {
check(mob);
Checks.notNull(mob, "Mob can not be null");

if (mobs.containsKey(mob.id())) throw new MobIdAlreadyTakenException(mob, get(mob.id()));
mobs.put(mob.id(), mob);
}


/**
* Returns the {@link CustomMob} associated with the given id.
*
Expand All @@ -53,16 +53,10 @@ public CustomMob get(String id) {
* Gets all matching {@link CustomMob}s, depending on the result of calling {@link Attributes#isAssignable(EntityType)}.
*
* @param active a set containing the ids of active mobs that should be checked
* @param type the type of the entity
* @param type the entityType of the entity
* @return an immutable list containing all mobs that are matching. May be empty.
*/
public List<CustomMob> getMatching(Set<String> active, EntityType type) {
return active.stream().map(mobs::get).filter(mob -> mob.attributes().isAssignable(type)).toList();
}

private void check(CustomMob mob) {
Checks.notNull(mob, "Mob can not be null");
Checks.notNull(mob.id(), "Mob id can not be null");
Checks.lowerCase(mob.id(), "Mob id must be lower case.");
}
}
31 changes: 14 additions & 17 deletions core/src/main/java/de/eldoria/bloodnight/mobs/MobSpawner.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,41 @@
import de.eldoria.bloodnight.configuration.Configuration;
import de.eldoria.bloodnight.configuration.elements.WorldSettings;
import de.eldoria.bloodnight.mob.CustomMob;
import de.eldoria.bloodnight.util.MobTags;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.plugin.Plugin;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;

public class MobSpawner implements Listener, Runnable {
public class MobSpawner implements Listener {
private final Configuration configuration;
private final Queue<Entity> nextTick = new ArrayDeque<>();
private final Queue<Entity> schedule = new ArrayDeque<>();
private final MobCoordinator coordinator;
private final Plugin plugin;

public MobSpawner(Configuration configuration, MobCoordinator coordinator) {
public MobSpawner(Configuration configuration, MobCoordinator coordinator, Plugin plugin) {
this.configuration = configuration;
this.coordinator = coordinator;
this.plugin = plugin;
}

@EventHandler(priority = EventPriority.MONITOR)
void onSpawn(EntitySpawnEvent event) {
public void onSpawn(EntitySpawnEvent event) {
// Wait one tick since there might be modification of freshly spawned mobs.
schedule.add(event.getEntity());
var entity = event.getEntity();
if (!(entity instanceof LivingEntity livingEntity)) return;
entity.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> handleSpawnedMob(livingEntity), 1);
}

void handleSpawnedMob(Entity entity) {
void handleSpawnedMob(LivingEntity entity) {
if (MobTags.isExtended(entity)) return;

WorldSettings worldSettings = configuration.worldConfig(entity.getWorld());
Set<String> active = worldSettings.mobSettings().spawning().activeTypes();
List<CustomMob> matching = configuration.mobs().getMatching(active, entity.getType());
Expand All @@ -45,12 +50,4 @@ void handleSpawnedMob(Entity entity) {
coordinator.register(entity, customMob);
}

@Override
public void run() {
while (!nextTick.isEmpty()) {
handleSpawnedMob(nextTick.poll());
}
nextTick.addAll(schedule);
schedule.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

Expand All @@ -55,7 +54,7 @@ void asJson() throws JsonProcessingException {
.build());
itemRegistry.register(ItemStackBuilder.of(Material.DIAMOND_CHESTPLATE).withDisplayName("Shiny Armor").build());

var itemIds = new ArrayList<String>(itemRegistry.registrations().keySet());
var itemIds = new ArrayList<>(itemRegistry.registrations().keySet());

MobRegistry mobs = new MobRegistry();

Expand Down Expand Up @@ -87,7 +86,7 @@ void asJson() throws JsonProcessingException {
ValueModifier.MULTIPLY,
2);

Extension extension = new Extension(ExtensionType.CARRIER, EntityType.SPIDER);
Extension extension = new Extension(ExtensionType.CARRIER, EntityType.SPIDER, new Equipment(null, null, null, null, null, null));
CustomMob customMob = new CustomMob("test", equipment, attributes, container, mobDrops, extension);
mobs.add(customMob);

Expand Down

0 comments on commit 9dd0198

Please sign in to comment.