Raidboss Announcer

LTS
Lucera

Raidboss Announcer is a mod that allows you to announce the RaidBoss to all players online.

Tested in Lucera revs. For any other rev, contact us here

Features:

  • Announce when a RaidBoss is attacked
  • Announce when a RaidBoss has dropped to x% HP (configurable)
  • Announce when a RaidBoss is dead

Easy setup download:

Or if you prefer to build yourself... These are the giranforge.properties configuration:

#===================================#
#
# GIRAN FORGE
# https://giranforge.com/
#
# This file goes into gameserver/config/custom/giranforge.properties
#===================================#


#===================================#
# Raidboss Announcements
#===================================#
# Enable/Disable the announcement of the RaidBoss
ENABLE_RB_ANNOUNCEMENTS=true

# Minimum level of the RaidBoss to trigger the announcement
MIN_RB_LEVEL=75

# Distance from RaidBoss where a player should receive the announcement
# If set to -1, the announcement will be shown to all players online
DISTANCE_TO_ANNOUNCE=5000

# This will show a nice message in players screen if true
SHOW_SCREEN_MESSAGE=true

# Time in ms to show the screen message
SHOW_SCREEN_MESSAGE_TIME=5000

Source code:

package services;

import l2.commons.configuration.ExProperties;
import l2.gameserver.Announcements;
import l2.gameserver.listener.actor.OnAttackListener;
import l2.gameserver.listener.actor.OnDeathListener;
import l2.gameserver.model.Creature;
import l2.gameserver.model.Player;
import l2.gameserver.model.instances.RaidBossInstance;
import l2.gameserver.model.actor.listener.CharListenerList;
import l2.gameserver.network.l2.components.ChatType;
import l2.gameserver.network.l2.components.IStaticPacket;
import l2.gameserver.network.l2.components.NpcString;
import l2.gameserver.network.l2.s2c.ExShowScreenMessage;
import l2.gameserver.network.l2.s2c.Say2;
import l2.gameserver.scripts.ScriptFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

public class BossAnnouncement implements OnAttackListener, OnDeathListener, ScriptFile {
    protected static final Logger _log = LoggerFactory.getLogger(BossAnnouncement.class);
    private static final BossAnnouncement INSTANCE = new BossAnnouncement();

    private final Map<Integer, Map<Integer, Boolean>> rbPercentages = new HashMap<>();
    private final Map<Integer, Boolean> isBeingAttacked = new HashMap<>();
    private static final String CONFIG_FILE = "config/custom/giranforge.properties";
    private static boolean ENABLE_RB_ANNOUNCEMENTS = true;
    private static int MIN_RB_LEVEL = 75;
    private static int DISTANCE_TO_ANNOUNCE = 5000; /// -1 to announce to everyone
    private static boolean SHOW_SCREEN_MESSAGE = false;
    private static int SHOW_SCREEN_MESSAGE_TIME = 5000;

    public static void loadConfigs() {
        ExProperties config = initProperties(CONFIG_FILE);
        ENABLE_RB_ANNOUNCEMENTS = Boolean.parseBoolean(config.getProperty("ENABLE_RB_ANNOUNCEMENTS", "true"));
        MIN_RB_LEVEL = Integer.parseInt(config.getProperty("MIN_RB_LEVEL", "15"));
        DISTANCE_TO_ANNOUNCE = Integer.parseInt(config.getProperty("DISTANCE_TO_ANNOUNCE", "30"));
        SHOW_SCREEN_MESSAGE = Boolean.parseBoolean(config.getProperty("SHOW_SCREEN_MESSAGE", "false"));
        SHOW_SCREEN_MESSAGE_TIME = Integer.parseInt(config.getProperty("SHOW_SCREEN_MESSAGE_TIME", "5000"));
    }

    public static ExProperties initProperties(String filename) {
        ExProperties result = new ExProperties();
        try {
            result.load(new File(filename));
        } catch (IOException ignored) {}
        return result;
    }
    
    @Override
    public void onAttack(Creature attacker, Creature target) {
        if (attacker instanceof Player && target instanceof RaidBossInstance raidBoss) {
            if (raidBoss.getLevel() < MIN_RB_LEVEL) {
                return;
            }

            double currentHp = raidBoss.getCurrentHp();
            double maxHp = raidBoss.getMaxHp();
            int hpPercentage = (int) ((currentHp / maxHp) * 100);

            if (!isBeingAttacked.containsKey(target.getObjectId())) {
                isBeingAttacked.put(target.getObjectId(), true);

                String message = "Raid Boss: " + raidBoss.getName() + " has been attacked!";

                announceToAll(message, raidBoss);
            }
            Map<Integer, Boolean> notifiedPercentages = rbPercentages.computeIfAbsent(raidBoss.getObjectId(), k -> new HashMap<>());
            String message = null;
            if (hpPercentage <= 50 && !notifiedPercentages.containsKey(50)) {
                notifiedPercentages.put(50, true);
                message = "Attention! Raid Boss: " + raidBoss.getName() + " has dropped to " + 50 + "% HP!";
            } else if (hpPercentage <= 25 && !notifiedPercentages.containsKey(25)){
                notifiedPercentages.put(25, true);
                message = "Attention! Raid Boss: " + raidBoss.getName() + " has dropped to " + 25 + "% HP!";
            } else if (hpPercentage <= 10 && !notifiedPercentages.containsKey(10)){
                notifiedPercentages.put(10, true);
                message = "Attention! Raid Boss: " + raidBoss.getName() + " has dropped to " + 10 + "% HP!";
            }

                if(message != null){
                    announceToAll(message, raidBoss);
                }
        }
    }

    @Override
    public void onDeath(Creature actor, Creature killer) {
        if (actor instanceof RaidBossInstance raidBoss) {
            if (raidBoss.getLevel() < MIN_RB_LEVEL) {
                return;
            }

            String message = "Raid Boss: " + raidBoss.getName() + " has been defeated!";
            announceToAll(message, raidBoss);

            rbPercentages.remove(raidBoss.getObjectId());
            isBeingAttacked.remove(raidBoss.getObjectId());
        }
    }

    private void announceToAll(String message, RaidBossInstance raidBoss) {
        Say2 announcement = new Say2(0, ChatType.ANNOUNCEMENT, "", message);
        IStaticPacket screenMessage = createScreenMessage(message, SHOW_SCREEN_MESSAGE_TIME);
        _log.info(screenMessage.toString());
        _log.info(String.valueOf(SHOW_SCREEN_MESSAGE));
        if(DISTANCE_TO_ANNOUNCE == -1) {
            Announcements.getInstance().announceToAll(announcement);
            if(SHOW_SCREEN_MESSAGE){
                Announcements.getInstance().announceToAll(screenMessage);
            }
            return;
        }
        raidBoss.getAroundCharacters(DISTANCE_TO_ANNOUNCE, 200).forEach(player -> {
            player.sendPacket(announcement);
            if(SHOW_SCREEN_MESSAGE){
                player.sendPacket(screenMessage);
            }
        });
    }

    private IStaticPacket createScreenMessage(String message, Integer duration){
        return new ExShowScreenMessage(
                NpcString.NONE,
                duration,
                ExShowScreenMessage.ScreenMessageAlign.TOP_CENTER,
                true,
                ExShowScreenMessage.STRING_TYPE,
                -1,
                true,
                message
        );
    }

    @Override
    public void onLoad() {
        if(ENABLE_RB_ANNOUNCEMENTS) {
            loadConfigs();
            CharListenerList.addGlobal(INSTANCE);
            _log.info("===================================");
            _log.info("Raidboss Announcements loaded");
            _log.info("===================================");
        }
    }

    @Override
    public void onReload() {
        onShutdown();
        onLoad();
    }

    @Override
    public void onShutdown() {
        CharListenerList.removeGlobal(INSTANCE);
    }
}