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.
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
<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>