diff --git a/src/main/java/com/zivilon/cinder_loe/CinderLoE.java b/src/main/java/com/zivilon/cinder_loe/CinderLoE.java index 3ad96ea..1368e0a 100644 --- a/src/main/java/com/zivilon/cinder_loe/CinderLoE.java +++ b/src/main/java/com/zivilon/cinder_loe/CinderLoE.java @@ -9,11 +9,7 @@ import com.zivilon.cinder_loe.character.CharacterRoleAPI; import com.zivilon.cinder_loe.client.render.*; import com.zivilon.cinder_loe.client.render.corrupt.*; import com.zivilon.cinder_loe.client.render.projectile.*; -import com.zivilon.cinder_loe.command.CommandCinderCharacter; -import com.zivilon.cinder_loe.command.CommandOpenCarriageMenu; -import com.zivilon.cinder_loe.command.CommandSetDestination; -import com.zivilon.cinder_loe.command.CommandWarband; -import com.zivilon.cinder_loe.command.CommandUnitConversion; +import com.zivilon.cinder_loe.command.*; import com.zivilon.cinder_loe.entity.*; import com.zivilon.cinder_loe.entity.animals.Monkey; import com.zivilon.cinder_loe.entity.boss.CryptBoss; @@ -404,6 +400,7 @@ public class CinderLoE { event.registerServerCommand(new CommandOpenCarriageMenu()); event.registerServerCommand(new CommandSetDestination()); event.registerServerCommand(new CommandUnitConversion()); + event.registerServerCommand(new CommandMouthOfSauron()); // event.registerServerCommand(new CommandMobileSound()); } diff --git a/src/main/java/com/zivilon/cinder_loe/character/CharacterEventListener.java b/src/main/java/com/zivilon/cinder_loe/character/CharacterEventListener.java index e76917f..518dd0a 100644 --- a/src/main/java/com/zivilon/cinder_loe/character/CharacterEventListener.java +++ b/src/main/java/com/zivilon/cinder_loe/character/CharacterEventListener.java @@ -1,7 +1,14 @@ package com.zivilon.cinder_loe.character; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.gameevent.TickEvent; +import lotr.common.LOTRLevelData; +import lotr.common.entity.npc.*; +import net.minecraft.entity.Entity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.living.LivingHurtEvent; @@ -12,9 +19,8 @@ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; import com.zivilon.cinder_loe.entity.npc.radagast.FangornAnimal; @@ -23,8 +29,141 @@ public class CharacterEventListener { public CharacterEventListener() { // Register the event listener MinecraftForge.EVENT_BUS.register(this); + FMLCommonHandler.instance().bus().register(this); } + // Mouth of Sauron statics + private static final int HIRE_INTERVAL = 600; // every 30s 600 ticks + private static final double HIRE_RADIUS = 50.0D; // 50 blocks + private static final int MAX_HIRED = 10; + + // tick counter + private static final Map tickCounters = new HashMap<>(); + //command check + public static final Set hiringEnabled = new HashSet<>(); + + //Mouth of Sauron event handler + @SubscribeEvent + public void onPlayerTick(TickEvent.PlayerTickEvent event) { + if (event.phase != TickEvent.Phase.END || event.player.worldObj.isRemote) return; + + EntityPlayerMP player = (EntityPlayerMP) event.player; + UUID id = player.getUniqueID(); + + + // Only “MouthOfSauron” gets this ability + if (!CharacterRoleAPI.getCharacterRoleUUID("MouthOfSauron").equals(id)) { + tickCounters.remove(id); + hiringEnabled.remove(id); + return; + } + if (!hiringEnabled.contains(id)) { + tickCounters.remove(id); + return; + } + + // Tick up, and only run when we hit the interval + int count = tickCounters.getOrDefault(id, 0) + 1; + if (count < HIRE_INTERVAL) { + tickCounters.put(id, count); + return; + } + tickCounters.put(id, 0); + //player.addChatMessage(new ChatComponentText("[DEBUG] tryHireNPCs called")); + + tryHireNPCs(player); + } + + private static void tryHireNPCs(EntityPlayerMP player) { + World world = player.worldObj; + UUID playerId = player.getUniqueID(); + + // Search box + AxisAlignedBB box = AxisAlignedBB.getBoundingBox( + player.posX - HIRE_RADIUS, player.posY - HIRE_RADIUS, player.posZ - HIRE_RADIUS, + player.posX + HIRE_RADIUS, player.posY + HIRE_RADIUS, player.posZ + HIRE_RADIUS + ); + + // 1) Grab all NPCs in the box + List allNPCs = world.getEntitiesWithinAABB(LOTREntityNPC.class, box); + + // 2) Figure out which ones this player already has hired + List alreadyHired = new ArrayList<>(); + for (LOTREntityNPC npc : allNPCs) { + if (npc.hiredNPCInfo != null + && playerId.equals(npc.hiredNPCInfo.getHiringPlayerUUID())) { + alreadyHired.add(npc); + } + } + if (alreadyHired.size() >= MAX_HIRED) { + return; // at cap + } + + // 3) Filter into candidates (correct type & not already hired) + List candidates = new ArrayList<>(); + for (LOTREntityNPC npc : allNPCs) { + boolean isCorrectType = + npc instanceof LOTREntityOrc + || npc instanceof LOTREntityWarg + || npc instanceof LOTREntityTroll; + + boolean notAlreadyHired = + npc.hiredNPCInfo == null + || !playerId.equals(npc.hiredNPCInfo.getHiringPlayerUUID()); + + if (isCorrectType && notAlreadyHired) { + candidates.add(npc); + } + } + if (candidates.isEmpty()) { + return; + } + + // 4) Shuffle & hire up to remaining slots + Collections.shuffle(candidates, world.rand); + int slots = MAX_HIRED - alreadyHired.size(); + + // Track which factions already announced this tick + Set announced = new HashSet<>(); + + for (LOTREntityNPC toHire : candidates) { + if (slots-- <= 0) break; + + // Alignment check + float align = LOTRLevelData.getData(player) + .getAlignment(toHire.getHiringFaction()); + if (align < 500.0F) continue; + + // Hire the NPC via your handler + MouthOfSauronEntityHandler entry = + new MouthOfSauronEntityHandler(toHire, 500f); + entry.getOrCreateHiredNPC(world) + .hiredNPCInfo.hireUnit( + player, + false, + toHire.getHiringFaction(), + entry, + "", + null + ); + + // Announce once per faction + String factionName = toHire.getHiringFaction().factionEntityName(); + if (announced.add(factionName)) { + String msg = EnumChatFormatting.YELLOW + + "You have rallied the " + + factionName + + " to your cause!"; + player.addChatMessage(new ChatComponentText(msg)); + } + } + } + + + + + + @SubscribeEvent(priority = EventPriority.NORMAL, receiveCanceled = true) public void onLivingHurt(LivingHurtEvent event) { // Check if the entity being hurt is a character of interest diff --git a/src/main/java/com/zivilon/cinder_loe/character/MouthOfSauronEntityHandler.java b/src/main/java/com/zivilon/cinder_loe/character/MouthOfSauronEntityHandler.java new file mode 100644 index 0000000..78a179b --- /dev/null +++ b/src/main/java/com/zivilon/cinder_loe/character/MouthOfSauronEntityHandler.java @@ -0,0 +1,32 @@ +package com.zivilon.cinder_loe.character; + +import lotr.common.entity.npc.LOTREntityNPC; +import lotr.common.entity.npc.LOTRHireableBase; +import lotr.common.entity.npc.LOTRUnitTradeEntry; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.world.World; + +public class MouthOfSauronEntityHandler extends LOTRUnitTradeEntry { + + private final LOTREntityNPC npc; + + public MouthOfSauronEntityHandler(LOTREntityNPC npc, float alignmentRequired) { + super(npc.getClass(), 0 /*costless*/, alignmentRequired); + this.npc = npc; + } + + @Override + public LOTREntityNPC getOrCreateHiredNPC(World world) { + // Return the very same NPC, no new spawn. + return npc; + } + + @Override + public boolean hasRequiredCostAndAlignment(EntityPlayer player, LOTRHireableBase trader) { + // If they're already hired, disallow re-hiring + if (npc.hiredNPCInfo != null && npc.hiredNPCInfo.isActive) { + return false; + } + return super.hasRequiredCostAndAlignment(player, trader); + } +} diff --git a/src/main/java/com/zivilon/cinder_loe/command/CommandMouthOfSauron.java b/src/main/java/com/zivilon/cinder_loe/command/CommandMouthOfSauron.java new file mode 100644 index 0000000..31265d7 --- /dev/null +++ b/src/main/java/com/zivilon/cinder_loe/command/CommandMouthOfSauron.java @@ -0,0 +1,47 @@ +package com.zivilon.cinder_loe.command; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.ChatComponentText; + +import java.util.UUID; + +import static com.zivilon.cinder_loe.character.CharacterEventListener.hiringEnabled; + +public class CommandMouthOfSauron extends CommandBase { + @Override + public String getCommandName() { + return "MouthofSauron"; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "/MouthofSauron "; + } + + @Override + public void processCommand(ICommandSender sender, String[] args) { + if (!(sender instanceof EntityPlayerMP)) { + sender.addChatMessage(new ChatComponentText("Only players may use this command.")); + return; + } + if (args.length != 1 || + (!args[0].equalsIgnoreCase("on") && !args[0].equalsIgnoreCase("off"))) { + throw new WrongUsageException(getCommandUsage(sender)); + } + + EntityPlayerMP player = (EntityPlayerMP) sender; + UUID id = player.getUniqueID(); + boolean enable = args[0].equalsIgnoreCase("on"); + + if (enable) { + hiringEnabled.add(id); + player.addChatMessage(new ChatComponentText("MouthOfSauron auto-hire ENABLED.")); + } else { + hiringEnabled.remove(id); + player.addChatMessage(new ChatComponentText("MouthOfSauron auto-hire DISABLED.")); + } + } +} \ No newline at end of file