Re-addition of Sel Mahum AIs.

This commit is contained in:
MobiusDev 2018-05-21 22:24:38 +00:00
parent 29603cb853
commit ebf1e85cce
35 changed files with 4191 additions and 0 deletions

View File

@ -0,0 +1,312 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.datatables.SpawnTable;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for drill groups.
* @author GKR
*/
public final class SelMahumDrill extends AbstractNpcAI
{
private static final int[] MAHUM_CHIEFS =
{
22775, // Sel Mahum Drill Sergeant
22776, // Sel Mahum Training Officer
22778, // Sel Mahum Drill Sergeant
};
private static final int[] MAHUM_SOLDIERS =
{
22780, // Sel Mahum Recruit
22782, // Sel Mahum Recruit
22783, // Sel Mahum Soldier
22784, // Sel Mahum Recruit
22785, // Sel Mahum Soldier
};
private static final int[] CHIEF_SOCIAL_ACTIONS =
{
1,
4,
5,
7
};
private static final Actions[] SOLDIER_SOCIAL_ACTIONS =
{
Actions.SCE_TRAINING_ACTION_A,
Actions.SCE_TRAINING_ACTION_B,
Actions.SCE_TRAINING_ACTION_C,
Actions.SCE_TRAINING_ACTION_D
};
private static final NpcStringId[] CHIEF_FSTRINGS =
{
NpcStringId.HOW_DARE_YOU_ATTACK_MY_RECRUITS,
NpcStringId.WHO_IS_DISRUPTING_THE_ORDER
};
private static final NpcStringId[] SOLDIER_FSTRINGS =
{
NpcStringId.THE_DRILLMASTER_IS_DEAD,
NpcStringId.LINE_UP_THE_RANKS
};
// Chiefs event broadcast range
private static final int TRAINING_RANGE = 1000;
private static enum Actions
{
SCE_TRAINING_ACTION_A(4, -1, 2, 2333),
SCE_TRAINING_ACTION_B(1, -1, 2, 4333),
SCE_TRAINING_ACTION_C(6, 5, 4, 1000),
SCE_TRAINING_ACTION_D(7, -1, 2, 1000);
private final int _socialActionId;
private final int _altSocialActionId;
private final int _repeatCount;
private final int _repeatInterval;
private Actions(int socialActionId, int altSocialActionId, int repeatCount, int repeatInterval)
{
_socialActionId = socialActionId;
_altSocialActionId = altSocialActionId;
_repeatCount = repeatCount;
_repeatInterval = repeatInterval;
}
protected int getSocialActionId()
{
return _socialActionId;
}
protected int getAltSocialActionId()
{
return _altSocialActionId;
}
protected int getRepeatCount()
{
return _repeatCount;
}
protected int getRepeatInterval()
{
return _repeatInterval;
}
}
private SelMahumDrill()
{
addAttackId(MAHUM_CHIEFS);
addAttackId(MAHUM_SOLDIERS);
addKillId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_SOLDIERS);
addSpawnId(MAHUM_CHIEFS);
addSpawnId(MAHUM_SOLDIERS);
// Start global return home timer
startQuestTimer("return_home", 120000, null, null, true);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "do_social_action":
{
if ((npc != null) && !npc.isDead())
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
if ((npc.getVariables().getInt("BUSY_STATE") == 0) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) && npc.staysInSpawnLoc())
{
final int idx = getRandom(6);
if (idx <= (CHIEF_SOCIAL_ACTIONS.length - 1))
{
npc.broadcastSocialAction(CHIEF_SOCIAL_ACTIONS[idx]);
npc.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", idx); // Pass social action index to soldiers via script value
npc.broadcastEvent("do_social_action", TRAINING_RANGE, null);
}
}
startQuestTimer("do_social_action", 15000, npc, null);
}
else if (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
handleSocialAction(npc, SOLDIER_SOCIAL_ACTIONS[npc.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX")], false);
}
}
break;
}
case "reset_busy_state":
{
if (npc != null)
{
npc.getVariables().remove("BUSY_STATE");
npc.disableCoreAI(false);
}
break;
}
case "return_home":
{
for (int npcId : MAHUM_SOLDIERS)
{
for (L2Spawn npcSpawn : SpawnTable.getInstance().getSpawns(npcId))
{
final L2Npc soldier = npcSpawn.getLastSpawn();
if ((soldier != null) && !soldier.isDead() && (npcSpawn.getName() != null) && npcSpawn.getName().startsWith("smtg_drill_group") && !soldier.staysInSpawnLoc() && ((soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)))
{
soldier.setHeading(npcSpawn.getHeading());
soldier.teleToLocation(npcSpawn.getLocation(), false);
}
}
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
{
if ((getRandom(10) < 1) && (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId())))
{
npc.broadcastEvent("ATTACKED", 1000, null);
}
return super.onAttack(npc, attacker, damage, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
if ((receiver != null) && !receiver.isDead() && receiver.isInMySpawnGroup(sender))
{
switch (eventName)
{
case "do_social_action":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
final int actionIndex = sender.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX");
receiver.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", actionIndex);
handleSocialAction(receiver, SOLDIER_SOCIAL_ACTIONS[actionIndex], true);
}
break;
}
case "CHIEF_DIED":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
if (getRandom(4) < 1)
{
receiver.broadcastSay(ChatType.NPC_GENERAL, SOLDIER_FSTRINGS[getRandom(2)]);
}
if (receiver.canBeAttacked())
{
((L2Attackable) receiver).clearAggroList();
}
receiver.disableCoreAI(true);
receiver.getVariables().set("BUSY_STATE", 1);
receiver.setRunning();
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location((receiver.getX() + getRandom(-800, 800)), (receiver.getY() + getRandom(-800, 800)), receiver.getZ(), receiver.getHeading()));
startQuestTimer("reset_busy_state", 5000, receiver, null);
}
break;
}
case "ATTACKED":
{
if (CommonUtil.contains(MAHUM_CHIEFS, receiver.getId()))
{
receiver.broadcastSay(ChatType.NPC_GENERAL, CHIEF_FSTRINGS[getRandom(2)]);
}
break;
}
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
npc.broadcastEvent("CHIEF_DIED", TRAINING_RANGE, null);
return null;
}
@Override
public String onSpawn(L2Npc npc)
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
startQuestTimer("do_social_action", 15000, npc, null);
}
else if ((getRandom(18) < 1) && CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
npc.getVariables().set("SOCIAL_ACTION_ALT_BEHAVIOR", 1);
}
// Restore AI handling by core
npc.disableCoreAI(false);
return super.onSpawn(npc);
}
private void handleSocialAction(L2Npc npc, Actions action, boolean firstCall)
{
if ((npc.getVariables().getInt("BUSY_STATE") != 0) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) || !npc.staysInSpawnLoc())
{
return;
}
final int socialActionId = (npc.getVariables().getInt("SOCIAL_ACTION_ALT_BEHAVIOR") == 0) ? action.getSocialActionId() : action.getAltSocialActionId();
if (socialActionId < 0)
{
return;
}
if (firstCall)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", action.getRepeatCount());
}
npc.broadcastSocialAction(socialActionId);
final int remainedCount = npc.getVariables().getInt("SOCIAL_ACTION_REMAINED_COUNT");
if (remainedCount > 0)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", (remainedCount - 1));
startQuestTimer("do_social_action", action.getRepeatInterval(), npc, null);
}
}
public static void main(String[] args)
{
new SelMahumDrill();
}
}

View File

@ -0,0 +1,410 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.data.xml.impl.SkillData;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for squads and chefs.
* @author GKR
*/
public final class SelMahumSquad extends AbstractNpcAI
{
// NPCs
private static final int CHEF = 18908;
private static final int FIRE = 18927;
private static final int STOVE = 18933;
private static final int OHS_Weapon = 15280;
private static final int THS_Weapon = 15281;
// Sel Mahum Squad Leaders
private static final int[] SQUAD_LEADERS =
{
22786,
22787,
22788
};
private static final NpcStringId[] CHEF_FSTRINGS =
{
NpcStringId.I_BROUGHT_THE_FOOD,
NpcStringId.COME_AND_EAT
};
private static final int FIRE_EFFECT_BURN = 1;
private static final int FIRE_EFFECT_NONE = 2;
private static final int MAHUM_EFFECT_EAT = 1;
private static final int MAHUM_EFFECT_SLEEP = 2;
private static final int MAHUM_EFFECT_NONE = 3;
private SelMahumSquad()
{
addAttackId(CHEF);
addAttackId(SQUAD_LEADERS);
addEventReceivedId(CHEF, FIRE, STOVE);
addEventReceivedId(SQUAD_LEADERS);
addFactionCallId(SQUAD_LEADERS);
addKillId(CHEF);
addMoveFinishedId(SQUAD_LEADERS);
addNodeArrivedId(CHEF);
addSkillSeeId(STOVE);
addSpawnId(CHEF, FIRE);
addSpawnId(SQUAD_LEADERS);
addSpellFinishedId(CHEF);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "chef_disable_reward":
{
npc.getVariables().set("REWARD_TIME_GONE", 1);
break;
}
case "chef_heal_player":
{
healPlayer(npc, player);
break;
}
case "chef_remove_invul":
{
if (npc.isMonster())
{
npc.setIsInvul(false);
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
if ((player != null) && !player.isDead() && npc.isInSurroundingRegion(player))
{
addAttackPlayerDesire(npc, player);
}
}
break;
}
case "chef_set_invul":
{
if (!npc.isDead())
{
npc.setIsInvul(true);
}
break;
}
case "fire":
{
startQuestTimer("fire", 30000 + getRandom(5000), npc, null);
npc.setDisplayEffect(FIRE_EFFECT_NONE);
if (getRandom(GameTimeController.getInstance().isNight() ? 2 : 4) < 1)
{
npc.setDisplayEffect(FIRE_EFFECT_BURN); // fire burns
npc.broadcastEvent("SCE_CAMPFIRE_START", 600, null);
}
else
{
npc.setDisplayEffect(FIRE_EFFECT_NONE); // fire goes out
npc.broadcastEvent("SCE_CAMPFIRE_END", 600, null);
}
break;
}
case "fire_arrived":
{
// myself.i_quest0 = 1;
npc.setWalking();
npc.setTarget(npc);
if (!npc.isRandomWalkingEnabled())
{
final Skill skill = SkillData.getInstance().getSkill(6331, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_SLEEP);
}
if (npc.getVariables().getInt("BUSY_STATE") == 1) // Eating
{
final Skill skill = SkillData.getInstance().getSkill(6332, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_EAT);
}
startQuestTimer("remove_effects", 300000, npc, null);
break;
}
case "notify_dinner":
{
npc.broadcastEvent("SCE_DINNER_EAT", 600, null);
break;
}
case "remove_effects":
{
// myself.i_quest0 = 0;
npc.setRunning();
npc.setDisplayEffect(MAHUM_EFFECT_NONE);
break;
}
case "reset_full_bottle_prize":
{
npc.getVariables().remove("FULL_BARREL_REWARDING_PLAYER");
break;
}
case "return_from_fire":
{
if (npc.isMonster() && !npc.isDead())
{
((L2MonsterInstance) npc).returnHome();
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
{
if ((npc.getId() == CHEF) && (npc.getVariables().getInt("BUSY_STATE") == 0))
{
if (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") == 0)
{
startQuestTimer("chef_remove_invul", 180000, npc, attacker);
startQuestTimer("chef_disable_reward", 60000, npc, null);
npc.getVariables().set("INVUL_REMOVE_TIMER_STARTED", 1);
}
startQuestTimer("chef_heal_player", 1000, npc, attacker);
startQuestTimer("chef_set_invul", 60000, npc, null);
npc.getVariables().set("BUSY_STATE", 1);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
handlePreAttackMotion(npc);
}
return super.onAttack(npc, attacker, damage, isSummon, skill);
}
@Override
public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isSummon)
{
handlePreAttackMotion(npc);
return super.onFactionCall(npc, caller, attacker, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
switch (eventName)
{
case "SCE_DINNER_CHECK":
{
if (receiver.getId() == FIRE)
{
receiver.setDisplayEffect(FIRE_EFFECT_BURN);
final L2Npc stove = addSpawn(STOVE, receiver.getX(), receiver.getY(), receiver.getZ() + 100, 0, false, 0);
stove.setSummoner(receiver);
startQuestTimer("notify_dinner", 2000, receiver, null); // @SCE_DINNER_EAT
sender.broadcastSay(ChatType.NPC_GENERAL, CHEF_FSTRINGS[getRandom(2)], 1250);
}
break;
}
case "SCE_CAMPFIRE_START":
{
if (receiver.isRandomWalkingEnabled() && !receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.setRunning();
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_CAMPFIRE_END":
{
if ((receiver.getId() == STOVE) && (receiver.getSummoner() == sender))
{
receiver.deleteMe();
}
else if ((receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(true);
receiver.getVariables().remove("BUSY_STATE");
receiver.setRHandId(THS_Weapon);
startQuestTimer("return_from_fire", 3000, receiver, null);
}
break;
}
case "SCE_DINNER_EAT":
{
if (!receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && (receiver.getVariables().getInt("BUSY_STATE", 0) == 0) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
if (!receiver.isRandomWalkingEnabled()) // i_ai0 == 1
{
receiver.setRHandId(THS_Weapon);
}
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.getVariables().set("BUSY_STATE", 1); // Eating - i_ai3 = 1
receiver.setRunning();
receiver.broadcastSay(ChatType.NPC_GENERAL, (getRandom(3) < 1) ? NpcStringId.LOOKS_DELICIOUS : NpcStringId.LET_S_GO_EAT);
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_SOUP_FAILURE":
{
if (CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.getVariables().set("FULL_BARREL_REWARDING_PLAYER", reference.getObjectId()); // TODO: Use it in 289 quest
startQuestTimer("reset_full_bottle_prize", 180000, receiver, null);
}
break;
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
if (npc.isMonster() && (npc.getVariables().getInt("REWARD_TIME_GONE") == 0))
{
npc.dropItem(killer, 15492, 1);
}
cancelQuestTimer("chef_remove_invul", npc, null);
cancelQuestTimer("chef_disable_reward", npc, null);
cancelQuestTimer("chef_heal_player", npc, null);
cancelQuestTimer("chef_set_invul", npc, null);
return super.onKill(npc, killer, isSummon);
}
@Override
public void onMoveFinished(L2Npc npc)
{
// Npc moves to fire
if (!npc.isRandomWalkingEnabled() && (npc.getX() == npc.getVariables().getInt("DESTINATION_X")) && (npc.getY() == npc.getVariables().getInt("DESTINATION_Y")))
{
npc.setRHandId(OHS_Weapon);
startQuestTimer("fire_arrived", 3000, npc, null);
}
}
@Override
public void onNodeArrived(L2Npc npc)
{
npc.broadcastEvent("SCE_DINNER_CHECK", 300, null);
}
@Override
public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
{
if ((npc.getId() == STOVE) && (skill.getId() == 9075) && CommonUtil.contains(targets, npc))
{
npc.doCast(SkillData.getInstance().getSkill(6688, 1));
npc.broadcastEvent("SCE_SOUP_FAILURE", 600, caster);
}
return super.onSkillSee(npc, caster, skill, targets, isSummon);
}
@Override
public String onSpawn(L2Npc npc)
{
if (npc.getId() == CHEF)
{
npc.setIsInvul(false);
}
else if (npc.getId() == FIRE)
{
startQuestTimer("fire", 1000, npc, null);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
npc.setDisplayEffect(3);
npc.setRandomWalking(true);
}
return super.onSpawn(npc);
}
@Override
public String onSpellFinished(L2Npc npc, L2PcInstance player, Skill skill)
{
if ((skill != null) && (skill.getId() == 6330))
{
healPlayer(npc, player);
}
return super.onSpellFinished(npc, player, skill);
}
private void healPlayer(L2Npc npc, L2PcInstance player)
{
if ((player != null) && !player.isDead() && (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") != 1) && ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_CAST)))
{
npc.setTarget(player);
npc.doCast(SkillData.getInstance().getSkill(6330, 1));
}
else
{
cancelQuestTimer("chef_set_invul", npc, null);
npc.getVariables().remove("BUSY_STATE");
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
npc.setWalking();
}
}
private void handlePreAttackMotion(L2Npc attacked)
{
cancelQuestTimer("remove_effects", attacked, null);
attacked.getVariables().remove("BUSY_STATE");
attacked.setRandomWalking(true);
attacked.setDisplayEffect(MAHUM_EFFECT_NONE);
if (attacked.getRightHandItem() == OHS_Weapon)
{
attacked.setRHandId(THS_Weapon);
}
// TODO: Check about i_quest0
}
public static void main(String[] args)
{
new SelMahumSquad();
}
}

View File

@ -40,6 +40,8 @@ import com.l2jmobius.gameserver.model.WalkInfo;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.tasks.npc.walker.ArrivedTask;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.holders.NpcRoutesHolder;
import com.l2jmobius.gameserver.network.NpcStringId;
@ -421,6 +423,9 @@ public final class WalkingManager implements IGameXmlReader
{
if (_activeRoutes.containsKey(npc.getObjectId()))
{
// Notify quest
EventDispatcher.getInstance().notifyEventAsync(new OnNpcMoveNodeArrived(npc), npc);
final WalkInfo walk = _activeRoutes.get(npc.getObjectId());
// Opposite should not happen... but happens sometime

View File

@ -89,6 +89,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcDespawn;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcEventReceived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -806,6 +807,30 @@ public abstract class AbstractScript extends ManagedScript implements IEventTime
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, int... npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, Collection<Integer> npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} finishes to move on its route.
* @param callback

View File

@ -48,6 +48,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcManorBypass;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMenuSelect;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -187,6 +188,7 @@ public enum EventType
ON_NPC_FIRST_TALK(OnNpcFirstTalk.class, void.class),
ON_NPC_HATE(OnAttackableHate.class, void.class, TerminateReturn.class),
ON_NPC_MOVE_FINISHED(OnNpcMoveFinished.class, void.class),
ON_NPC_MOVE_NODE_ARRIVED(OnNpcMoveNodeArrived.class, void.class),
ON_NPC_MOVE_ROUTE_FINISHED(OnNpcMoveRouteFinished.class, void.class),
ON_NPC_QUEST_START(null, void.class),
ON_NPC_SKILL_FINISHED(OnNpcSkillFinished.class, void.class),

View File

@ -0,0 +1,45 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.events.impl.character.npc;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.events.EventType;
import com.l2jmobius.gameserver.model.events.impl.IBaseEvent;
/**
* @author UnAfraid
*/
public class OnNpcMoveNodeArrived implements IBaseEvent
{
private final L2Npc _npc;
public OnNpcMoveNodeArrived(L2Npc npc)
{
_npc = npc;
}
public L2Npc getNpc()
{
return _npc;
}
@Override
public EventType getType()
{
return EventType.ON_NPC_MOVE_NODE_ARRIVED;
}
}

View File

@ -951,6 +951,21 @@ public class Quest extends AbstractScript implements IIdentifiable
}
}
/**
* @param npc
*/
public final void notifyNodeArrived(L2Npc npc)
{
try
{
onNodeArrived(npc);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Exception on onNodeArrived() in notifyNodeArrived(): " + e.getMessage(), e);
}
}
/**
* @param npc
*/
@ -1390,6 +1405,14 @@ public class Quest extends AbstractScript implements IIdentifiable
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive a walking node
* @param npc registered NPC
*/
public void onNodeArrived(L2Npc npc)
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive to last node
* @param npc registered NPC
@ -2200,6 +2223,24 @@ public class Quest extends AbstractScript implements IIdentifiable
setNpcMoveFinishedId(event -> notifyMoveFinished(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(int... npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(Collection<Integer> npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onRouteFinished trigger for NPC
* @param npcIds the IDs of the NPCs to register

View File

@ -0,0 +1,312 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.datatables.SpawnTable;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for drill groups.
* @author GKR
*/
public final class SelMahumDrill extends AbstractNpcAI
{
private static final int[] MAHUM_CHIEFS =
{
22775, // Sel Mahum Drill Sergeant
22776, // Sel Mahum Training Officer
22778, // Sel Mahum Drill Sergeant
};
private static final int[] MAHUM_SOLDIERS =
{
22780, // Sel Mahum Recruit
22782, // Sel Mahum Recruit
22783, // Sel Mahum Soldier
22784, // Sel Mahum Recruit
22785, // Sel Mahum Soldier
};
private static final int[] CHIEF_SOCIAL_ACTIONS =
{
1,
4,
5,
7
};
private static final Actions[] SOLDIER_SOCIAL_ACTIONS =
{
Actions.SCE_TRAINING_ACTION_A,
Actions.SCE_TRAINING_ACTION_B,
Actions.SCE_TRAINING_ACTION_C,
Actions.SCE_TRAINING_ACTION_D
};
private static final NpcStringId[] CHIEF_FSTRINGS =
{
NpcStringId.HOW_DARE_YOU_ATTACK_MY_RECRUITS,
NpcStringId.WHO_IS_DISRUPTING_THE_ORDER
};
private static final NpcStringId[] SOLDIER_FSTRINGS =
{
NpcStringId.THE_DRILLMASTER_IS_DEAD,
NpcStringId.LINE_UP_THE_RANKS
};
// Chiefs event broadcast range
private static final int TRAINING_RANGE = 1000;
private static enum Actions
{
SCE_TRAINING_ACTION_A(4, -1, 2, 2333),
SCE_TRAINING_ACTION_B(1, -1, 2, 4333),
SCE_TRAINING_ACTION_C(6, 5, 4, 1000),
SCE_TRAINING_ACTION_D(7, -1, 2, 1000);
private final int _socialActionId;
private final int _altSocialActionId;
private final int _repeatCount;
private final int _repeatInterval;
private Actions(int socialActionId, int altSocialActionId, int repeatCount, int repeatInterval)
{
_socialActionId = socialActionId;
_altSocialActionId = altSocialActionId;
_repeatCount = repeatCount;
_repeatInterval = repeatInterval;
}
protected int getSocialActionId()
{
return _socialActionId;
}
protected int getAltSocialActionId()
{
return _altSocialActionId;
}
protected int getRepeatCount()
{
return _repeatCount;
}
protected int getRepeatInterval()
{
return _repeatInterval;
}
}
private SelMahumDrill()
{
addAttackId(MAHUM_CHIEFS);
addAttackId(MAHUM_SOLDIERS);
addKillId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_SOLDIERS);
addSpawnId(MAHUM_CHIEFS);
addSpawnId(MAHUM_SOLDIERS);
// Start global return home timer
startQuestTimer("return_home", 120000, null, null, true);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "do_social_action":
{
if ((npc != null) && !npc.isDead())
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
if ((npc.getVariables().getInt("BUSY_STATE") == 0) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) && npc.staysInSpawnLoc())
{
final int idx = getRandom(6);
if (idx <= (CHIEF_SOCIAL_ACTIONS.length - 1))
{
npc.broadcastSocialAction(CHIEF_SOCIAL_ACTIONS[idx]);
npc.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", idx); // Pass social action index to soldiers via script value
npc.broadcastEvent("do_social_action", TRAINING_RANGE, null);
}
}
startQuestTimer("do_social_action", 15000, npc, null);
}
else if (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
handleSocialAction(npc, SOLDIER_SOCIAL_ACTIONS[npc.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX")], false);
}
}
break;
}
case "reset_busy_state":
{
if (npc != null)
{
npc.getVariables().remove("BUSY_STATE");
npc.disableCoreAI(false);
}
break;
}
case "return_home":
{
for (int npcId : MAHUM_SOLDIERS)
{
for (L2Spawn npcSpawn : SpawnTable.getInstance().getSpawns(npcId))
{
final L2Npc soldier = npcSpawn.getLastSpawn();
if ((soldier != null) && !soldier.isDead() && (npcSpawn.getName() != null) && npcSpawn.getName().startsWith("smtg_drill_group") && !soldier.staysInSpawnLoc() && ((soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)))
{
soldier.setHeading(npcSpawn.getHeading());
soldier.teleToLocation(npcSpawn.getLocation(), false);
}
}
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
{
if ((getRandom(10) < 1) && (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId())))
{
npc.broadcastEvent("ATTACKED", 1000, null);
}
return super.onAttack(npc, attacker, damage, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
if ((receiver != null) && !receiver.isDead() && receiver.isInMySpawnGroup(sender))
{
switch (eventName)
{
case "do_social_action":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
final int actionIndex = sender.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX");
receiver.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", actionIndex);
handleSocialAction(receiver, SOLDIER_SOCIAL_ACTIONS[actionIndex], true);
}
break;
}
case "CHIEF_DIED":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
if (getRandom(4) < 1)
{
receiver.broadcastSay(ChatType.NPC_GENERAL, SOLDIER_FSTRINGS[getRandom(2)]);
}
if (receiver.canBeAttacked())
{
((L2Attackable) receiver).clearAggroList();
}
receiver.disableCoreAI(true);
receiver.getVariables().set("BUSY_STATE", 1);
receiver.setRunning();
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location((receiver.getX() + getRandom(-800, 800)), (receiver.getY() + getRandom(-800, 800)), receiver.getZ(), receiver.getHeading()));
startQuestTimer("reset_busy_state", 5000, receiver, null);
}
break;
}
case "ATTACKED":
{
if (CommonUtil.contains(MAHUM_CHIEFS, receiver.getId()))
{
receiver.broadcastSay(ChatType.NPC_GENERAL, CHIEF_FSTRINGS[getRandom(2)]);
}
break;
}
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
npc.broadcastEvent("CHIEF_DIED", TRAINING_RANGE, null);
return null;
}
@Override
public String onSpawn(L2Npc npc)
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
startQuestTimer("do_social_action", 15000, npc, null);
}
else if ((getRandom(18) < 1) && CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
npc.getVariables().set("SOCIAL_ACTION_ALT_BEHAVIOR", 1);
}
// Restore AI handling by core
npc.disableCoreAI(false);
return super.onSpawn(npc);
}
private void handleSocialAction(L2Npc npc, Actions action, boolean firstCall)
{
if ((npc.getVariables().getInt("BUSY_STATE") != 0) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) || !npc.staysInSpawnLoc())
{
return;
}
final int socialActionId = (npc.getVariables().getInt("SOCIAL_ACTION_ALT_BEHAVIOR") == 0) ? action.getSocialActionId() : action.getAltSocialActionId();
if (socialActionId < 0)
{
return;
}
if (firstCall)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", action.getRepeatCount());
}
npc.broadcastSocialAction(socialActionId);
final int remainedCount = npc.getVariables().getInt("SOCIAL_ACTION_REMAINED_COUNT");
if (remainedCount > 0)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", (remainedCount - 1));
startQuestTimer("do_social_action", action.getRepeatInterval(), npc, null);
}
}
public static void main(String[] args)
{
new SelMahumDrill();
}
}

View File

@ -0,0 +1,410 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.data.xml.impl.SkillData;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for squads and chefs.
* @author GKR
*/
public final class SelMahumSquad extends AbstractNpcAI
{
// NPCs
private static final int CHEF = 18908;
private static final int FIRE = 18927;
private static final int STOVE = 18933;
private static final int OHS_Weapon = 15280;
private static final int THS_Weapon = 15281;
// Sel Mahum Squad Leaders
private static final int[] SQUAD_LEADERS =
{
22786,
22787,
22788
};
private static final NpcStringId[] CHEF_FSTRINGS =
{
NpcStringId.I_BROUGHT_THE_FOOD,
NpcStringId.COME_AND_EAT
};
private static final int FIRE_EFFECT_BURN = 1;
private static final int FIRE_EFFECT_NONE = 2;
private static final int MAHUM_EFFECT_EAT = 1;
private static final int MAHUM_EFFECT_SLEEP = 2;
private static final int MAHUM_EFFECT_NONE = 3;
private SelMahumSquad()
{
addAttackId(CHEF);
addAttackId(SQUAD_LEADERS);
addEventReceivedId(CHEF, FIRE, STOVE);
addEventReceivedId(SQUAD_LEADERS);
addFactionCallId(SQUAD_LEADERS);
addKillId(CHEF);
addMoveFinishedId(SQUAD_LEADERS);
addNodeArrivedId(CHEF);
addSkillSeeId(STOVE);
addSpawnId(CHEF, FIRE);
addSpawnId(SQUAD_LEADERS);
addSpellFinishedId(CHEF);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "chef_disable_reward":
{
npc.getVariables().set("REWARD_TIME_GONE", 1);
break;
}
case "chef_heal_player":
{
healPlayer(npc, player);
break;
}
case "chef_remove_invul":
{
if (npc.isMonster())
{
npc.setIsInvul(false);
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
if ((player != null) && !player.isDead() && npc.isInSurroundingRegion(player))
{
addAttackPlayerDesire(npc, player);
}
}
break;
}
case "chef_set_invul":
{
if (!npc.isDead())
{
npc.setIsInvul(true);
}
break;
}
case "fire":
{
startQuestTimer("fire", 30000 + getRandom(5000), npc, null);
npc.setDisplayEffect(FIRE_EFFECT_NONE);
if (getRandom(GameTimeController.getInstance().isNight() ? 2 : 4) < 1)
{
npc.setDisplayEffect(FIRE_EFFECT_BURN); // fire burns
npc.broadcastEvent("SCE_CAMPFIRE_START", 600, null);
}
else
{
npc.setDisplayEffect(FIRE_EFFECT_NONE); // fire goes out
npc.broadcastEvent("SCE_CAMPFIRE_END", 600, null);
}
break;
}
case "fire_arrived":
{
// myself.i_quest0 = 1;
npc.setWalking();
npc.setTarget(npc);
if (!npc.isRandomWalkingEnabled())
{
final Skill skill = SkillData.getInstance().getSkill(6331, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_SLEEP);
}
if (npc.getVariables().getInt("BUSY_STATE") == 1) // Eating
{
final Skill skill = SkillData.getInstance().getSkill(6332, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_EAT);
}
startQuestTimer("remove_effects", 300000, npc, null);
break;
}
case "notify_dinner":
{
npc.broadcastEvent("SCE_DINNER_EAT", 600, null);
break;
}
case "remove_effects":
{
// myself.i_quest0 = 0;
npc.setRunning();
npc.setDisplayEffect(MAHUM_EFFECT_NONE);
break;
}
case "reset_full_bottle_prize":
{
npc.getVariables().remove("FULL_BARREL_REWARDING_PLAYER");
break;
}
case "return_from_fire":
{
if (npc.isMonster() && !npc.isDead())
{
((L2MonsterInstance) npc).returnHome();
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
{
if ((npc.getId() == CHEF) && (npc.getVariables().getInt("BUSY_STATE") == 0))
{
if (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") == 0)
{
startQuestTimer("chef_remove_invul", 180000, npc, attacker);
startQuestTimer("chef_disable_reward", 60000, npc, null);
npc.getVariables().set("INVUL_REMOVE_TIMER_STARTED", 1);
}
startQuestTimer("chef_heal_player", 1000, npc, attacker);
startQuestTimer("chef_set_invul", 60000, npc, null);
npc.getVariables().set("BUSY_STATE", 1);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
handlePreAttackMotion(npc);
}
return super.onAttack(npc, attacker, damage, isSummon, skill);
}
@Override
public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isSummon)
{
handlePreAttackMotion(npc);
return super.onFactionCall(npc, caller, attacker, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
switch (eventName)
{
case "SCE_DINNER_CHECK":
{
if (receiver.getId() == FIRE)
{
receiver.setDisplayEffect(FIRE_EFFECT_BURN);
final L2Npc stove = addSpawn(STOVE, receiver.getX(), receiver.getY(), receiver.getZ() + 100, 0, false, 0);
stove.setSummoner(receiver);
startQuestTimer("notify_dinner", 2000, receiver, null); // @SCE_DINNER_EAT
sender.broadcastSay(ChatType.NPC_GENERAL, CHEF_FSTRINGS[getRandom(2)], 1250);
}
break;
}
case "SCE_CAMPFIRE_START":
{
if (receiver.isRandomWalkingEnabled() && !receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.setRunning();
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_CAMPFIRE_END":
{
if ((receiver.getId() == STOVE) && (receiver.getSummoner() == sender))
{
receiver.deleteMe();
}
else if ((receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(true);
receiver.getVariables().remove("BUSY_STATE");
receiver.setRHandId(THS_Weapon);
startQuestTimer("return_from_fire", 3000, receiver, null);
}
break;
}
case "SCE_DINNER_EAT":
{
if (!receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && (receiver.getVariables().getInt("BUSY_STATE", 0) == 0) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
if (!receiver.isRandomWalkingEnabled()) // i_ai0 == 1
{
receiver.setRHandId(THS_Weapon);
}
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.getVariables().set("BUSY_STATE", 1); // Eating - i_ai3 = 1
receiver.setRunning();
receiver.broadcastSay(ChatType.NPC_GENERAL, (getRandom(3) < 1) ? NpcStringId.LOOKS_DELICIOUS : NpcStringId.LET_S_GO_EAT);
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_SOUP_FAILURE":
{
if (CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.getVariables().set("FULL_BARREL_REWARDING_PLAYER", reference.getObjectId()); // TODO: Use it in 289 quest
startQuestTimer("reset_full_bottle_prize", 180000, receiver, null);
}
break;
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
if (npc.isMonster() && (npc.getVariables().getInt("REWARD_TIME_GONE") == 0))
{
npc.dropItem(killer, 15492, 1);
}
cancelQuestTimer("chef_remove_invul", npc, null);
cancelQuestTimer("chef_disable_reward", npc, null);
cancelQuestTimer("chef_heal_player", npc, null);
cancelQuestTimer("chef_set_invul", npc, null);
return super.onKill(npc, killer, isSummon);
}
@Override
public void onMoveFinished(L2Npc npc)
{
// Npc moves to fire
if (!npc.isRandomWalkingEnabled() && (npc.getX() == npc.getVariables().getInt("DESTINATION_X")) && (npc.getY() == npc.getVariables().getInt("DESTINATION_Y")))
{
npc.setRHandId(OHS_Weapon);
startQuestTimer("fire_arrived", 3000, npc, null);
}
}
@Override
public void onNodeArrived(L2Npc npc)
{
npc.broadcastEvent("SCE_DINNER_CHECK", 300, null);
}
@Override
public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
{
if ((npc.getId() == STOVE) && (skill.getId() == 9075) && CommonUtil.contains(targets, npc))
{
npc.doCast(SkillData.getInstance().getSkill(6688, 1));
npc.broadcastEvent("SCE_SOUP_FAILURE", 600, caster);
}
return super.onSkillSee(npc, caster, skill, targets, isSummon);
}
@Override
public String onSpawn(L2Npc npc)
{
if (npc.getId() == CHEF)
{
npc.setIsInvul(false);
}
else if (npc.getId() == FIRE)
{
startQuestTimer("fire", 1000, npc, null);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
npc.setDisplayEffect(3);
npc.setRandomWalking(true);
}
return super.onSpawn(npc);
}
@Override
public String onSpellFinished(L2Npc npc, L2PcInstance player, Skill skill)
{
if ((skill != null) && (skill.getId() == 6330))
{
healPlayer(npc, player);
}
return super.onSpellFinished(npc, player, skill);
}
private void healPlayer(L2Npc npc, L2PcInstance player)
{
if ((player != null) && !player.isDead() && (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") != 1) && ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_CAST)))
{
npc.setTarget(player);
npc.doCast(SkillData.getInstance().getSkill(6330, 1));
}
else
{
cancelQuestTimer("chef_set_invul", npc, null);
npc.getVariables().remove("BUSY_STATE");
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
npc.setWalking();
}
}
private void handlePreAttackMotion(L2Npc attacked)
{
cancelQuestTimer("remove_effects", attacked, null);
attacked.getVariables().remove("BUSY_STATE");
attacked.setRandomWalking(true);
attacked.setDisplayEffect(MAHUM_EFFECT_NONE);
if (attacked.getRightHandItem() == OHS_Weapon)
{
attacked.setRHandId(THS_Weapon);
}
// TODO: Check about i_quest0
}
public static void main(String[] args)
{
new SelMahumSquad();
}
}

View File

@ -40,6 +40,8 @@ import com.l2jmobius.gameserver.model.WalkInfo;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.tasks.npc.walker.ArrivedTask;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.holders.NpcRoutesHolder;
import com.l2jmobius.gameserver.network.NpcStringId;
@ -421,6 +423,9 @@ public final class WalkingManager implements IGameXmlReader
{
if (_activeRoutes.containsKey(npc.getObjectId()))
{
// Notify quest
EventDispatcher.getInstance().notifyEventAsync(new OnNpcMoveNodeArrived(npc), npc);
final WalkInfo walk = _activeRoutes.get(npc.getObjectId());
// Opposite should not happen... but happens sometime

View File

@ -89,6 +89,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcDespawn;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcEventReceived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -806,6 +807,30 @@ public abstract class AbstractScript extends ManagedScript implements IEventTime
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, int... npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, Collection<Integer> npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} finishes to move on its route.
* @param callback

View File

@ -48,6 +48,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcManorBypass;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMenuSelect;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -187,6 +188,7 @@ public enum EventType
ON_NPC_FIRST_TALK(OnNpcFirstTalk.class, void.class),
ON_NPC_HATE(OnAttackableHate.class, void.class, TerminateReturn.class),
ON_NPC_MOVE_FINISHED(OnNpcMoveFinished.class, void.class),
ON_NPC_MOVE_NODE_ARRIVED(OnNpcMoveNodeArrived.class, void.class),
ON_NPC_MOVE_ROUTE_FINISHED(OnNpcMoveRouteFinished.class, void.class),
ON_NPC_QUEST_START(null, void.class),
ON_NPC_SKILL_FINISHED(OnNpcSkillFinished.class, void.class),

View File

@ -0,0 +1,45 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.events.impl.character.npc;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.events.EventType;
import com.l2jmobius.gameserver.model.events.impl.IBaseEvent;
/**
* @author UnAfraid
*/
public class OnNpcMoveNodeArrived implements IBaseEvent
{
private final L2Npc _npc;
public OnNpcMoveNodeArrived(L2Npc npc)
{
_npc = npc;
}
public L2Npc getNpc()
{
return _npc;
}
@Override
public EventType getType()
{
return EventType.ON_NPC_MOVE_NODE_ARRIVED;
}
}

View File

@ -953,6 +953,21 @@ public class Quest extends AbstractScript implements IIdentifiable
}
}
/**
* @param npc
*/
public final void notifyNodeArrived(L2Npc npc)
{
try
{
onNodeArrived(npc);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Exception on onNodeArrived() in notifyNodeArrived(): " + e.getMessage(), e);
}
}
/**
* @param npc
*/
@ -1392,6 +1407,14 @@ public class Quest extends AbstractScript implements IIdentifiable
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive a walking node
* @param npc registered NPC
*/
public void onNodeArrived(L2Npc npc)
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive to last node
* @param npc registered NPC
@ -2202,6 +2225,24 @@ public class Quest extends AbstractScript implements IIdentifiable
setNpcMoveFinishedId(event -> notifyMoveFinished(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(int... npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(Collection<Integer> npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onRouteFinished trigger for NPC
* @param npcIds the IDs of the NPCs to register

View File

@ -0,0 +1,312 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.datatables.SpawnTable;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for drill groups.
* @author GKR
*/
public final class SelMahumDrill extends AbstractNpcAI
{
private static final int[] MAHUM_CHIEFS =
{
22775, // Sel Mahum Drill Sergeant
22776, // Sel Mahum Training Officer
22778, // Sel Mahum Drill Sergeant
};
private static final int[] MAHUM_SOLDIERS =
{
22780, // Sel Mahum Recruit
22782, // Sel Mahum Recruit
22783, // Sel Mahum Soldier
22784, // Sel Mahum Recruit
22785, // Sel Mahum Soldier
};
private static final int[] CHIEF_SOCIAL_ACTIONS =
{
1,
4,
5,
7
};
private static final Actions[] SOLDIER_SOCIAL_ACTIONS =
{
Actions.SCE_TRAINING_ACTION_A,
Actions.SCE_TRAINING_ACTION_B,
Actions.SCE_TRAINING_ACTION_C,
Actions.SCE_TRAINING_ACTION_D
};
private static final NpcStringId[] CHIEF_FSTRINGS =
{
NpcStringId.HOW_DARE_YOU_ATTACK_MY_RECRUITS,
NpcStringId.WHO_IS_DISRUPTING_THE_ORDER
};
private static final NpcStringId[] SOLDIER_FSTRINGS =
{
NpcStringId.THE_DRILLMASTER_IS_DEAD,
NpcStringId.LINE_UP_THE_RANKS
};
// Chiefs event broadcast range
private static final int TRAINING_RANGE = 1000;
private static enum Actions
{
SCE_TRAINING_ACTION_A(4, -1, 2, 2333),
SCE_TRAINING_ACTION_B(1, -1, 2, 4333),
SCE_TRAINING_ACTION_C(6, 5, 4, 1000),
SCE_TRAINING_ACTION_D(7, -1, 2, 1000);
private final int _socialActionId;
private final int _altSocialActionId;
private final int _repeatCount;
private final int _repeatInterval;
private Actions(int socialActionId, int altSocialActionId, int repeatCount, int repeatInterval)
{
_socialActionId = socialActionId;
_altSocialActionId = altSocialActionId;
_repeatCount = repeatCount;
_repeatInterval = repeatInterval;
}
protected int getSocialActionId()
{
return _socialActionId;
}
protected int getAltSocialActionId()
{
return _altSocialActionId;
}
protected int getRepeatCount()
{
return _repeatCount;
}
protected int getRepeatInterval()
{
return _repeatInterval;
}
}
private SelMahumDrill()
{
addAttackId(MAHUM_CHIEFS);
addAttackId(MAHUM_SOLDIERS);
addKillId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_SOLDIERS);
addSpawnId(MAHUM_CHIEFS);
addSpawnId(MAHUM_SOLDIERS);
// Start global return home timer
startQuestTimer("return_home", 120000, null, null, true);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "do_social_action":
{
if ((npc != null) && !npc.isDead())
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
if ((npc.getVariables().getInt("BUSY_STATE") == 0) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) && npc.staysInSpawnLoc())
{
final int idx = getRandom(6);
if (idx <= (CHIEF_SOCIAL_ACTIONS.length - 1))
{
npc.broadcastSocialAction(CHIEF_SOCIAL_ACTIONS[idx]);
npc.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", idx); // Pass social action index to soldiers via script value
npc.broadcastEvent("do_social_action", TRAINING_RANGE, null);
}
}
startQuestTimer("do_social_action", 15000, npc, null);
}
else if (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
handleSocialAction(npc, SOLDIER_SOCIAL_ACTIONS[npc.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX")], false);
}
}
break;
}
case "reset_busy_state":
{
if (npc != null)
{
npc.getVariables().remove("BUSY_STATE");
npc.disableCoreAI(false);
}
break;
}
case "return_home":
{
for (int npcId : MAHUM_SOLDIERS)
{
for (L2Spawn npcSpawn : SpawnTable.getInstance().getSpawns(npcId))
{
final L2Npc soldier = npcSpawn.getLastSpawn();
if ((soldier != null) && !soldier.isDead() && (npcSpawn.getName() != null) && npcSpawn.getName().startsWith("smtg_drill_group") && !soldier.staysInSpawnLoc() && ((soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)))
{
soldier.setHeading(npcSpawn.getHeading());
soldier.teleToLocation(npcSpawn.getLocation(), false);
}
}
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
{
if ((getRandom(10) < 1) && (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId())))
{
npc.broadcastEvent("ATTACKED", 1000, null);
}
return super.onAttack(npc, attacker, damage, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
if ((receiver != null) && !receiver.isDead() && receiver.isInMySpawnGroup(sender))
{
switch (eventName)
{
case "do_social_action":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
final int actionIndex = sender.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX");
receiver.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", actionIndex);
handleSocialAction(receiver, SOLDIER_SOCIAL_ACTIONS[actionIndex], true);
}
break;
}
case "CHIEF_DIED":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
if (getRandom(4) < 1)
{
receiver.broadcastSay(ChatType.NPC_GENERAL, SOLDIER_FSTRINGS[getRandom(2)]);
}
if (receiver.canBeAttacked())
{
((L2Attackable) receiver).clearAggroList();
}
receiver.disableCoreAI(true);
receiver.getVariables().set("BUSY_STATE", 1);
receiver.setRunning();
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location((receiver.getX() + getRandom(-800, 800)), (receiver.getY() + getRandom(-800, 800)), receiver.getZ(), receiver.getHeading()));
startQuestTimer("reset_busy_state", 5000, receiver, null);
}
break;
}
case "ATTACKED":
{
if (CommonUtil.contains(MAHUM_CHIEFS, receiver.getId()))
{
receiver.broadcastSay(ChatType.NPC_GENERAL, CHIEF_FSTRINGS[getRandom(2)]);
}
break;
}
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
npc.broadcastEvent("CHIEF_DIED", TRAINING_RANGE, null);
return null;
}
@Override
public String onSpawn(L2Npc npc)
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
startQuestTimer("do_social_action", 15000, npc, null);
}
else if ((getRandom(18) < 1) && CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
npc.getVariables().set("SOCIAL_ACTION_ALT_BEHAVIOR", 1);
}
// Restore AI handling by core
npc.disableCoreAI(false);
return super.onSpawn(npc);
}
private void handleSocialAction(L2Npc npc, Actions action, boolean firstCall)
{
if ((npc.getVariables().getInt("BUSY_STATE") != 0) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) || !npc.staysInSpawnLoc())
{
return;
}
final int socialActionId = (npc.getVariables().getInt("SOCIAL_ACTION_ALT_BEHAVIOR") == 0) ? action.getSocialActionId() : action.getAltSocialActionId();
if (socialActionId < 0)
{
return;
}
if (firstCall)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", action.getRepeatCount());
}
npc.broadcastSocialAction(socialActionId);
final int remainedCount = npc.getVariables().getInt("SOCIAL_ACTION_REMAINED_COUNT");
if (remainedCount > 0)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", (remainedCount - 1));
startQuestTimer("do_social_action", action.getRepeatInterval(), npc, null);
}
}
public static void main(String[] args)
{
new SelMahumDrill();
}
}

View File

@ -0,0 +1,410 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.data.xml.impl.SkillData;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for squads and chefs.
* @author GKR
*/
public final class SelMahumSquad extends AbstractNpcAI
{
// NPCs
private static final int CHEF = 18908;
private static final int FIRE = 18927;
private static final int STOVE = 18933;
private static final int OHS_Weapon = 15280;
private static final int THS_Weapon = 15281;
// Sel Mahum Squad Leaders
private static final int[] SQUAD_LEADERS =
{
22786,
22787,
22788
};
private static final NpcStringId[] CHEF_FSTRINGS =
{
NpcStringId.I_BROUGHT_THE_FOOD,
NpcStringId.COME_AND_EAT
};
private static final int FIRE_EFFECT_BURN = 1;
private static final int FIRE_EFFECT_NONE = 2;
private static final int MAHUM_EFFECT_EAT = 1;
private static final int MAHUM_EFFECT_SLEEP = 2;
private static final int MAHUM_EFFECT_NONE = 3;
private SelMahumSquad()
{
addAttackId(CHEF);
addAttackId(SQUAD_LEADERS);
addEventReceivedId(CHEF, FIRE, STOVE);
addEventReceivedId(SQUAD_LEADERS);
addFactionCallId(SQUAD_LEADERS);
addKillId(CHEF);
addMoveFinishedId(SQUAD_LEADERS);
addNodeArrivedId(CHEF);
addSkillSeeId(STOVE);
addSpawnId(CHEF, FIRE);
addSpawnId(SQUAD_LEADERS);
addSpellFinishedId(CHEF);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "chef_disable_reward":
{
npc.getVariables().set("REWARD_TIME_GONE", 1);
break;
}
case "chef_heal_player":
{
healPlayer(npc, player);
break;
}
case "chef_remove_invul":
{
if (npc.isMonster())
{
npc.setIsInvul(false);
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
if ((player != null) && !player.isDead() && npc.isInSurroundingRegion(player))
{
addAttackPlayerDesire(npc, player);
}
}
break;
}
case "chef_set_invul":
{
if (!npc.isDead())
{
npc.setIsInvul(true);
}
break;
}
case "fire":
{
startQuestTimer("fire", 30000 + getRandom(5000), npc, null);
npc.setDisplayEffect(FIRE_EFFECT_NONE);
if (getRandom(GameTimeController.getInstance().isNight() ? 2 : 4) < 1)
{
npc.setDisplayEffect(FIRE_EFFECT_BURN); // fire burns
npc.broadcastEvent("SCE_CAMPFIRE_START", 600, null);
}
else
{
npc.setDisplayEffect(FIRE_EFFECT_NONE); // fire goes out
npc.broadcastEvent("SCE_CAMPFIRE_END", 600, null);
}
break;
}
case "fire_arrived":
{
// myself.i_quest0 = 1;
npc.setWalking();
npc.setTarget(npc);
if (!npc.isRandomWalkingEnabled())
{
final Skill skill = SkillData.getInstance().getSkill(6331, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_SLEEP);
}
if (npc.getVariables().getInt("BUSY_STATE") == 1) // Eating
{
final Skill skill = SkillData.getInstance().getSkill(6332, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_EAT);
}
startQuestTimer("remove_effects", 300000, npc, null);
break;
}
case "notify_dinner":
{
npc.broadcastEvent("SCE_DINNER_EAT", 600, null);
break;
}
case "remove_effects":
{
// myself.i_quest0 = 0;
npc.setRunning();
npc.setDisplayEffect(MAHUM_EFFECT_NONE);
break;
}
case "reset_full_bottle_prize":
{
npc.getVariables().remove("FULL_BARREL_REWARDING_PLAYER");
break;
}
case "return_from_fire":
{
if (npc.isMonster() && !npc.isDead())
{
((L2MonsterInstance) npc).returnHome();
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
{
if ((npc.getId() == CHEF) && (npc.getVariables().getInt("BUSY_STATE") == 0))
{
if (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") == 0)
{
startQuestTimer("chef_remove_invul", 180000, npc, attacker);
startQuestTimer("chef_disable_reward", 60000, npc, null);
npc.getVariables().set("INVUL_REMOVE_TIMER_STARTED", 1);
}
startQuestTimer("chef_heal_player", 1000, npc, attacker);
startQuestTimer("chef_set_invul", 60000, npc, null);
npc.getVariables().set("BUSY_STATE", 1);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
handlePreAttackMotion(npc);
}
return super.onAttack(npc, attacker, damage, isSummon, skill);
}
@Override
public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isSummon)
{
handlePreAttackMotion(npc);
return super.onFactionCall(npc, caller, attacker, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
switch (eventName)
{
case "SCE_DINNER_CHECK":
{
if (receiver.getId() == FIRE)
{
receiver.setDisplayEffect(FIRE_EFFECT_BURN);
final L2Npc stove = addSpawn(STOVE, receiver.getX(), receiver.getY(), receiver.getZ() + 100, 0, false, 0);
stove.setSummoner(receiver);
startQuestTimer("notify_dinner", 2000, receiver, null); // @SCE_DINNER_EAT
sender.broadcastSay(ChatType.NPC_GENERAL, CHEF_FSTRINGS[getRandom(2)], 1250);
}
break;
}
case "SCE_CAMPFIRE_START":
{
if (receiver.isRandomWalkingEnabled() && !receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.setRunning();
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_CAMPFIRE_END":
{
if ((receiver.getId() == STOVE) && (receiver.getSummoner() == sender))
{
receiver.deleteMe();
}
else if ((receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(true);
receiver.getVariables().remove("BUSY_STATE");
receiver.setRHandId(THS_Weapon);
startQuestTimer("return_from_fire", 3000, receiver, null);
}
break;
}
case "SCE_DINNER_EAT":
{
if (!receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && (receiver.getVariables().getInt("BUSY_STATE", 0) == 0) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
if (!receiver.isRandomWalkingEnabled()) // i_ai0 == 1
{
receiver.setRHandId(THS_Weapon);
}
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.getVariables().set("BUSY_STATE", 1); // Eating - i_ai3 = 1
receiver.setRunning();
receiver.broadcastSay(ChatType.NPC_GENERAL, (getRandom(3) < 1) ? NpcStringId.LOOKS_DELICIOUS : NpcStringId.LET_S_GO_EAT);
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_SOUP_FAILURE":
{
if (CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.getVariables().set("FULL_BARREL_REWARDING_PLAYER", reference.getObjectId()); // TODO: Use it in 289 quest
startQuestTimer("reset_full_bottle_prize", 180000, receiver, null);
}
break;
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
if (npc.isMonster() && (npc.getVariables().getInt("REWARD_TIME_GONE") == 0))
{
npc.dropItem(killer, 15492, 1);
}
cancelQuestTimer("chef_remove_invul", npc, null);
cancelQuestTimer("chef_disable_reward", npc, null);
cancelQuestTimer("chef_heal_player", npc, null);
cancelQuestTimer("chef_set_invul", npc, null);
return super.onKill(npc, killer, isSummon);
}
@Override
public void onMoveFinished(L2Npc npc)
{
// Npc moves to fire
if (!npc.isRandomWalkingEnabled() && (npc.getX() == npc.getVariables().getInt("DESTINATION_X")) && (npc.getY() == npc.getVariables().getInt("DESTINATION_Y")))
{
npc.setRHandId(OHS_Weapon);
startQuestTimer("fire_arrived", 3000, npc, null);
}
}
@Override
public void onNodeArrived(L2Npc npc)
{
npc.broadcastEvent("SCE_DINNER_CHECK", 300, null);
}
@Override
public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
{
if ((npc.getId() == STOVE) && (skill.getId() == 9075) && CommonUtil.contains(targets, npc))
{
npc.doCast(SkillData.getInstance().getSkill(6688, 1));
npc.broadcastEvent("SCE_SOUP_FAILURE", 600, caster);
}
return super.onSkillSee(npc, caster, skill, targets, isSummon);
}
@Override
public String onSpawn(L2Npc npc)
{
if (npc.getId() == CHEF)
{
npc.setIsInvul(false);
}
else if (npc.getId() == FIRE)
{
startQuestTimer("fire", 1000, npc, null);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
npc.setDisplayEffect(3);
npc.setRandomWalking(true);
}
return super.onSpawn(npc);
}
@Override
public String onSpellFinished(L2Npc npc, L2PcInstance player, Skill skill)
{
if ((skill != null) && (skill.getId() == 6330))
{
healPlayer(npc, player);
}
return super.onSpellFinished(npc, player, skill);
}
private void healPlayer(L2Npc npc, L2PcInstance player)
{
if ((player != null) && !player.isDead() && (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") != 1) && ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_CAST)))
{
npc.setTarget(player);
npc.doCast(SkillData.getInstance().getSkill(6330, 1));
}
else
{
cancelQuestTimer("chef_set_invul", npc, null);
npc.getVariables().remove("BUSY_STATE");
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
npc.setWalking();
}
}
private void handlePreAttackMotion(L2Npc attacked)
{
cancelQuestTimer("remove_effects", attacked, null);
attacked.getVariables().remove("BUSY_STATE");
attacked.setRandomWalking(true);
attacked.setDisplayEffect(MAHUM_EFFECT_NONE);
if (attacked.getRightHandItem() == OHS_Weapon)
{
attacked.setRHandId(THS_Weapon);
}
// TODO: Check about i_quest0
}
public static void main(String[] args)
{
new SelMahumSquad();
}
}

View File

@ -40,6 +40,8 @@ import com.l2jmobius.gameserver.model.WalkInfo;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.tasks.npc.walker.ArrivedTask;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.holders.NpcRoutesHolder;
import com.l2jmobius.gameserver.network.NpcStringId;
@ -421,6 +423,9 @@ public final class WalkingManager implements IGameXmlReader
{
if (_activeRoutes.containsKey(npc.getObjectId()))
{
// Notify quest
EventDispatcher.getInstance().notifyEventAsync(new OnNpcMoveNodeArrived(npc), npc);
final WalkInfo walk = _activeRoutes.get(npc.getObjectId());
// Opposite should not happen... but happens sometime

View File

@ -90,6 +90,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcDespawn;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcEventReceived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -807,6 +808,30 @@ public abstract class AbstractScript extends ManagedScript implements IEventTime
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, int... npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, Collection<Integer> npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} finishes to move on its route.
* @param callback

View File

@ -48,6 +48,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcManorBypass;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMenuSelect;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -187,6 +188,7 @@ public enum EventType
ON_NPC_FIRST_TALK(OnNpcFirstTalk.class, void.class),
ON_NPC_HATE(OnAttackableHate.class, void.class, TerminateReturn.class),
ON_NPC_MOVE_FINISHED(OnNpcMoveFinished.class, void.class),
ON_NPC_MOVE_NODE_ARRIVED(OnNpcMoveNodeArrived.class, void.class),
ON_NPC_MOVE_ROUTE_FINISHED(OnNpcMoveRouteFinished.class, void.class),
ON_NPC_QUEST_START(null, void.class),
ON_NPC_SKILL_FINISHED(OnNpcSkillFinished.class, void.class),

View File

@ -0,0 +1,45 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.events.impl.character.npc;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.events.EventType;
import com.l2jmobius.gameserver.model.events.impl.IBaseEvent;
/**
* @author UnAfraid
*/
public class OnNpcMoveNodeArrived implements IBaseEvent
{
private final L2Npc _npc;
public OnNpcMoveNodeArrived(L2Npc npc)
{
_npc = npc;
}
public L2Npc getNpc()
{
return _npc;
}
@Override
public EventType getType()
{
return EventType.ON_NPC_MOVE_NODE_ARRIVED;
}
}

View File

@ -954,6 +954,21 @@ public class Quest extends AbstractScript implements IIdentifiable
}
}
/**
* @param npc
*/
public final void notifyNodeArrived(L2Npc npc)
{
try
{
onNodeArrived(npc);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Exception on onNodeArrived() in notifyNodeArrived(): " + e.getMessage(), e);
}
}
/**
* @param npc
*/
@ -1393,6 +1408,14 @@ public class Quest extends AbstractScript implements IIdentifiable
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive a walking node
* @param npc registered NPC
*/
public void onNodeArrived(L2Npc npc)
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive to last node
* @param npc registered NPC
@ -2203,6 +2226,24 @@ public class Quest extends AbstractScript implements IIdentifiable
setNpcMoveFinishedId(event -> notifyMoveFinished(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(int... npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(Collection<Integer> npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onRouteFinished trigger for NPC
* @param npcIds the IDs of the NPCs to register

View File

@ -0,0 +1,312 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.datatables.SpawnTable;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for drill groups.
* @author GKR
*/
public final class SelMahumDrill extends AbstractNpcAI
{
private static final int[] MAHUM_CHIEFS =
{
22775, // Sel Mahum Drill Sergeant
22776, // Sel Mahum Training Officer
22778, // Sel Mahum Drill Sergeant
};
private static final int[] MAHUM_SOLDIERS =
{
22780, // Sel Mahum Recruit
22782, // Sel Mahum Recruit
22783, // Sel Mahum Soldier
22784, // Sel Mahum Recruit
22785, // Sel Mahum Soldier
};
private static final int[] CHIEF_SOCIAL_ACTIONS =
{
1,
4,
5,
7
};
private static final Actions[] SOLDIER_SOCIAL_ACTIONS =
{
Actions.SCE_TRAINING_ACTION_A,
Actions.SCE_TRAINING_ACTION_B,
Actions.SCE_TRAINING_ACTION_C,
Actions.SCE_TRAINING_ACTION_D
};
private static final NpcStringId[] CHIEF_FSTRINGS =
{
NpcStringId.HOW_DARE_YOU_ATTACK_MY_RECRUITS,
NpcStringId.WHO_IS_DISRUPTING_THE_ORDER
};
private static final NpcStringId[] SOLDIER_FSTRINGS =
{
NpcStringId.THE_DRILLMASTER_IS_DEAD,
NpcStringId.LINE_UP_THE_RANKS
};
// Chiefs event broadcast range
private static final int TRAINING_RANGE = 1000;
private static enum Actions
{
SCE_TRAINING_ACTION_A(4, -1, 2, 2333),
SCE_TRAINING_ACTION_B(1, -1, 2, 4333),
SCE_TRAINING_ACTION_C(6, 5, 4, 1000),
SCE_TRAINING_ACTION_D(7, -1, 2, 1000);
private final int _socialActionId;
private final int _altSocialActionId;
private final int _repeatCount;
private final int _repeatInterval;
private Actions(int socialActionId, int altSocialActionId, int repeatCount, int repeatInterval)
{
_socialActionId = socialActionId;
_altSocialActionId = altSocialActionId;
_repeatCount = repeatCount;
_repeatInterval = repeatInterval;
}
protected int getSocialActionId()
{
return _socialActionId;
}
protected int getAltSocialActionId()
{
return _altSocialActionId;
}
protected int getRepeatCount()
{
return _repeatCount;
}
protected int getRepeatInterval()
{
return _repeatInterval;
}
}
private SelMahumDrill()
{
addAttackId(MAHUM_CHIEFS);
addAttackId(MAHUM_SOLDIERS);
addKillId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_SOLDIERS);
addSpawnId(MAHUM_CHIEFS);
addSpawnId(MAHUM_SOLDIERS);
// Start global return home timer
startQuestTimer("return_home", 120000, null, null, true);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "do_social_action":
{
if ((npc != null) && !npc.isDead())
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
if ((npc.getVariables().getInt("BUSY_STATE") == 0) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) && npc.staysInSpawnLoc())
{
final int idx = getRandom(6);
if (idx <= (CHIEF_SOCIAL_ACTIONS.length - 1))
{
npc.broadcastSocialAction(CHIEF_SOCIAL_ACTIONS[idx]);
npc.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", idx); // Pass social action index to soldiers via script value
npc.broadcastEvent("do_social_action", TRAINING_RANGE, null);
}
}
startQuestTimer("do_social_action", 15000, npc, null);
}
else if (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
handleSocialAction(npc, SOLDIER_SOCIAL_ACTIONS[npc.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX")], false);
}
}
break;
}
case "reset_busy_state":
{
if (npc != null)
{
npc.getVariables().remove("BUSY_STATE");
npc.disableCoreAI(false);
}
break;
}
case "return_home":
{
for (int npcId : MAHUM_SOLDIERS)
{
for (L2Spawn npcSpawn : SpawnTable.getInstance().getSpawns(npcId))
{
final L2Npc soldier = npcSpawn.getLastSpawn();
if ((soldier != null) && !soldier.isDead() && (npcSpawn.getName() != null) && npcSpawn.getName().startsWith("smtg_drill_group") && !soldier.staysInSpawnLoc() && ((soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)))
{
soldier.setHeading(npcSpawn.getHeading());
soldier.teleToLocation(npcSpawn.getLocation(), false);
}
}
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
{
if ((getRandom(10) < 1) && (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId())))
{
npc.broadcastEvent("ATTACKED", 1000, null);
}
return super.onAttack(npc, attacker, damage, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
if ((receiver != null) && !receiver.isDead() && receiver.isInMySpawnGroup(sender))
{
switch (eventName)
{
case "do_social_action":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
final int actionIndex = sender.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX");
receiver.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", actionIndex);
handleSocialAction(receiver, SOLDIER_SOCIAL_ACTIONS[actionIndex], true);
}
break;
}
case "CHIEF_DIED":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
if (getRandom(4) < 1)
{
receiver.broadcastSay(ChatType.NPC_GENERAL, SOLDIER_FSTRINGS[getRandom(2)]);
}
if (receiver.canBeAttacked())
{
((L2Attackable) receiver).clearAggroList();
}
receiver.disableCoreAI(true);
receiver.getVariables().set("BUSY_STATE", 1);
receiver.setRunning();
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location((receiver.getX() + getRandom(-800, 800)), (receiver.getY() + getRandom(-800, 800)), receiver.getZ(), receiver.getHeading()));
startQuestTimer("reset_busy_state", 5000, receiver, null);
}
break;
}
case "ATTACKED":
{
if (CommonUtil.contains(MAHUM_CHIEFS, receiver.getId()))
{
receiver.broadcastSay(ChatType.NPC_GENERAL, CHIEF_FSTRINGS[getRandom(2)]);
}
break;
}
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
npc.broadcastEvent("CHIEF_DIED", TRAINING_RANGE, null);
return null;
}
@Override
public String onSpawn(L2Npc npc)
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
startQuestTimer("do_social_action", 15000, npc, null);
}
else if ((getRandom(18) < 1) && CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
npc.getVariables().set("SOCIAL_ACTION_ALT_BEHAVIOR", 1);
}
// Restore AI handling by core
npc.disableCoreAI(false);
return super.onSpawn(npc);
}
private void handleSocialAction(L2Npc npc, Actions action, boolean firstCall)
{
if ((npc.getVariables().getInt("BUSY_STATE") != 0) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) || !npc.staysInSpawnLoc())
{
return;
}
final int socialActionId = (npc.getVariables().getInt("SOCIAL_ACTION_ALT_BEHAVIOR") == 0) ? action.getSocialActionId() : action.getAltSocialActionId();
if (socialActionId < 0)
{
return;
}
if (firstCall)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", action.getRepeatCount());
}
npc.broadcastSocialAction(socialActionId);
final int remainedCount = npc.getVariables().getInt("SOCIAL_ACTION_REMAINED_COUNT");
if (remainedCount > 0)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", (remainedCount - 1));
startQuestTimer("do_social_action", action.getRepeatInterval(), npc, null);
}
}
public static void main(String[] args)
{
new SelMahumDrill();
}
}

View File

@ -0,0 +1,410 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.data.xml.impl.SkillData;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for squads and chefs.
* @author GKR
*/
public final class SelMahumSquad extends AbstractNpcAI
{
// NPCs
private static final int CHEF = 18908;
private static final int FIRE = 18927;
private static final int STOVE = 18933;
private static final int OHS_Weapon = 15280;
private static final int THS_Weapon = 15281;
// Sel Mahum Squad Leaders
private static final int[] SQUAD_LEADERS =
{
22786,
22787,
22788
};
private static final NpcStringId[] CHEF_FSTRINGS =
{
NpcStringId.I_BROUGHT_THE_FOOD,
NpcStringId.COME_AND_EAT
};
private static final int FIRE_EFFECT_BURN = 1;
private static final int FIRE_EFFECT_NONE = 2;
private static final int MAHUM_EFFECT_EAT = 1;
private static final int MAHUM_EFFECT_SLEEP = 2;
private static final int MAHUM_EFFECT_NONE = 3;
private SelMahumSquad()
{
addAttackId(CHEF);
addAttackId(SQUAD_LEADERS);
addEventReceivedId(CHEF, FIRE, STOVE);
addEventReceivedId(SQUAD_LEADERS);
addFactionCallId(SQUAD_LEADERS);
addKillId(CHEF);
addMoveFinishedId(SQUAD_LEADERS);
addNodeArrivedId(CHEF);
addSkillSeeId(STOVE);
addSpawnId(CHEF, FIRE);
addSpawnId(SQUAD_LEADERS);
addSpellFinishedId(CHEF);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "chef_disable_reward":
{
npc.getVariables().set("REWARD_TIME_GONE", 1);
break;
}
case "chef_heal_player":
{
healPlayer(npc, player);
break;
}
case "chef_remove_invul":
{
if (npc.isMonster())
{
npc.setIsInvul(false);
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
if ((player != null) && !player.isDead() && npc.isInSurroundingRegion(player))
{
addAttackPlayerDesire(npc, player);
}
}
break;
}
case "chef_set_invul":
{
if (!npc.isDead())
{
npc.setIsInvul(true);
}
break;
}
case "fire":
{
startQuestTimer("fire", 30000 + getRandom(5000), npc, null);
npc.setDisplayEffect(FIRE_EFFECT_NONE);
if (getRandom(GameTimeController.getInstance().isNight() ? 2 : 4) < 1)
{
npc.setDisplayEffect(FIRE_EFFECT_BURN); // fire burns
npc.broadcastEvent("SCE_CAMPFIRE_START", 600, null);
}
else
{
npc.setDisplayEffect(FIRE_EFFECT_NONE); // fire goes out
npc.broadcastEvent("SCE_CAMPFIRE_END", 600, null);
}
break;
}
case "fire_arrived":
{
// myself.i_quest0 = 1;
npc.setWalking();
npc.setTarget(npc);
if (!npc.isRandomWalkingEnabled())
{
final Skill skill = SkillData.getInstance().getSkill(6331, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_SLEEP);
}
if (npc.getVariables().getInt("BUSY_STATE") == 1) // Eating
{
final Skill skill = SkillData.getInstance().getSkill(6332, 1);
if (!npc.isAffectedBySkill(skill.getId()))
{
npc.doCast(skill);
}
npc.setDisplayEffect(MAHUM_EFFECT_EAT);
}
startQuestTimer("remove_effects", 300000, npc, null);
break;
}
case "notify_dinner":
{
npc.broadcastEvent("SCE_DINNER_EAT", 600, null);
break;
}
case "remove_effects":
{
// myself.i_quest0 = 0;
npc.setRunning();
npc.setDisplayEffect(MAHUM_EFFECT_NONE);
break;
}
case "reset_full_bottle_prize":
{
npc.getVariables().remove("FULL_BARREL_REWARDING_PLAYER");
break;
}
case "return_from_fire":
{
if (npc.isMonster() && !npc.isDead())
{
((L2MonsterInstance) npc).returnHome();
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
{
if ((npc.getId() == CHEF) && (npc.getVariables().getInt("BUSY_STATE") == 0))
{
if (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") == 0)
{
startQuestTimer("chef_remove_invul", 180000, npc, attacker);
startQuestTimer("chef_disable_reward", 60000, npc, null);
npc.getVariables().set("INVUL_REMOVE_TIMER_STARTED", 1);
}
startQuestTimer("chef_heal_player", 1000, npc, attacker);
startQuestTimer("chef_set_invul", 60000, npc, null);
npc.getVariables().set("BUSY_STATE", 1);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
handlePreAttackMotion(npc);
}
return super.onAttack(npc, attacker, damage, isSummon, skill);
}
@Override
public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isSummon)
{
handlePreAttackMotion(npc);
return super.onFactionCall(npc, caller, attacker, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
switch (eventName)
{
case "SCE_DINNER_CHECK":
{
if (receiver.getId() == FIRE)
{
receiver.setDisplayEffect(FIRE_EFFECT_BURN);
final L2Npc stove = addSpawn(STOVE, receiver.getX(), receiver.getY(), receiver.getZ() + 100, 0, false, 0);
stove.setSummoner(receiver);
startQuestTimer("notify_dinner", 2000, receiver, null); // @SCE_DINNER_EAT
sender.broadcastSay(ChatType.NPC_GENERAL, CHEF_FSTRINGS[getRandom(2)], 1250);
}
break;
}
case "SCE_CAMPFIRE_START":
{
if (receiver.isRandomWalkingEnabled() && !receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.setRunning();
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_CAMPFIRE_END":
{
if ((receiver.getId() == STOVE) && (receiver.getSummoner() == sender))
{
receiver.deleteMe();
}
else if ((receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setRandomWalking(true);
receiver.getVariables().remove("BUSY_STATE");
receiver.setRHandId(THS_Weapon);
startQuestTimer("return_from_fire", 3000, receiver, null);
}
break;
}
case "SCE_DINNER_EAT":
{
if (!receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && (receiver.getVariables().getInt("BUSY_STATE", 0) == 0) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
if (!receiver.isRandomWalkingEnabled()) // i_ai0 == 1
{
receiver.setRHandId(THS_Weapon);
}
receiver.setRandomWalking(false); // Moving to fire - i_ai0 = 1
receiver.getVariables().set("BUSY_STATE", 1); // Eating - i_ai3 = 1
receiver.setRunning();
receiver.broadcastSay(ChatType.NPC_GENERAL, (getRandom(3) < 1) ? NpcStringId.LOOKS_DELICIOUS : NpcStringId.LET_S_GO_EAT);
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_SOUP_FAILURE":
{
if (CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.getVariables().set("FULL_BARREL_REWARDING_PLAYER", reference.getObjectId()); // TODO: Use it in 289 quest
startQuestTimer("reset_full_bottle_prize", 180000, receiver, null);
}
break;
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
if (npc.isMonster() && (npc.getVariables().getInt("REWARD_TIME_GONE") == 0))
{
npc.dropItem(killer, 15492, 1);
}
cancelQuestTimer("chef_remove_invul", npc, null);
cancelQuestTimer("chef_disable_reward", npc, null);
cancelQuestTimer("chef_heal_player", npc, null);
cancelQuestTimer("chef_set_invul", npc, null);
return super.onKill(npc, killer, isSummon);
}
@Override
public void onMoveFinished(L2Npc npc)
{
// Npc moves to fire
if (!npc.isRandomWalkingEnabled() && (npc.getX() == npc.getVariables().getInt("DESTINATION_X")) && (npc.getY() == npc.getVariables().getInt("DESTINATION_Y")))
{
npc.setRHandId(OHS_Weapon);
startQuestTimer("fire_arrived", 3000, npc, null);
}
}
@Override
public void onNodeArrived(L2Npc npc)
{
npc.broadcastEvent("SCE_DINNER_CHECK", 300, null);
}
@Override
public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
{
if ((npc.getId() == STOVE) && (skill.getId() == 9075) && CommonUtil.contains(targets, npc))
{
npc.doCast(SkillData.getInstance().getSkill(6688, 1));
npc.broadcastEvent("SCE_SOUP_FAILURE", 600, caster);
}
return super.onSkillSee(npc, caster, skill, targets, isSummon);
}
@Override
public String onSpawn(L2Npc npc)
{
if (npc.getId() == CHEF)
{
npc.setIsInvul(false);
}
else if (npc.getId() == FIRE)
{
startQuestTimer("fire", 1000, npc, null);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
npc.setDisplayEffect(3);
npc.setRandomWalking(true);
}
return super.onSpawn(npc);
}
@Override
public String onSpellFinished(L2Npc npc, L2PcInstance player, Skill skill)
{
if ((skill != null) && (skill.getId() == 6330))
{
healPlayer(npc, player);
}
return super.onSpellFinished(npc, player, skill);
}
private void healPlayer(L2Npc npc, L2PcInstance player)
{
if ((player != null) && !player.isDead() && (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") != 1) && ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_CAST)))
{
npc.setTarget(player);
npc.doCast(SkillData.getInstance().getSkill(6330, 1));
}
else
{
cancelQuestTimer("chef_set_invul", npc, null);
npc.getVariables().remove("BUSY_STATE");
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
npc.setWalking();
}
}
private void handlePreAttackMotion(L2Npc attacked)
{
cancelQuestTimer("remove_effects", attacked, null);
attacked.getVariables().remove("BUSY_STATE");
attacked.setRandomWalking(true);
attacked.setDisplayEffect(MAHUM_EFFECT_NONE);
if (attacked.getRightHandItem() == OHS_Weapon)
{
attacked.setRHandId(THS_Weapon);
}
// TODO: Check about i_quest0
}
public static void main(String[] args)
{
new SelMahumSquad();
}
}

View File

@ -40,6 +40,8 @@ import com.l2jmobius.gameserver.model.WalkInfo;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.tasks.npc.walker.ArrivedTask;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.holders.NpcRoutesHolder;
import com.l2jmobius.gameserver.network.NpcStringId;
@ -421,6 +423,9 @@ public final class WalkingManager implements IGameXmlReader
{
if (_activeRoutes.containsKey(npc.getObjectId()))
{
// Notify quest
EventDispatcher.getInstance().notifyEventAsync(new OnNpcMoveNodeArrived(npc), npc);
final WalkInfo walk = _activeRoutes.get(npc.getObjectId());
// Opposite should not happen... but happens sometime

View File

@ -90,6 +90,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcDespawn;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcEventReceived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -807,6 +808,30 @@ public abstract class AbstractScript extends ManagedScript implements IEventTime
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, int... npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, Collection<Integer> npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} finishes to move on its route.
* @param callback

View File

@ -48,6 +48,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcManorBypass;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMenuSelect;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -187,6 +188,7 @@ public enum EventType
ON_NPC_FIRST_TALK(OnNpcFirstTalk.class, void.class),
ON_NPC_HATE(OnAttackableHate.class, void.class, TerminateReturn.class),
ON_NPC_MOVE_FINISHED(OnNpcMoveFinished.class, void.class),
ON_NPC_MOVE_NODE_ARRIVED(OnNpcMoveNodeArrived.class, void.class),
ON_NPC_MOVE_ROUTE_FINISHED(OnNpcMoveRouteFinished.class, void.class),
ON_NPC_QUEST_START(null, void.class),
ON_NPC_SKILL_FINISHED(OnNpcSkillFinished.class, void.class),

View File

@ -0,0 +1,45 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.events.impl.character.npc;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.events.EventType;
import com.l2jmobius.gameserver.model.events.impl.IBaseEvent;
/**
* @author UnAfraid
*/
public class OnNpcMoveNodeArrived implements IBaseEvent
{
private final L2Npc _npc;
public OnNpcMoveNodeArrived(L2Npc npc)
{
_npc = npc;
}
public L2Npc getNpc()
{
return _npc;
}
@Override
public EventType getType()
{
return EventType.ON_NPC_MOVE_NODE_ARRIVED;
}
}

View File

@ -954,6 +954,21 @@ public class Quest extends AbstractScript implements IIdentifiable
}
}
/**
* @param npc
*/
public final void notifyNodeArrived(L2Npc npc)
{
try
{
onNodeArrived(npc);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Exception on onNodeArrived() in notifyNodeArrived(): " + e.getMessage(), e);
}
}
/**
* @param npc
*/
@ -1393,6 +1408,14 @@ public class Quest extends AbstractScript implements IIdentifiable
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive a walking node
* @param npc registered NPC
*/
public void onNodeArrived(L2Npc npc)
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive to last node
* @param npc registered NPC
@ -2203,6 +2226,24 @@ public class Quest extends AbstractScript implements IIdentifiable
setNpcMoveFinishedId(event -> notifyMoveFinished(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(int... npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(Collection<Integer> npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onRouteFinished trigger for NPC
* @param npcIds the IDs of the NPCs to register

View File

@ -0,0 +1,312 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.datatables.SpawnTable;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for drill groups.
* @author GKR
*/
public final class SelMahumDrill extends AbstractNpcAI
{
private static final int[] MAHUM_CHIEFS =
{
22775, // Sel Mahum Drill Sergeant
22776, // Sel Mahum Training Officer
22778, // Sel Mahum Drill Sergeant
};
private static final int[] MAHUM_SOLDIERS =
{
22780, // Sel Mahum Recruit
22782, // Sel Mahum Recruit
22783, // Sel Mahum Soldier
22784, // Sel Mahum Recruit
22785, // Sel Mahum Soldier
};
private static final int[] CHIEF_SOCIAL_ACTIONS =
{
1,
4,
5,
7
};
private static final Actions[] SOLDIER_SOCIAL_ACTIONS =
{
Actions.SCE_TRAINING_ACTION_A,
Actions.SCE_TRAINING_ACTION_B,
Actions.SCE_TRAINING_ACTION_C,
Actions.SCE_TRAINING_ACTION_D
};
private static final NpcStringId[] CHIEF_FSTRINGS =
{
NpcStringId.HOW_DARE_YOU_ATTACK_MY_RECRUITS,
NpcStringId.WHO_IS_DISRUPTING_THE_ORDER
};
private static final NpcStringId[] SOLDIER_FSTRINGS =
{
NpcStringId.THE_DRILLMASTER_IS_DEAD,
NpcStringId.LINE_UP_THE_RANKS
};
// Chiefs event broadcast range
private static final int TRAINING_RANGE = 1000;
private static enum Actions
{
SCE_TRAINING_ACTION_A(4, -1, 2, 2333),
SCE_TRAINING_ACTION_B(1, -1, 2, 4333),
SCE_TRAINING_ACTION_C(6, 5, 4, 1000),
SCE_TRAINING_ACTION_D(7, -1, 2, 1000);
private final int _socialActionId;
private final int _altSocialActionId;
private final int _repeatCount;
private final int _repeatInterval;
private Actions(int socialActionId, int altSocialActionId, int repeatCount, int repeatInterval)
{
_socialActionId = socialActionId;
_altSocialActionId = altSocialActionId;
_repeatCount = repeatCount;
_repeatInterval = repeatInterval;
}
protected int getSocialActionId()
{
return _socialActionId;
}
protected int getAltSocialActionId()
{
return _altSocialActionId;
}
protected int getRepeatCount()
{
return _repeatCount;
}
protected int getRepeatInterval()
{
return _repeatInterval;
}
}
private SelMahumDrill()
{
addAttackId(MAHUM_CHIEFS);
addAttackId(MAHUM_SOLDIERS);
addKillId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_CHIEFS);
addEventReceivedId(MAHUM_SOLDIERS);
addSpawnId(MAHUM_CHIEFS);
addSpawnId(MAHUM_SOLDIERS);
// Start global return home timer
startQuestTimer("return_home", 120000, null, null, true);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "do_social_action":
{
if ((npc != null) && !npc.isDead())
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
if ((npc.getVariables().getInt("BUSY_STATE") == 0) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) && npc.staysInSpawnLoc())
{
final int idx = getRandom(6);
if (idx <= (CHIEF_SOCIAL_ACTIONS.length - 1))
{
npc.broadcastSocialAction(CHIEF_SOCIAL_ACTIONS[idx]);
npc.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", idx); // Pass social action index to soldiers via script value
npc.broadcastEvent("do_social_action", TRAINING_RANGE, null);
}
}
startQuestTimer("do_social_action", 15000, npc, null);
}
else if (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
handleSocialAction(npc, SOLDIER_SOCIAL_ACTIONS[npc.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX")], false);
}
}
break;
}
case "reset_busy_state":
{
if (npc != null)
{
npc.getVariables().remove("BUSY_STATE");
npc.disableCoreAI(false);
}
break;
}
case "return_home":
{
for (int npcId : MAHUM_SOLDIERS)
{
for (L2Spawn npcSpawn : SpawnTable.getInstance().getSpawns(npcId))
{
final L2Npc soldier = npcSpawn.getLastSpawn();
if ((soldier != null) && !soldier.isDead() && (npcSpawn.getName() != null) && npcSpawn.getName().startsWith("smtg_drill_group") && !soldier.staysInSpawnLoc() && ((soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (soldier.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)))
{
soldier.setHeading(npcSpawn.getHeading());
soldier.teleToLocation(npcSpawn.getLocation(), false);
}
}
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
{
if ((getRandom(10) < 1) && (CommonUtil.contains(MAHUM_SOLDIERS, npc.getId())))
{
npc.broadcastEvent("ATTACKED", 1000, null);
}
return super.onAttack(npc, attacker, damage, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
if ((receiver != null) && !receiver.isDead() && receiver.isInMySpawnGroup(sender))
{
switch (eventName)
{
case "do_social_action":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
final int actionIndex = sender.getVariables().getInt("SOCIAL_ACTION_NEXT_INDEX");
receiver.getVariables().set("SOCIAL_ACTION_NEXT_INDEX", actionIndex);
handleSocialAction(receiver, SOLDIER_SOCIAL_ACTIONS[actionIndex], true);
}
break;
}
case "CHIEF_DIED":
{
if (CommonUtil.contains(MAHUM_SOLDIERS, receiver.getId()))
{
if (getRandom(4) < 1)
{
receiver.broadcastSay(ChatType.NPC_GENERAL, SOLDIER_FSTRINGS[getRandom(2)]);
}
if (receiver.canBeAttacked())
{
((L2Attackable) receiver).clearAggroList();
}
receiver.disableCoreAI(true);
receiver.getVariables().set("BUSY_STATE", 1);
receiver.setRunning();
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location((receiver.getX() + getRandom(-800, 800)), (receiver.getY() + getRandom(-800, 800)), receiver.getZ(), receiver.getHeading()));
startQuestTimer("reset_busy_state", 5000, receiver, null);
}
break;
}
case "ATTACKED":
{
if (CommonUtil.contains(MAHUM_CHIEFS, receiver.getId()))
{
receiver.broadcastSay(ChatType.NPC_GENERAL, CHIEF_FSTRINGS[getRandom(2)]);
}
break;
}
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
npc.broadcastEvent("CHIEF_DIED", TRAINING_RANGE, null);
return null;
}
@Override
public String onSpawn(L2Npc npc)
{
if (CommonUtil.contains(MAHUM_CHIEFS, npc.getId()))
{
startQuestTimer("do_social_action", 15000, npc, null);
}
else if ((getRandom(18) < 1) && CommonUtil.contains(MAHUM_SOLDIERS, npc.getId()))
{
npc.getVariables().set("SOCIAL_ACTION_ALT_BEHAVIOR", 1);
}
// Restore AI handling by core
npc.disableCoreAI(false);
return super.onSpawn(npc);
}
private void handleSocialAction(L2Npc npc, Actions action, boolean firstCall)
{
if ((npc.getVariables().getInt("BUSY_STATE") != 0) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) || !npc.staysInSpawnLoc())
{
return;
}
final int socialActionId = (npc.getVariables().getInt("SOCIAL_ACTION_ALT_BEHAVIOR") == 0) ? action.getSocialActionId() : action.getAltSocialActionId();
if (socialActionId < 0)
{
return;
}
if (firstCall)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", action.getRepeatCount());
}
npc.broadcastSocialAction(socialActionId);
final int remainedCount = npc.getVariables().getInt("SOCIAL_ACTION_REMAINED_COUNT");
if (remainedCount > 0)
{
npc.getVariables().set("SOCIAL_ACTION_REMAINED_COUNT", (remainedCount - 1));
startQuestTimer("do_social_action", action.getRepeatInterval(), npc, null);
}
}
public static void main(String[] args)
{
new SelMahumDrill();
}
}

View File

@ -0,0 +1,401 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ai.areas.SelMahumTrainingGrounds;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.data.xml.impl.SkillData;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import ai.AbstractNpcAI;
/**
* Sel Mahum Training Ground AI for squads and chefs.
* @author GKR
*/
public final class SelMahumSquad extends AbstractNpcAI
{
// NPCs
private static final int CHEF = 18908;
private static final int FIRE = 18927;
private static final int STOVE = 18933;
private static final int OHS_Weapon = 15280;
private static final int THS_Weapon = 15281;
// Sel Mahum Squad Leaders
private static final int[] SQUAD_LEADERS =
{
22786,
22787,
22788
};
private static final NpcStringId[] CHEF_FSTRINGS =
{
NpcStringId.I_BROUGHT_THE_FOOD,
NpcStringId.COME_AND_EAT
};
private static final int FIRE_EFFECT_BURN = 1;
private static final int FIRE_EFFECT_NONE = 2;
private static final int MAHUM_EFFECT_EAT = 1;
private static final int MAHUM_EFFECT_SLEEP = 2;
private static final int MAHUM_EFFECT_NONE = 3;
private SelMahumSquad()
{
addAttackId(CHEF);
addAttackId(SQUAD_LEADERS);
addEventReceivedId(CHEF, FIRE, STOVE);
addEventReceivedId(SQUAD_LEADERS);
addFactionCallId(SQUAD_LEADERS);
addKillId(CHEF);
addMoveFinishedId(SQUAD_LEADERS);
addNodeArrivedId(CHEF);
addSkillSeeId(STOVE);
addSpawnId(CHEF, FIRE);
addSpawnId(SQUAD_LEADERS);
addSpellFinishedId(CHEF);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "chef_disable_reward":
{
npc.getVariables().set("REWARD_TIME_GONE", 1);
break;
}
case "chef_heal_player":
{
healPlayer(npc, player);
break;
}
case "chef_remove_invul":
{
if (npc.isMonster())
{
npc.setIsInvul(false);
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
if ((player != null) && !player.isDead() && npc.isInSurroundingRegion(player))
{
addAttackDesire(npc, player);
}
}
break;
}
case "chef_set_invul":
{
if (!npc.isDead())
{
npc.setIsInvul(true);
}
break;
}
case "fire":
{
startQuestTimer("fire", 30000 + getRandom(5000), npc, null);
npc.setDisplayEffect(FIRE_EFFECT_NONE);
if (getRandom(GameTimeController.getInstance().isNight() ? 2 : 4) < 1)
{
npc.setDisplayEffect(FIRE_EFFECT_BURN); // fire burns
npc.broadcastEvent("SCE_CAMPFIRE_START", 600, null);
}
else
{
npc.setDisplayEffect(FIRE_EFFECT_NONE); // fire goes out
npc.broadcastEvent("SCE_CAMPFIRE_END", 600, null);
}
break;
}
case "fire_arrived":
{
// myself.i_quest0 = 1;
npc.setWalking();
npc.setTarget(npc);
if (npc.isNoRndWalk())
{
npc.doCast(SkillData.getInstance().getSkill(6331, 1));
npc.setDisplayEffect(MAHUM_EFFECT_SLEEP);
}
if (npc.getVariables().getInt("BUSY_STATE") == 1) // Eating
{
npc.doCast(SkillData.getInstance().getSkill(6332, 1));
npc.setDisplayEffect(MAHUM_EFFECT_EAT);
}
startQuestTimer("remove_effects", 300000, npc, null);
break;
}
case "notify_dinner":
{
npc.broadcastEvent("SCE_DINNER_EAT", 600, null);
break;
}
case "remove_effects":
{
// myself.i_quest0 = 0;
npc.setRunning();
npc.setDisplayEffect(MAHUM_EFFECT_NONE);
break;
}
case "reset_full_bottle_prize":
{
npc.getVariables().remove("FULL_BARREL_REWARDING_PLAYER");
break;
}
case "return_from_fire":
{
if (npc.isMonster() && !npc.isDead())
{
((L2MonsterInstance) npc).returnHome();
}
break;
}
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
{
if ((npc.getId() == CHEF) && (npc.getVariables().getInt("BUSY_STATE") == 0))
{
if (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") == 0)
{
startQuestTimer("chef_remove_invul", 180000, npc, attacker);
startQuestTimer("chef_disable_reward", 60000, npc, null);
npc.getVariables().set("INVUL_REMOVE_TIMER_STARTED", 1);
}
startQuestTimer("chef_heal_player", 1000, npc, attacker);
startQuestTimer("chef_set_invul", 60000, npc, null);
npc.getVariables().set("BUSY_STATE", 1);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
handlePreAttackMotion(npc);
}
return super.onAttack(npc, attacker, damage, isSummon, skill);
}
@Override
public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isSummon)
{
handlePreAttackMotion(npc);
return super.onFactionCall(npc, caller, attacker, isSummon);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
switch (eventName)
{
case "SCE_DINNER_CHECK":
{
if (receiver.getId() == FIRE)
{
receiver.setDisplayEffect(FIRE_EFFECT_BURN);
final L2Npc stove = addSpawn(STOVE, receiver.getX(), receiver.getY(), receiver.getZ() + 100, 0, false, 0);
stove.setSummoner(receiver);
startQuestTimer("notify_dinner", 2000, receiver, null); // @SCE_DINNER_EAT
sender.broadcastSay(ChatType.NPC_GENERAL, CHEF_FSTRINGS[getRandom(2)], 1250);
}
break;
}
case "SCE_CAMPFIRE_START":
{
if (!receiver.isNoRndWalk() && !receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setIsNoRndWalk(true); // Moving to fire - i_ai0 = 1
receiver.setRunning();
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_CAMPFIRE_END":
{
if ((receiver.getId() == STOVE) && (receiver.getSummoner() == sender))
{
receiver.deleteMe();
}
else if ((receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.setIsNoRndWalk(false);
receiver.getVariables().remove("BUSY_STATE");
receiver.setRHandId(THS_Weapon);
startQuestTimer("return_from_fire", 3000, receiver, null);
}
break;
}
case "SCE_DINNER_EAT":
{
if (!receiver.isDead() && (receiver.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK) && (receiver.getVariables().getInt("BUSY_STATE", 0) == 0) && CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
if (receiver.isNoRndWalk()) // i_ai0 == 1
{
receiver.setRHandId(THS_Weapon);
}
receiver.setIsNoRndWalk(true); // Moving to fire - i_ai0 = 1
receiver.getVariables().set("BUSY_STATE", 1); // Eating - i_ai3 = 1
receiver.setRunning();
receiver.broadcastSay(ChatType.NPC_GENERAL, (getRandom(3) < 1) ? NpcStringId.LOOKS_DELICIOUS : NpcStringId.LET_S_GO_EAT);
final Location loc = sender.getPointInRange(100, 200);
loc.setHeading(receiver.getHeading());
receiver.stopMove(null);
receiver.getVariables().set("DESTINATION_X", loc.getX());
receiver.getVariables().set("DESTINATION_Y", loc.getY());
receiver.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc);
}
break;
}
case "SCE_SOUP_FAILURE":
{
if (CommonUtil.contains(SQUAD_LEADERS, receiver.getId()))
{
receiver.getVariables().set("FULL_BARREL_REWARDING_PLAYER", reference.getObjectId()); // TODO: Use it in 289 quest
startQuestTimer("reset_full_bottle_prize", 180000, receiver, null);
}
break;
}
}
return super.onEventReceived(eventName, sender, receiver, reference);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
if (npc.isMonster() && (npc.getVariables().getInt("REWARD_TIME_GONE") == 0))
{
((L2MonsterInstance) npc).dropItem(killer, 15492, 1);
}
cancelQuestTimer("chef_remove_invul", npc, null);
cancelQuestTimer("chef_disable_reward", npc, null);
cancelQuestTimer("chef_heal_player", npc, null);
cancelQuestTimer("chef_set_invul", npc, null);
return super.onKill(npc, killer, isSummon);
}
@Override
public void onMoveFinished(L2Npc npc)
{
// NPC moves to fire
if (npc.isNoRndWalk() && (npc.getX() == npc.getVariables().getInt("DESTINATION_X")) && (npc.getY() == npc.getVariables().getInt("DESTINATION_Y")))
{
npc.setRHandId(OHS_Weapon);
startQuestTimer("fire_arrived", 3000, npc, null);
}
}
@Override
public void onNodeArrived(L2Npc npc)
{
npc.broadcastEvent("SCE_DINNER_CHECK", 300, null);
}
@Override
public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
{
if ((npc.getId() == STOVE) && (skill.getId() == 9075) && CommonUtil.contains(targets, npc))
{
npc.doCast(SkillData.getInstance().getSkill(6688, 1));
npc.broadcastEvent("SCE_SOUP_FAILURE", 600, caster);
}
return super.onSkillSee(npc, caster, skill, targets, isSummon);
}
@Override
public String onSpawn(L2Npc npc)
{
if (npc.getId() == CHEF)
{
npc.setIsInvul(false);
}
else if (npc.getId() == FIRE)
{
startQuestTimer("fire", 1000, npc, null);
}
else if (CommonUtil.contains(SQUAD_LEADERS, npc.getId()))
{
npc.setDisplayEffect(3);
npc.setIsNoRndWalk(false);
}
return super.onSpawn(npc);
}
@Override
public String onSpellFinished(L2Npc npc, L2PcInstance player, Skill skill)
{
if ((skill != null) && (skill.getId() == 6330))
{
healPlayer(npc, player);
}
return super.onSpellFinished(npc, player, skill);
}
private void healPlayer(L2Npc npc, L2PcInstance player)
{
if ((player != null) && !player.isDead() && (npc.getVariables().getInt("INVUL_REMOVE_TIMER_STARTED") != 1) && ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_CAST)))
{
npc.setTarget(player);
npc.doCast(SkillData.getInstance().getSkill(6330, 1));
}
else
{
cancelQuestTimer("chef_set_invul", npc, null);
npc.getVariables().remove("BUSY_STATE");
npc.getVariables().remove("INVUL_REMOVE_TIMER_STARTED");
npc.setWalking();
}
}
private void handlePreAttackMotion(L2Npc attacked)
{
cancelQuestTimer("remove_effects", attacked, null);
attacked.getVariables().remove("BUSY_STATE");
attacked.setIsNoRndWalk(false);
attacked.setDisplayEffect(MAHUM_EFFECT_NONE);
if (attacked.getRightHandItem() == OHS_Weapon)
{
attacked.setRHandId(THS_Weapon);
}
// TODO: Check about i_quest0
}
public static void main(String[] args)
{
new SelMahumSquad();
}
}

View File

@ -38,6 +38,8 @@ import com.l2jmobius.gameserver.model.WalkInfo;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.tasks.npc.walker.ArrivedTask;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.holders.NpcRoutesHolder;
import com.l2jmobius.gameserver.network.NpcStringId;
@ -382,6 +384,9 @@ public final class WalkingManager implements IGameXmlReader
return;
}
// Notify quest
EventDispatcher.getInstance().notifyEventAsync(new OnNpcMoveNodeArrived(npc), npc);
final WalkInfo walk = _activeRoutes.get(npc.getObjectId());
// Opposite should not happen... but happens sometime
if ((walk.getCurrentNodeId() < 0) || (walk.getCurrentNodeId() >= walk.getRoute().getNodesCount()))

View File

@ -72,6 +72,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcCreatureSee
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcEventReceived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -665,6 +666,30 @@ public abstract class AbstractScript extends ManagedScript
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, int... npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
/**
* Provides instant callback operation when {@link L2Npc} arrive to node of its route
* @param callback
* @param npcIds
* @return
*/
protected final List<AbstractEventListener> setNpcMoveNodeArrivedId(Consumer<OnNpcMoveNodeArrived> callback, Collection<Integer> npcIds)
{
return registerConsumer(callback, EventType.ON_NPC_MOVE_NODE_ARRIVED, ListenerRegisterType.NPC, npcIds);
}
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Provides instant callback operation when {@link L2Npc} finishes to move on its route.
* @param callback

View File

@ -34,6 +34,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcEventReceiv
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcFirstTalk;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcManorBypass;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveNodeArrived;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcMoveRouteFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
import com.l2jmobius.gameserver.model.events.impl.character.npc.OnNpcSkillSee;
@ -152,6 +153,7 @@ public enum EventType
ON_NPC_FIRST_TALK(OnNpcFirstTalk.class, void.class),
ON_NPC_HATE(OnAttackableHate.class, void.class, TerminateReturn.class),
ON_NPC_MOVE_FINISHED(OnNpcMoveFinished.class, void.class),
ON_NPC_MOVE_NODE_ARRIVED(OnNpcMoveNodeArrived.class, void.class),
ON_NPC_MOVE_ROUTE_FINISHED(OnNpcMoveRouteFinished.class, void.class),
ON_NPC_QUEST_START(null, void.class),
ON_NPC_SKILL_FINISHED(OnNpcSkillFinished.class, void.class),

View File

@ -0,0 +1,45 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.events.impl.character.npc;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.events.EventType;
import com.l2jmobius.gameserver.model.events.impl.IBaseEvent;
/**
* @author UnAfraid
*/
public class OnNpcMoveNodeArrived implements IBaseEvent
{
private final L2Npc _npc;
public OnNpcMoveNodeArrived(L2Npc npc)
{
_npc = npc;
}
public L2Npc getNpc()
{
return _npc;
}
@Override
public EventType getType()
{
return EventType.ON_NPC_MOVE_NODE_ARRIVED;
}
}

View File

@ -917,6 +917,21 @@ public class Quest extends AbstractScript implements IIdentifiable
}
}
/**
* @param npc
*/
public final void notifyNodeArrived(L2Npc npc)
{
try
{
onNodeArrived(npc);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Exception on onNodeArrived() in notifyNodeArrived(): " + e.getMessage(), e);
}
}
/**
* @param npc
*/
@ -1325,6 +1340,14 @@ public class Quest extends AbstractScript implements IIdentifiable
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive a walking node
* @param npc registered NPC
*/
public void onNodeArrived(L2Npc npc)
{
}
/**
* This function is called whenever a walker NPC (controlled by WalkingManager) arrive to last node
* @param npc registered NPC
@ -2023,6 +2046,24 @@ public class Quest extends AbstractScript implements IIdentifiable
setNpcMoveFinishedId(event -> notifyMoveFinished(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(int... npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onNodeArrived trigger for NPC
* @param npcIds the IDs of the NPCs to register
*/
public void addNodeArrivedId(Collection<Integer> npcIds)
{
setNpcMoveNodeArrivedId(event -> notifyNodeArrived(event.getNpc()), npcIds);
}
/**
* Register onRouteFinished trigger for NPC
* @param npcIds the IDs of the NPCs to register