L2JMobius

Public Development => Shares/Contributions => Topic started by: javierdc on July 03, 2025, 06:16:34 PM

Title: Auto Buff Fake Players
Post by: javierdc on July 03, 2025, 06:16:34 PM
This script automatically applies a series of buffs to Fake Players (fake AI characters) when they spawn (appear in the world) and reapplies them every 19 minutes, keeping them always ready for combat.



⚙️ Step-by-step operation:
Initial buff at spawn:

When a Fake Player appears in the game (onSpawn()), the system:

Manually applies a list of skills (buffs), without using maps or external lists.

It directly uses SkillData.getInstance().getSkill(skillId, skillLevel) to instantiate and apply each buff to the character.

Automatic reapplication every 19 minutes:

A task (ScheduledFuture) is scheduled to reapply the exact same buffs to the same Fake Player every 19 minutes to prevent them from expiring.

This task runs indefinitely as long as the Fake Player is active.

Types of buffs applied:

Fighter or Mage buffs can be included, depending on the setup (although the actual difference is not visible in the snippet).

Examples of buffs that may be applied:

Shield, Might, Focus, Haste, Wind Walk, HP Recovery, etc.

Detection and control:

If for any reason the Fake Player disappears or is eliminated, the scheduled buff task is canceled.

Code: [Select]
package custom.FakePlayers;

import org.l2jmobius.Config;
import org.l2jmobius.commons.threads.ThreadPool;
import org.l2jmobius.gameserver.ai.CtrlIntention;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.holders.SkillHolder;
import org.l2jmobius.gameserver.model.skill.Skill;
import org.l2jmobius.gameserver.model.skill.SkillCaster;
import org.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;

import ai.AbstractNpcAI;

public class AutoBuffFakePlayers extends AbstractNpcAI
{
    private static final int[] FAKE_PLAYER_IDS = { 80001, 80101 };

    private static final SkillHolder[] FIGHTER_BUFFS = {
        new SkillHolder(1040, 4),    // Shield
        new SkillHolder(1068, 4),    // Might
        new SkillHolder(45107, 3),   // Wind Walk
        new SkillHolder(45108, 3),   // Magic Barrier
        new SkillHolder(45109, 4),   // Focus
        new SkillHolder(45110, 4),   // Death Whisper
        new SkillHolder(45111, 3),   // Haste
        new SkillHolder(45112, 3),   // Berserker Spirit
        new SkillHolder(45176, 2),   // HP Recovery
        new SkillHolder(45177, 3)    // MP Recovery
    };

    private static final SkillHolder[] MAGE_BUFFS = {
        new SkillHolder(1040, 4),    // Shield
        new SkillHolder(1047, 5),    // Mana Regeneration
        new SkillHolder(1078, 2),    // Concentration
        new SkillHolder(45107, 3),   // Wind Walk
        new SkillHolder(45104, 4),   // Acumen
        new SkillHolder(45105, 4),   // Empower
        new SkillHolder(45106, 3),   // Wild Magic
        new SkillHolder(45108, 3),   // Magic Barrier
        new SkillHolder(45112, 3),   // Berserker Spirit
        new SkillHolder(45176, 2),   // HP Recovery
        new SkillHolder(45177, 3)    // MP Recovery
    };

    // Mapa para guardar la tarea periódica de cada NPC
    private final Map<Integer, ScheduledFuture<?>> _buffCheckTasks = new ConcurrentHashMap<>();

    public AutoBuffFakePlayers()
    {
        if (Config.FAKE_PLAYERS_ENABLED)
        {
            addSpawnId(FAKE_PLAYER_IDS);
            //System.out.println("AutoBuffFakePlayers: Script cargado correctamente.");
        }
    }

    public String onSpawn(Npc npc)
    {
        //System.out.println("AutoBuffFakePlayers: " + npc.getName() + " ha spawneado.");
        startBuffing(npc);
        startBuffCheckTask(npc);
        return null;
    }

    public String onAdvEvent(String event, Npc npc, Player player)
    {
        if ((npc == null) || npc.isDead())
        {
            return null;
        }

        if ("REBUFF".equals(event))
        {
            startBuffing(npc);
        }
        // No hace falta usar timer CHECK_BUFFS manual aquí, se maneja con ThreadPool
        return null;
    }

    private void startBuffing(Npc npc)
    {
        npc.abortAttack();
        npc.abortCast();
        npc.setWalking();
        npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);

        final String aiType = npc.getTemplate().getParameters().getString("ai", "FIGHTER").toUpperCase();
        final SkillHolder[] buffs = aiType.equals("MAGE") ? MAGE_BUFFS : FIGHTER_BUFFS;

        castBuffsSequentially(npc, buffs, 0);
    }

    private void castBuffsSequentially(Npc npc, SkillHolder[] skills, int index)
    {
        if ((npc == null) || npc.isDead() || index >= skills.length)
        {
            npc.setRunning();
            npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
            startQuestTimer("REBUFF", 19 * 60 * 1000, npc, null);
            return;
        }

        final SkillHolder skillHolder = skills[index];
        final Skill skill = skillHolder.getSkill();

        if (skill != null)
        {
            npc.setTarget(null);
            npc.abortAttack();
            npc.abortCast();
            npc.setTarget(npc);
            npc.setWalking();
            npc.setCurrentMp(npc.getMaxMp());

            SkillCaster.triggerCast(npc, npc, skill);
            npc.broadcastPacket(new MagicSkillUse(npc, npc, skill.getId(), skill.getLevel(), skill.getHitTime(), skill.getReuseDelay()));

            //System.out.println("AutoBuffFakePlayers: " + npc.getName() + " casteó " + skill.getName());

            int delay = skill.getHitTime() + 500;
            getTimers().addTimer("CAST_NEXT_BUFF_" + npc.getObjectId(), delay, evnt ->
            {
                castBuffsSequentially(npc, skills, index + 1);
            });
        }
        else
        {
            getTimers().addTimer("CAST_NEXT_BUFF_" + npc.getObjectId(), 1000, evnt ->
            {
                castBuffsSequentially(npc, skills, index + 1);
            });
        }
    }

    private void startBuffCheckTask(Npc npc)
    {
        if (_buffCheckTasks.containsKey(npc.getObjectId()))
        {
            _buffCheckTasks.get(npc.getObjectId()).cancel(false);
        }

        //System.out.println("AutoBuffFakePlayers: Iniciando verificación periódica para " + npc.getName());

        ScheduledFuture<?> task = ThreadPool.scheduleAtFixedRate(() ->
        {
            if ((npc == null) || npc.isDead())
            {
                _buffCheckTasks.remove(npc.getObjectId());
                return;
            }

            //System.out.println("AutoBuffFakePlayers: [CHECK_BUFFS] Revisando buffs para: " + npc.getName() + " (ID: " + npc.getId() + ")");

            checkAndRebuff(npc);

        }, 60_000L, 60_000L); // 60 segundos delay inicial y periodo

        _buffCheckTasks.put(npc.getObjectId(), task);
    }

    private void checkAndRebuff(Npc npc)
    {
        if ((npc == null) || npc.isDead())
        {
            return;
        }

        final String aiType = npc.getTemplate().getParameters().getString("ai", "FIGHTER").toUpperCase();
        final SkillHolder[] expectedBuffs = aiType.equals("MAGE") ? MAGE_BUFFS : FIGHTER_BUFFS;

        List<SkillHolder> missingBuffs = new ArrayList<>();

        for (SkillHolder holder : expectedBuffs)
        {
            boolean hasBuff = npc.getEffectList().getEffects().stream().anyMatch(e ->
                e.getSkill().getId() == holder.getSkillId() && e.getSkill().getLevel() == holder.getSkillLevel()
            );

            if (!hasBuff)
            {
                missingBuffs.add(holder);
            }
        }

        if (!missingBuffs.isEmpty())
        {
            //System.out.println("AutoBuffFakePlayers: " + npc.getName() + " tiene buffs faltantes. Reaplicando...");
            npc.abortAttack();
            npc.abortCast();
            npc.setWalking();
            npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);

            castBuffsSequentially(npc, missingBuffs.toArray(new SkillHolder[0]), 0);
        }
/*         else
        {
            //System.out.println("AutoBuffFakePlayers: " + npc.getName() + " tiene todos los buffs.");
        } */
    }

    public static void main(String[] args)
    {
        new AutoBuffFakePlayers();
    }
}

in fpc_combat.xml

Code: [Select]
<npc id="80001" level="47" type="Monster" name="Espartaco"><!-- title="Duelist" -->
<race>HUMAN</race>
<sex>MALE</sex>
<stats int="21" str="40" con="43" men="25" dex="30" wit="35" >
<vitals hp="4000" hpRegen="10.8" mp="4000" mpRegen="3.0" />
<attack physical="494" magical="100" random="30" critical="220" accuracy="250" attackSpeed="508" type="SWORD" range="40" distance="80" width="120" />
<defence physical="722" magical="700" />
<speed>
<walk ground="82" />
<run ground="173" />
</speed>
</stats>
<status fakePlayer="true" talkable="false" attackable="true" />
<ai type="FIGHTER" clanHelpRange="1000" aggroRange="1000" isAggressive="true"> <!-- FIGHTER, ARCHER, BALANCED, MAGE, HEALER -->
<clanList>
<clan>Peteros</clan>
</clanList>
</ai>
<collision>
<radius normal="9" />
<height normal="23" />
</collision>
<parameters>
<param name="ai" value="FIGHTER" /> <!-- AutoBuffFakePlayers -->
</parameters>
<skillList>
<skill id="261" level="1" /> <!-- Triple Sonic Slash -->
<skill id="1" level="5" /> <!-- Rising Attack -->
<skill id="6" level="37" /> <!-- Sonic Blaster -->
<!-- Buff skills -->
<skill id="1040" level="4" /> <!-- Shield -->
<skill id="1068" level="4" /> <!-- Might -->
<skill id="1047" level="5" /> <!-- Mana Regeneration -->
<skill id="1078" level="2" /> <!-- Concentration -->
<skill id="45104" level="4" /> <!-- Acumen -->
<skill id="45105" level="4" /> <!-- Empower -->
<skill id="45106" level="3" /> <!-- Wild Magic -->
<skill id="45107" level="3" /> <!-- Wind Walk -->
<skill id="45108" level="3" /> <!-- Magic Barrier -->
<skill id="45109" level="4" /> <!-- Focus -->
<skill id="45110" level="4" /> <!-- Death Whisper -->
<skill id="45111" level="3" /> <!-- Haste -->
<skill id="45112" level="3" /> <!-- Berserker Spirit -->
<skill id="45113" level="4" /> <!-- Clarity -->
<skill id="45176" level="2" /> <!-- HP Recovery -->
<skill id="45177" level="3" /> <!-- MP Recovery -->
</skillList>
</npc>