diff --git a/data/conf/zones/deniran.xml b/data/conf/zones/deniran.xml
index ab5423f454..1b3d20fa14 100644
--- a/data/conf/zones/deniran.xml
+++ b/data/conf/zones/deniran.xml
@@ -1033,6 +1033,7 @@ Entrance fee is 10 money
+
diff --git a/data/script/region/atlantis/city/exterior/ZelanNPC.lua b/data/script/region/atlantis/city/exterior/ZelanNPC.lua
deleted file mode 100644
index e2525440a0..0000000000
--- a/data/script/region/atlantis/city/exterior/ZelanNPC.lua
+++ /dev/null
@@ -1,63 +0,0 @@
---[[
- ***************************************************************************
- * Copyright © 2020 - Arianne *
- ***************************************************************************
- ***************************************************************************
- * *
- * 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 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************
-]]
-
-
-local zoneName = "-7_deniran_atlantis"
-
-if game:setZone(zoneName) then
- local zelan = entities:createSpeakerNPC("Zelan")
-
- -- NPC appearance & behavior
- zelan:setEntityClass("atlantismale01npc")
- zelan:setCollisionAction(CollisionAction.STOP)
-
- -- NPC location & path
- local nodes = {
- {63, 66},
- {75, 66},
- }
- zelan:setPathAndPosition(nodes, true)
-
- -- NPC dialog
- zelan:addGreeting()
- zelan:addGoodbye()
-
- -- add Zelan to the world
- game:add(zelan)
-
-
- -- quest
- local quest = quests.simple:create("unicorn_horns_for_zelan", "Unicorn Horns for Zelan", "Zelan")
- quest:setDescription("Zelan needs help gathering unicorn horns.")
-
- quest:setReply(quests.simple.ID_REQUEST,
- "Hello! I'm in need of some unicorn horns to make some daggers."
- .. " It is really dangerous in the woods surrounding Atlantis. If you are a brave sort"
- .. " I could really use some help gathering unicorn horns. Will you help me?")
- quest:setReply(quests.simple.ID_ACCEPT,
- "Great! Be careful out there lots of large monsters, and those centaurs are really nasty")
- quest:setReply(quests.simple.ID_REWARD, "Thanks a bunch!")
- quest:setReply(quests.simple.ID_REJECT, "Thats ok, I will find someone else to help me.")
-
- quest:setItemToCollect("unicorn horn", 10)
- quest:setXPReward(50000)
- quest:setKarmaReward(5.0)
- quest:addItemReward("soup", 3)
- quest:addItemReward("money", 20000)
- quest:setRegion(Region.ATLANTIS)
-
- quests:register(quest)
-else
- logger:error("Could not set zone: " .. zoneName)
-end
diff --git a/src/games/stendhal/server/core/rp/StendhalQuestSystem.java b/src/games/stendhal/server/core/rp/StendhalQuestSystem.java
index ab6fdb8be5..cf1adfd696 100644
--- a/src/games/stendhal/server/core/rp/StendhalQuestSystem.java
+++ b/src/games/stendhal/server/core/rp/StendhalQuestSystem.java
@@ -186,6 +186,7 @@ public class StendhalQuestSystem {
loadQuest(new TrapsForKlaas());
//loadQuest(new TutorialIsland());
loadQuest(new UltimateCollector());
+ loadQuest(new UnicornHornsForZelan());
loadQuest(new VampireSword());
loadQuest(new WaterForXhiphin());
loadQuest(new WeaponsCollector());
diff --git a/src/games/stendhal/server/entity/player/UpdateConverter.java b/src/games/stendhal/server/entity/player/UpdateConverter.java
index 739e211162..df3620475e 100644
--- a/src/games/stendhal/server/entity/player/UpdateConverter.java
+++ b/src/games/stendhal/server/entity/player/UpdateConverter.java
@@ -552,6 +552,15 @@ public abstract class UpdateConverter {
}
}
+ // 1.43: Unicorn Horns for Zelan converted to QuestManuscript
+ final String zelanQName = "unicorn_horns_for_zelan";
+ if (player.hasQuest(zelanQName)) {
+ final String[] zelanSlots = player.getQuest(zelanQName).split(";");
+ if (zelanSlots.length == 2) {
+ player.setQuest(zelanQName, zelanSlots[0] + ";;" + zelanSlots[1]);
+ }
+ }
+
// fix quest slots for kills quests.
fixKillQuestsSlots(player);
diff --git a/src/games/stendhal/server/maps/atlantis/cityoutside/ZelanNPC.java b/src/games/stendhal/server/maps/atlantis/cityoutside/ZelanNPC.java
new file mode 100644
index 0000000000..79a012d466
--- /dev/null
+++ b/src/games/stendhal/server/maps/atlantis/cityoutside/ZelanNPC.java
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * (C) Copyright 2003-2022 - Stendhal *
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ ***************************************************************************/
+package games.stendhal.server.maps.atlantis.cityoutside;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import games.stendhal.server.core.config.ZoneConfigurator;
+import games.stendhal.server.core.engine.StendhalRPZone;
+import games.stendhal.server.core.pathfinder.FixedPath;
+import games.stendhal.server.core.pathfinder.Node;
+import games.stendhal.server.entity.CollisionAction;
+import games.stendhal.server.entity.npc.SpeakerNPC;
+
+
+public class ZelanNPC implements ZoneConfigurator {
+
+ @Override
+ public void configureZone(final StendhalRPZone zone, final Map attributes) {
+ zone.add(buildNPC());
+ }
+
+ private SpeakerNPC buildNPC() {
+ final SpeakerNPC zelan = new SpeakerNPC("Zelan");
+
+ // appearance & behavior
+ zelan.setEntityClass("atlantismale01npc");
+ zelan.setCollisionAction(CollisionAction.STOP);
+
+ // location & path
+ zelan.setPathAndPosition(new FixedPath(Arrays.asList(
+ new Node(63, 66),
+ new Node(75, 66)), true));
+
+ // dialog
+ zelan.addGreeting();
+ zelan.addGoodbye();
+
+ return zelan;
+ }
+}
diff --git a/src/games/stendhal/server/maps/quests/UnicornHornsForZelan.java b/src/games/stendhal/server/maps/quests/UnicornHornsForZelan.java
new file mode 100644
index 0000000000..f141a74873
--- /dev/null
+++ b/src/games/stendhal/server/maps/quests/UnicornHornsForZelan.java
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright © 2003-2023 - Arianne *
+ ***************************************************************************
+ ***************************************************************************
+ * *
+ * 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 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+package games.stendhal.server.maps.quests;
+
+import games.stendhal.server.entity.npc.action.EquipItemAction;
+import games.stendhal.server.entity.npc.action.IncreaseKarmaAction;
+import games.stendhal.server.entity.npc.action.IncreaseXPAction;
+import games.stendhal.server.entity.npc.quest.BringItemTask;
+import games.stendhal.server.entity.npc.quest.QuestBuilder;
+import games.stendhal.server.entity.npc.quest.QuestManuscript;
+import games.stendhal.server.maps.Region;
+
+
+/**
+ * QUEST: Unicorn Horns for Zelan
+ *
+ * PARTICIPANTS:
+ *
+ *
+ * STEPS:
+ *
+ * - Zelan needs 10 unicorn horns.
+ * - Bring the unicorn horns to him for a reward.
+ *
+ *
+ * REWARD:
+ *
+ * - 50000 xp
+ * - 5 karma
+ * - 3 soup
+ * - 20000 money
+ *
+ *
+ * REPETITIONS:
+ *
+ */
+public class UnicornHornsForZelan implements QuestManuscript {
+
+ @Override
+ public QuestBuilder> story() {
+ QuestBuilder quest = new QuestBuilder<>(new BringItemTask());
+
+ final String npcName = "Zelan";
+ final int quantity = 10;
+ final String itemName = "unicorn horn";
+ final String plItemName = itemName + "s";
+ final int rewardSoup = 3;
+ final int rewardMoney = 20000;
+
+ quest.info()
+ .name("Unicorn Horns for Zelan")
+ .internalName("unicorn_horns_for_zelan")
+ .description("Zelan needs help gathering unicorn horns.")
+ .region(Region.ATLANTIS)
+ .questGiverNpc(npcName)
+ .notRepeatable();
+
+ quest.history()
+ .whenNpcWasMet(npcName + " asked me to get " + quantity
+ + " " + plItemName + ".")
+ .whenQuestWasRejected("I do not want to help " + npcName + ".")
+ .whenQuestWasAccepted("I have not found what I am looking for yet.")
+ .whenTaskWasCompleted("I have what " + npcName + " asked for.")
+ .whenQuestWasCompleted("I found " + quantity
+ + " " + plItemName + " for " + npcName + ".");
+
+ quest.offer()
+ .respondToRequest("Hello! I'm in need of some unicorn horns to"
+ + " make some daggers. It is really dangerous in the woods"
+ + " surrounding Atlantis. If you are a brave sort I could"
+ + " really use some help gathering unicorn horns. Will you"
+ + " help me?")
+ .respondToUnrepeatableRequest("Thanks, but I don't need any more"
+ + " help.")
+ .respondToAccept("Great! Be careful out there lots of large"
+ + " monsters, and those centaurs are really nasty.")
+ .respondToReject("Thats ok, I will find someone else to help me.")
+ .rejectionKarmaPenalty(0)
+ //~ .remind("I have already asked you to get " + quantity
+ //~ + " " + plItemName + ". Are you #done?");
+ .remind("I asked you to bring me " + quantity + " " + plItemName
+ + ".");
+
+ quest.task()
+ .requestItem(quantity, itemName);
+
+ quest.complete()
+ .greet("Did you find the " + plItemName + "?")
+ .respondToReject("I asked you to bring me " + quantity + " "
+ + plItemName + ".")
+ .respondToAccept("Thanks a bunch! As a reward I will give you "
+ + rewardSoup + " soups and " + rewardMoney + " money.")
+ .rewardWith(new IncreaseXPAction(50000))
+ .rewardWith(new IncreaseKarmaAction(5.0))
+ .rewardWith(new EquipItemAction("soup", rewardSoup))
+ .rewardWith(new EquipItemAction("money", rewardMoney));
+
+ return quest;
+ }
+}
diff --git a/tests/scripts/quest/UnicornHornsForZelanTest.java b/tests/games/stendhal/server/maps/quests/UnicornHornsForZelanTest.java
similarity index 70%
rename from tests/scripts/quest/UnicornHornsForZelanTest.java
rename to tests/games/stendhal/server/maps/quests/UnicornHornsForZelanTest.java
index c08a5e294d..2492416f2f 100644
--- a/tests/scripts/quest/UnicornHornsForZelanTest.java
+++ b/tests/games/stendhal/server/maps/quests/UnicornHornsForZelanTest.java
@@ -1,5 +1,5 @@
/***************************************************************************
- * (C) Copyright 2003-2022 - Arianne *
+ * Copyright © 2003-2023 - Arianne *
***************************************************************************
***************************************************************************
* *
@@ -9,7 +9,7 @@
* (at your option) any later version. *
* *
***************************************************************************/
-package scripts.quest;
+package games.stendhal.server.maps.quests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -21,28 +21,31 @@ import org.junit.Before;
import org.junit.Test;
import games.stendhal.server.core.engine.SingletonRepository;
+import games.stendhal.server.core.engine.StendhalRPZone;
import games.stendhal.server.entity.npc.ConversationStates;
import games.stendhal.server.entity.npc.SpeakerNPC;
import games.stendhal.server.entity.npc.fsm.Engine;
-import utilities.LuaTestHelper;
-import utilities.PlayerTestHelper;
+import games.stendhal.server.entity.player.Player;
+import games.stendhal.server.maps.atlantis.cityoutside.ZelanNPC;
+import utilities.QuestHelper;
-public class UnicornHornsForZelanTest extends LuaTestHelper {
+public class UnicornHornsForZelanTest extends QuestHelper {
private SpeakerNPC zelan;
+ private Player player;
private final String slot = "unicorn_horns_for_zelan";
@Before
public void setUp() {
- setUpZone("-7_deniran_atlantis");
- load("data/script/region/atlantis/city/exterior/ZelanNPC.lua");
- loadCachedQuests();
-
- addPlayerToWorld();
+ player = createPlayer("tester");
+ // load Zelan
+ new ZelanNPC().configureZone(new StendhalRPZone("testzone"), null);
zelan = SingletonRepository.getNPCList().get("Zelan");
+ // load quest
+ quests.loadQuest(new UnicornHornsForZelan());
}
@Test
@@ -57,6 +60,8 @@ public class UnicornHornsForZelanTest extends LuaTestHelper {
}
private void testQuest() {
+ assertTrue(quests.isLoaded(quests.getQuestFromSlot(slot)));
+
final Engine en = zelan.getEngine();
assertFalse(player.hasQuest(slot));
@@ -72,11 +77,13 @@ public class UnicornHornsForZelanTest extends LuaTestHelper {
assertEquals(ConversationStates.ATTENDING, en.getCurrentState());
en.step(player, "quest");
- assertEquals(ConversationStates.QUEST_OFFERED, en.getCurrentState());
+ assertEquals(ConversationStates.QUEST_OFFERED,
+ en.getCurrentState());
assertEquals(
"Hello! I'm in need of some unicorn horns to make some daggers."
- + " It is really dangerous in the woods surrounding Atlantis. If you are a brave sort"
- + " I could really use some help gathering unicorn horns. Will you help me?",
+ + " It is really dangerous in the woods surrounding Atlantis. If"
+ + " you are a brave sort I could really use some help gathering"
+ + " unicorn horns. Will you help me?",
getReply(zelan));
en.step(player, "no");
assertEquals(ConversationStates.ATTENDING, en.getCurrentState());
@@ -90,36 +97,52 @@ public class UnicornHornsForZelanTest extends LuaTestHelper {
en.step(player, "yes");
assertEquals(ConversationStates.ATTENDING, en.getCurrentState());
assertEquals(
- "Great! Be careful out there lots of large monsters, and those centaurs are really nasty",
+ "Great! Be careful out there lots of large monsters, and those"
+ + " centaurs are really nasty.",
getReply(zelan));
assertEquals("start", player.getQuest(slot, 0));
assertTrue(player.getKarma() == playerKarma);
en.step(player, "bye");
assertEquals(ConversationStates.IDLE, en.getCurrentState());
- PlayerTestHelper.equipWithStackableItem(player, "unicorn horn", 9);
+ equipWithStackableItem(player, "unicorn horn", 9);
en.step(player, "hi");
assertEquals(ConversationStates.ATTENDING, en.getCurrentState());
en.step(player, "done");
- assertEquals("I asked you to bring me 10 unicorn horns.", getReply(zelan));
+ assertEquals(
+ "I asked you to bring me 10 unicorn horns.",
+ getReply(zelan));
en.step(player, "bye");
- PlayerTestHelper.equipWithStackableItem(player, "unicorn horn", 1);
+ equipWithStackableItem(player, "unicorn horn", 1);
en.step(player, "hi");
- en.step(player, "done");
+ assertEquals(ConversationStates.QUEST_ITEM_BROUGHT,
+ en.getCurrentState());
+ assertEquals("Did you find the unicorn horns?", getReply(zelan));
+ en.step(player, "yes");
assertEquals(
- "Thanks a bunch! As a reward I will give you 3 soups and 20000 money.",
+ "Thanks a bunch! As a reward I will give you 3 soups and 20000"
+ + " money.",
getReply(zelan));
en.step(player, "bye");
assertEquals(ConversationStates.IDLE, en.getCurrentState());
assertEquals("done", player.getQuest(slot, 0));
+ assertEquals("1", player.getQuest(slot, 2));
assertTrue(player.getKarma() == playerKarma + 5);
assertEquals(playerXP + 50000, player.getXP());
assertEquals(0, player.getNumberOfEquipped("unicorn horn"));
assertEquals(3, player.getNumberOfEquipped("soup"));
assertEquals(20000, player.getNumberOfEquipped("money"));
+
+ en.step(player, "hi");
+ en.step(player, "quest");
+ assertEquals(ConversationStates.ATTENDING, en.getCurrentState());
+ assertEquals(
+ "Thanks, but I don't need any more help.",
+ getReply(zelan));
+ en.step(player, "bye");
}
}