From f4d74be01e8725f94ea5e96c62c70b68bbfbba83 Mon Sep 17 00:00:00 2001
From: mobius <8391001+MobiusDevelopment@users.noreply.github.com>
Date: Fri, 2 Jan 2015 11:10:00 +0000
Subject: [PATCH] Dr. Chaos AI.
---
.../data/scripts/ai/individual/DrChaos.java | 347 ++++++++++++++----
.../dist/game/data/stats/npcs/25500-25600.xml | 2 +-
.../dist/tools/sql/server/grandboss_data.sql | 8 +-
3 files changed, 279 insertions(+), 78 deletions(-)
diff --git a/trunk/dist/game/data/scripts/ai/individual/DrChaos.java b/trunk/dist/game/data/scripts/ai/individual/DrChaos.java
index e7b6683a6c..75fee270c0 100644
--- a/trunk/dist/game/data/scripts/ai/individual/DrChaos.java
+++ b/trunk/dist/game/data/scripts/ai/individual/DrChaos.java
@@ -18,117 +18,312 @@
*/
package ai.individual;
+import ai.npc.AbstractNpcAI;
+
import com.l2jserver.gameserver.ai.CtrlIntention;
-import com.l2jserver.gameserver.datatables.SpawnTable;
-import com.l2jserver.gameserver.model.L2Spawn;
+import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.model.Location;
+import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.L2Npc;
+import com.l2jserver.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jserver.gameserver.model.quest.Quest;
+import com.l2jserver.gameserver.network.clientpackets.Say2;
import com.l2jserver.gameserver.network.serverpackets.PlaySound;
+import com.l2jserver.gameserver.network.serverpackets.SocialAction;
import com.l2jserver.gameserver.network.serverpackets.SpecialCamera;
+import com.l2jserver.util.Rnd;
/**
- * DrChaos' AI.
- * @author Kerberos
+ * Dr. Chaos is a boss @ Pavel's Ruins. Some things to know :
+ *
+ * - As a mad scientist, he thinks all are spies, and for so if you stand too much longer near him you're considered as an "assassin from Black Anvil Guild".
+ * - You can chat with him, but if you try too much he will become angry.
+ * - That adaptation sends a decent cinematic made with the different social actions too.
+ * - The status of the RB is saved under GBs table, in order to retrieve the state if server restarts.
+ * - The spawn of the different NPCs (Dr. Chaos / War golem) is handled by that script aswell.
+ *
+ * @author Kerberos, Tryskell.
*/
-public final class DrChaos extends Quest
+public class DrChaos extends AbstractNpcAI
{
- private static final int DR_CHAOS = 32033;
- private static final int STRANGE_MACHINE = 32032;
- private static final int CHAOS_GOLEM = 25703;
- private static boolean _IsGolemSpawned;
+ private static final int DOCTOR_CHAOS = 32033;
+ private static final int CHAOS_GOLEM = 25512;
- private static final Location PLAYER_TELEPORT = new Location(94832, -112624, -3304);
- private static final Location NPC_LOCATION = new Location(-113091, -243942, -15536);
+ private static final byte NORMAL = 0; // Dr. Chaos is in NPC form.
+ private static final byte CRAZY = 1; // Dr. Chaos entered on golem form.
+ private static final byte DEAD = 2; // Dr. Chaos has been killed and has not yet spawned.
- private DrChaos()
+ private long _lastAttackVsGolem = 0;
+ private int _pissedOffTimer;
+
+ public DrChaos()
{
- // TODO extends AbstractNpcAI
- super(-1, "Doctor Chaos", "ai/individual");
- addFirstTalkId(DR_CHAOS);
- _IsGolemSpawned = false;
+ super(DrChaos.class.getSimpleName(), "ai");
+
+ addFirstTalkId(DOCTOR_CHAOS); // Different HTMs following actual humor.
+ addSpawnId(DOCTOR_CHAOS); // Timer activation at 30sec + paranoia activity.
+
+ addKillId(CHAOS_GOLEM); // Message + despawn.
+ addAttackId(CHAOS_GOLEM); // Random messages when he attacks.
+
+ StatsSet info = GrandBossManager.getInstance().getStatsSet(CHAOS_GOLEM);
+ int status = GrandBossManager.getInstance().getBossStatus(CHAOS_GOLEM);
+
+ // Load the reset date and time for Dr. Chaos from DB.
+ if (status == DEAD)
+ {
+ long temp = (info.getLong("respawn_time") - System.currentTimeMillis());
+ if (temp > 0)
+ {
+ startQuestTimer("reset_drchaos", temp, null, null, false);
+ }
+ else
+ {
+ // The time has already expired while the server was offline. Delete the saved time and
+ // immediately spawn Dr. Chaos. Also the state need to be changed for NORMAL
+ addSpawn(DOCTOR_CHAOS, 96320, -110912, -3328, 8191, false, 0, false);
+ GrandBossManager.getInstance().setBossStatus(CHAOS_GOLEM, NORMAL);
+ }
+ }
+ // Spawn the war golem.
+ else if (status == CRAZY)
+ {
+ int loc_x = info.getInt("loc_x");
+ int loc_y = info.getInt("loc_y");
+ int loc_z = info.getInt("loc_z");
+ int heading = info.getInt("heading");
+ final int hp = info.getInt("currentHP");
+ final int mp = info.getInt("currentMP");
+
+ L2GrandBossInstance golem = (L2GrandBossInstance) addSpawn(CHAOS_GOLEM, loc_x, loc_y, loc_z, heading, false, 0, false);
+ GrandBossManager.getInstance().addBoss(golem);
+
+ final L2Npc _golem = golem;
+
+ _golem.setCurrentHpMp(hp, mp);
+ _golem.setRunning();
+
+ // start monitoring Dr. Chaos's inactivity
+ _lastAttackVsGolem = System.currentTimeMillis();
+ startQuestTimer("golem_despawn", 60000, _golem, null, true);
+ }
+ // Spawn the regular NPC.
+ else
+ {
+ addSpawn(DOCTOR_CHAOS, 96320, -110912, -3328, 8191, false, 0, false);
+ }
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
- switch (event)
+ if (event.equalsIgnoreCase("reset_drchaos"))
{
- case "1":
+ GrandBossManager.getInstance().setBossStatus(CHAOS_GOLEM, NORMAL);
+ addSpawn(DOCTOR_CHAOS, 96320, -110912, -3328, 8191, false, 0, false);
+ }
+ // despawn the live Dr. Chaos after 30 minutes of inactivity
+ else if (event.equalsIgnoreCase("golem_despawn") && (npc != null))
+ {
+ if (npc.getId() == CHAOS_GOLEM)
{
- L2Npc machine = null;
- for (L2Spawn spawn : SpawnTable.getInstance().getSpawns(STRANGE_MACHINE))
+ if ((_lastAttackVsGolem + 1800000) < System.currentTimeMillis())
{
- if (spawn != null)
- {
- machine = spawn.getLastSpawn();
- }
+ // Despawn the war golem.
+ npc.deleteMe();
+
+ addSpawn(DOCTOR_CHAOS, 96320, -110912, -3328, 8191, false, 0, false); // spawn Dr. Chaos
+ GrandBossManager.getInstance().setBossStatus(CHAOS_GOLEM, NORMAL); // mark Dr. Chaos is not crazy any more
+ cancelQuestTimer("golem_despawn", npc, null);
}
- if (machine != null)
- {
- npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, machine);
- machine.broadcastPacket(new SpecialCamera(machine, 1, -200, 15, 10000, 1000, 20000, 0, 0, 0, 0, 0));
- }
- else
- {
- startQuestTimer("2", 2000, npc, player);
- }
- startQuestTimer("3", 10000, npc, player);
- break;
- }
- case "2":
- {
- npc.broadcastSocialAction(3);
- break;
- }
- case "3":
- {
- npc.broadcastPacket(new SpecialCamera(npc, 1, -150, 10, 3000, 1000, 20000, 0, 0, 0, 0, 0));
- startQuestTimer("4", 2500, npc, player);
- break;
- }
- case "4":
- {
- npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(96055, -110759, -3312, 0));
- startQuestTimer("5", 2000, npc, player);
- break;
- }
- case "5":
- {
- player.teleToLocation(PLAYER_TELEPORT);
- npc.teleToLocation(NPC_LOCATION);
- if (!_IsGolemSpawned)
- {
- L2Npc golem = addSpawn(CHAOS_GOLEM, 94640, -112496, -3336, 0, false, 0);
- _IsGolemSpawned = true;
- startQuestTimer("6", 1000, golem, player);
- player.sendPacket(new PlaySound(1, "Rm03_A", 0, 0, 0, 0, 0));
- }
- break;
- }
- case "6":
- {
- npc.broadcastPacket(new SpecialCamera(npc, 30, -200, 20, 6000, 700, 8000, 0, 0, 0, 0, 0));
- break;
}
}
+ else if (event.equalsIgnoreCase("1"))
+ {
+ npc.broadcastPacket(new SocialAction(npc.getObjectId(), 2));
+ npc.broadcastPacket(new SpecialCamera(npc, 1, -200, 15, 5500, 1000, 13500, 0, 0, 0, 0, 0));
+ }
+ else if (event.equalsIgnoreCase("2"))
+ {
+ npc.broadcastPacket(new SocialAction(npc.getObjectId(), 3));
+ }
+ else if (event.equalsIgnoreCase("3"))
+ {
+ npc.broadcastPacket(new SocialAction(npc.getObjectId(), 1));
+ }
+ else if (event.equalsIgnoreCase("4"))
+ {
+ npc.broadcastPacket(new SpecialCamera(npc, 1, -150, 10, 3500, 1000, 5000, 0, 0, 0, 0, 0));
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(95928, -110671, -3340, 0));
+ }
+ else if (event.equalsIgnoreCase("5"))
+ {
+ // Delete Dr. Chaos && spawn the war golem.
+ npc.deleteMe();
+ L2GrandBossInstance golem = (L2GrandBossInstance) addSpawn(CHAOS_GOLEM, 96080, -110822, -3343, 0, false, 0, false);
+ GrandBossManager.getInstance().addBoss(golem);
+
+ // The "npc" variable attribution is now for the golem.
+ npc = golem;
+ npc.broadcastPacket(new SpecialCamera(npc, 30, 200, 20, 6000, 700, 8000, 0, 0, 0, 0, 0));
+ npc.broadcastPacket(new SocialAction(npc.getObjectId(), 1));
+ npc.broadcastPacket(new PlaySound(1, "Rm03_A", 0, 0, 0, 0, 0));
+
+ // start monitoring Dr. Chaos's inactivity
+ _lastAttackVsGolem = System.currentTimeMillis();
+ startQuestTimer("golem_despawn", 60000, npc, null, true);
+ }
+ // Check every sec if someone is in range, if found, launch one task to decrease the timer.
+ else if (event.equalsIgnoreCase("paranoia_activity"))
+ {
+ if (GrandBossManager.getInstance().getBossStatus(CHAOS_GOLEM) == NORMAL)
+ {
+ for (L2PcInstance obj : npc.getKnownList().getKnownPlayersInRadius(500))
+ {
+ if (obj.isDead())
+ {
+ continue;
+ }
+
+ _pissedOffTimer -= 1;
+
+ // Make him speak.
+ if (_pissedOffTimer == 15)
+ {
+ broadcastNpcSay(npc, Say2.NPC_ALL, "How dare you trespass into my territory! Have you no fear?");
+ }
+
+ // That was "too much" for that time.
+ if (_pissedOffTimer <= 0)
+ {
+ crazyMidgetBecomesAngry(npc);
+ }
+ }
+ }
+ }
+
return super.onAdvEvent(event, npc, player);
}
@Override
public String onFirstTalk(L2Npc npc, L2PcInstance player)
{
- if (npc.getId() == DR_CHAOS)
+ String htmltext = "";
+
+ if (GrandBossManager.getInstance().getBossStatus(CHAOS_GOLEM) == NORMAL)
{
- npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(96323, -110914, -3328, 0));
- this.startQuestTimer("1", 3000, npc, player);
+ _pissedOffTimer -= 1 + Rnd.get(5); // remove 1-5 secs.
+
+ if ((_pissedOffTimer > 20) && (_pissedOffTimer <= 30))
+ {
+ htmltext = "Doctor Chaos:
What?! Who are you? How did you come here?
You really look suspicious... Aren't those filthy members of Black Anvil guild send you? No? Mhhhhh... I don't trust you!";
+ }
+ else if ((_pissedOffTimer > 10) && (_pissedOffTimer <= 20))
+ {
+ htmltext = "Doctor Chaos:
Why are you standing here? Don't you see it's a private propertie? Don't look at him with those eyes... Did you smile?! Don't make fun of me! He will ... destroy ... you ... if you continue!";
+ }
+ else if ((_pissedOffTimer > 0) && (_pissedOffTimer <= 10))
+ {
+ htmltext = "Doctor Chaos:
I know why you are here, traitor! He discovered your plans! You are assassin ... sent by the Black Anvil guild! But you won't kill the Emperor of Evil!";
+ }
+ else if (_pissedOffTimer <= 0)
+ {
+ crazyMidgetBecomesAngry(npc);
+ }
+ }
+
+ return htmltext;
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ // 30 seconds timer at initialization.
+ _pissedOffTimer = 30;
+
+ // Initialization of the paranoia.
+ startQuestTimer("paranoia_activity", 1000, npc, null, true);
+
+ return null;
+ }
+
+ @Override
+ public String onKill(L2Npc npc, L2PcInstance player, boolean isPet)
+ {
+ cancelQuestTimer("golem_despawn", npc, null);
+ broadcastNpcSay(npc, Say2.NPC_ALL, "Urggh! You will pay dearly for this insult.");
+
+ // "lock" Dr. Chaos for regular RB time (36H fixed +- 24H random)
+ long respawnTime = (36 + Rnd.get(-24, 24)) * 3600000;
+
+ GrandBossManager.getInstance().setBossStatus(CHAOS_GOLEM, DEAD);
+ startQuestTimer("reset_drchaos", respawnTime, null, null, false);
+
+ // also save the respawn time so that the info is maintained past reboots
+ StatsSet info = GrandBossManager.getInstance().getStatsSet(CHAOS_GOLEM);
+ info.set("respawn_time", System.currentTimeMillis() + respawnTime);
+ GrandBossManager.getInstance().setStatsSet(CHAOS_GOLEM, info);
+
+ return null;
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance victim, int damage, boolean isPet)
+ {
+ int chance = Rnd.get(300);
+
+ // Choose a message from 3 choices (1/100)
+ if (chance < 3)
+ {
+ String message = "";
+ switch (chance)
+ {
+ case 0:
+ message = "Bwah-ha-ha! Your doom is at hand! Behold the Ultra Secret Super Weapon!";
+ break;
+ case 1:
+ message = "Foolish, insignificant creatures! How dare you challenge me!";
+ break;
+ default:
+ message = "I see that none will challenge me now!";
+ break;
+ }
+
+ // Make him speak.
+ broadcastNpcSay(npc, Say2.NPC_ALL, message);
+ }
+ return null;
+ }
+
+ /**
+ * Launches the complete animation.
+ * @param npc the midget.
+ */
+ private void crazyMidgetBecomesAngry(L2Npc npc)
+ {
+ if (GrandBossManager.getInstance().getBossStatus(CHAOS_GOLEM) == NORMAL)
+ {
+ // Set the status to "crazy".
+ GrandBossManager.getInstance().setBossStatus(CHAOS_GOLEM, CRAZY);
+
+ // Cancels the paranoia timer.
+ cancelQuestTimer("paranoia_activity", npc, null);
+
+ // Makes the NPC moves near the Strange Box speaking.
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(96323, -110914, -3328, 0));
+ broadcastNpcSay(npc, Say2.NPC_ALL, "Fools! Why haven't you fled yet? Prepare to learn a lesson!");
+
+ // Delayed animation timers.
+ startQuestTimer("1", 2000, npc, null, false); // 2 secs, time to launch dr.C anim 2. Cam 1 on.
+ startQuestTimer("2", 4000, npc, null, false); // 2,5 secs, time to launch dr.C anim 3.
+ startQuestTimer("3", 6500, npc, null, false); // 6 secs, time to launch dr.C anim 1.
+ startQuestTimer("4", 12500, npc, null, false); // 4,5 secs to make the NPC moves to the grotto. Cam 2 on.
+ startQuestTimer("5", 17000, npc, null, false); // 4 secs for golem spawn, and golem anim. Cam 3 on.
}
- return "";
}
public static void main(String[] args)
{
new DrChaos();
}
-}
+}
\ No newline at end of file
diff --git a/trunk/dist/game/data/stats/npcs/25500-25600.xml b/trunk/dist/game/data/stats/npcs/25500-25600.xml
index 096337ef27..bcb5106638 100644
--- a/trunk/dist/game/data/stats/npcs/25500-25600.xml
+++ b/trunk/dist/game/data/stats/npcs/25500-25600.xml
@@ -635,7 +635,7 @@
-
+
diff --git a/trunk/dist/tools/sql/server/grandboss_data.sql b/trunk/dist/tools/sql/server/grandboss_data.sql
index 05bd7585af..29f91087dd 100644
--- a/trunk/dist/tools/sql/server/grandboss_data.sql
+++ b/trunk/dist/tools/sql/server/grandboss_data.sql
@@ -36,4 +36,10 @@ INSERT IGNORE INTO `grandboss_data` (`boss_id`,`loc_x`,`loc_y`,`loc_z`,`heading`
-- (29178, 0, 0, 0, 0, 0, 0), -- Freya (Spelling) (85)
-- (29178, 0, 0, 0, 0, 0, 0), -- Freya (Stand) (85)
-- (29178, 0, 0, 0, 0, 0, 0), -- Freya (Stand Hard) (85)
--- (29179, 0, 0, 0, 0, 0, 0), -- Zaken Day (83)
\ No newline at end of file
+-- (29179, 0, 0, 0, 0, 0, 0), -- Zaken Day (83)
+
+--
+-- Dr. Chaos
+--
+DELETE FROM spawnlist WHERE (npc_templateid = 32033);
+INSERT IGNORE INTO grandboss_data VALUES (25512, 96320, -110912, -3328, 8191, 0, 0, 0, 0);