Skip to content

Commit

Permalink
Adds the ability to include MythicMobs in Blueprints. Fixes #2316
Browse files Browse the repository at this point in the history
  • Loading branch information
tastybento committed Mar 10, 2024
1 parent cb7c63a commit 4810c4c
Show file tree
Hide file tree
Showing 14 changed files with 455 additions and 48 deletions.
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@
<id>MG-Dev Jenkins CI Maven Repository</id>
<url>https://ci.mg-dev.eu/plugin/repository/everything</url>
</repository>
<!-- For MythicMobs -->
<repository>
<id>nexus</id>
<name>Lumine Releases</name>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
</repositories>

<dependencies>
Expand Down Expand Up @@ -297,6 +303,12 @@
<version>${myworlds.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.lumine</groupId>
<artifactId>Mythic-Dist</artifactId>
<version>5.3.5</version>
<scope>provided</scope>
</dependency>
<!-- Shaded APIs -->
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/world/bentobox/bentobox/BentoBox.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import world.bentobox.bentobox.hooks.ItemsAdderHook;
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
import world.bentobox.bentobox.hooks.MyWorldsHook;
import world.bentobox.bentobox.hooks.MythicMobsHook;
import world.bentobox.bentobox.hooks.SlimefunHook;
import world.bentobox.bentobox.hooks.VaultHook;
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
Expand Down Expand Up @@ -185,6 +186,9 @@ private void completeSetup(long loadTime) {
final long enableStart = System.currentTimeMillis();
hooksManager.registerHook(new VaultHook());

// MythicMobs
hooksManager.registerHook(new MythicMobsHook());

hooksManager.registerHook(new PlaceholderAPIHook());
// Setup the Placeholders manager
placeholdersManager = new PlaceholdersManager(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.bukkit.Bukkit;
import org.bukkit.Location;
Expand Down Expand Up @@ -43,6 +44,7 @@
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
import world.bentobox.bentobox.hooks.MythicMobsHook;

/**
* The clipboard provides the holding spot for an active blueprint that is being
Expand All @@ -67,16 +69,24 @@ public class BlueprintClipboard {
private final Map<Vector, BlueprintBlock> bpAttachable = new LinkedHashMap<>();
private final Map<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>();
private final BentoBox plugin = BentoBox.getInstance();
private Optional<MythicMobsHook> mmh;

/**
* Create a clipboard for blueprint
* @param blueprint - the blueprint to load into the clipboard
*/
public BlueprintClipboard(@NonNull Blueprint blueprint) {
this.blueprint = blueprint;
// MythicMobs
mmh = plugin.getHooks().getHook("MythicMobs").filter(hook -> hook instanceof MythicMobsHook)
.map(h -> (MythicMobsHook) h);
}

public BlueprintClipboard() { }
public BlueprintClipboard() {
// MythicMobs
mmh = plugin.getHooks().getHook("MythicMobs").filter(hook -> hook instanceof MythicMobsHook)
.map(h -> (MythicMobsHook) h);
}

/**
* Copy the blocks between pos1 and pos2 into the clipboard for a user.
Expand Down Expand Up @@ -285,6 +295,7 @@ private List<BlueprintEntity> setEntities(Collection<LivingEntity> entities) {
List<BlueprintEntity> bpEnts = new ArrayList<>();
for (LivingEntity entity: entities) {
BlueprintEntity bpe = new BlueprintEntity();

bpe.setType(entity.getType());
bpe.setCustomName(entity.getCustomName());
if (entity instanceof Villager villager) {
Expand Down Expand Up @@ -317,6 +328,10 @@ private List<BlueprintEntity> setEntities(Collection<LivingEntity> entities) {
if (entity instanceof Horse horse) {
bpe.setStyle(horse.getStyle());
}

mmh.filter(mm -> mm.isMythicMob(entity)).map(mm -> mm.getMythicMob(entity))
.ifPresent(mmr -> bpe.setMythicMobsRecord(mmr));

bpEnts.add(bpe);
}
return bpEnts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@
*/
public class BlueprintEntity {

public record MythicMobRecord(String type, String displayName, double level, float power, String stance) {
};

// GSON can serialize records, but the record class needs to be know in advance. So this breaks out the record entries
@Expose
String MMtype;
@Expose
Double MMLevel;
@Expose
String MMStance;
@Expose
Float MMpower;

@Expose
private DyeColor color;
@Expose
Expand All @@ -50,7 +63,6 @@ public class BlueprintEntity {
private Integer experience;
@Expose
private Villager.Type villagerType;


/**
* @since 1.8.0
Expand Down Expand Up @@ -85,7 +97,6 @@ public void configureEntity(Entity e) {
if (style != null && e instanceof Horse horse) {
horse.setStyle(style);
}

}

/**
Expand Down Expand Up @@ -270,5 +281,24 @@ public void setVillagerType(Villager.Type villagerType) {
public void setDomestication(Integer domestication) {
this.domestication = domestication;
}

/**
* @return the mythicMobsRecord
*/
public MythicMobRecord getMythicMobsRecord() {
return new MythicMobRecord(this.MMtype, this.getCustomName(), this.MMLevel, this.MMpower, this.MMStance);
}

/**
* @param mythicMobsRecord the mythicMobsRecord to set
* @since 2.1.0
*/
public void setMythicMobsRecord(MythicMobRecord mmr) {
this.setCustomName(mmr.displayName());
this.MMtype = mmr.type();
this.MMLevel = mmr.level();
this.MMStance = mmr.stance();
this.MMpower = mmr.power();
}

}
70 changes: 70 additions & 0 deletions src/main/java/world/bentobox/bentobox/hooks/MythicMobsHook.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package world.bentobox.bentobox.hooks;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Entity;

import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.mythic.core.mobs.ActiveMob;
import world.bentobox.bentobox.api.hooks.Hook;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.MythicMobRecord;

/**
* Provides implementation and interfacing to interact with MythicMobs.
*
* @author tastybento
* @since 2.2.0
*/
public class MythicMobsHook extends Hook {

public MythicMobsHook() {
super("MythicMobs", Material.CREEPER_HEAD);
}

public boolean isMythicMob(Entity bukkitEntity) {
return MythicBukkit.inst().getMobManager().isMythicMob(bukkitEntity);
}

public MythicMobRecord getMythicMob(Entity bukkitEntity) {
ActiveMob mm = MythicBukkit.inst().getMobManager().getActiveMob(bukkitEntity.getUniqueId()).orElse(null);
if (mm != null) {
return new MythicMobRecord(mm.getMobType(), mm.getDisplayName(), mm.getLevel(),
mm.getPower(),
mm.getStance());
}
return null;
}


@Override
public boolean hook() {
return true; // The hook process shouldn't fail
}

@Override
public String getFailureCause() {
return null; // The hook process shouldn't fail
}

/**
* Spawn a MythicMob
* @param mmr MythicMobRecord
* @param spawnLocation location
* @return true if spawn is successful
*/
public boolean spawnMythicMob(MythicMobRecord mmr, Location spawnLocation) {
return MythicBukkit.inst().getMobManager().getMythicMob(mmr.type()).map(mob -> {
// A delay is required before spawning, I assume because the blocks are pasted using NMS
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> {
// spawns mob
ActiveMob activeMob = mob.spawn(BukkitAdapter.adapt(spawnLocation), mmr.level());
activeMob.setDisplayName(mmr.displayName());
activeMob.setPower(mmr.power());
activeMob.setStance(mmr.stance());
}, 40L);
return true;
}).orElse(false);
}
}
58 changes: 38 additions & 20 deletions src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.hooks.MythicMobsHook;
import world.bentobox.bentobox.nms.PasteHandler;

/**
Expand Down Expand Up @@ -172,29 +173,46 @@ public static void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner
public static CompletableFuture<Void> setEntity(Island island, Location location, List<BlueprintEntity> list) {
World world = location.getWorld();
assert world != null;
return Util.getChunkAtAsync(location).thenRun(() -> list.stream().filter(k -> k.getType() != null).forEach(k -> {
LivingEntity e = (LivingEntity) location.getWorld().spawnEntity(location, k.getType());
if (k.getCustomName() != null) {
String customName = k.getCustomName();
return Util.getChunkAtAsync(location).thenRun(() -> list.stream().filter(k -> k.getType() != null)
.forEach(k -> spawnBlueprintEntity(k, location, island)));
}

if (island != null) {
// Parse any placeholders in the entity's name, if the owner's connected (he should)
Optional<Player> owner = Optional.ofNullable(island.getOwner())
.map(User::getInstance)
.map(User::getPlayer);
if (owner.isPresent()) {
// Parse for the player's name first (in case placeholders might need it)
customName = customName.replace(TextVariables.NAME, owner.get().getName());
// Now parse the placeholders
customName = plugin.getPlaceholdersManager().replacePlaceholders(owner.get(), customName);
}
}
/**
* Spawn an entity
* @param k the blueprint entity definition
* @param location location
* @param island island
* @return true if Bukkit entity spawned, false if MythicMob entity spawned
*/
static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island island) {
if (k.getMythicMobsRecord() != null && plugin.getHooks().getHook("MythicMobs")
.filter(mmh -> mmh instanceof MythicMobsHook)
.map(mmh -> ((MythicMobsHook) mmh).spawnMythicMob(k.getMythicMobsRecord(), location))
.orElse(false)) {
// MythicMob has spawned.
return false;
}
LivingEntity e = (LivingEntity) location.getWorld().spawnEntity(location, k.getType());
if (k.getCustomName() != null) {
String customName = k.getCustomName();

// Actually set the custom name
e.setCustomName(customName);
if (island != null) {
// Parse any placeholders in the entity's name, if the owner's connected (he should)
Optional<Player> owner = Optional.ofNullable(island.getOwner()).map(User::getInstance)
.map(User::getPlayer);
if (owner.isPresent()) {
// Parse for the player's name first (in case placeholders might need it)
customName = customName.replace(TextVariables.NAME, owner.get().getName());
// Now parse the placeholders
customName = plugin.getPlaceholdersManager().replacePlaceholders(owner.get(), customName);
}
}
k.configureEntity(e);
}));

// Actually set the custom name
e.setCustomName(customName);
}
k.configureEntity(e);
return true;
}

/**
Expand Down
6 changes: 2 additions & 4 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: BentoBox
main: world.bentobox.bentobox.BentoBox
version: ${project.version}${build.number}
api-version: "1.18"
api-version: "1.20"

authors: [tastybento, Poslovitch]
contributors: ["The BentoBoxWorld Community"]
Expand All @@ -17,15 +17,13 @@ softdepend:
- Vault
- PlaceholderAPI
- dynmap
- WorldBorderAPI
- BsbMongo
- WorldGeneratorApi
- AdvancedChests
- LangUtils
- WildStacker
- LuckPerms
- HolographicDisplays
- EconomyPlus
- MythicMobs

libraries:
- mysql:mysql-connector-java:${mysql.version}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import world.bentobox.bentobox.blueprints.Blueprint;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.CommandsManager;
import world.bentobox.bentobox.managers.HooksManager;
import world.bentobox.bentobox.managers.LocalesManager;

/**
Expand Down Expand Up @@ -73,6 +74,11 @@ public void setUp() throws Exception {
// Set up plugin
Whitebox.setInternalState(BentoBox.class, "instance", plugin);

// Hooks
HooksManager hooksManager = mock(HooksManager.class);
when(hooksManager.getHook(anyString())).thenReturn(Optional.empty());
when(plugin.getHooks()).thenReturn(hooksManager);

// Blueprints Manager
when(plugin.getBlueprintsManager()).thenReturn(bm);

Expand Down
Loading

0 comments on commit 4810c4c

Please sign in to comment.